spring-boothibernatelucenehibernate-search

how to use hiberanate search 7.1.0 analyzer settin in spring boot 3


I am currently implementing the search function using Hibernate search 7.1.0.

I'm currently setting up analyzer, but I'm constantly getting an error that execution doesn't have that analyzer.

I set up applicatoin.properties and added dependency to gradle normally, but I don't know why the problem is happening. Below is my code.

package study.hibernateSearch;

import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory;
import org.hibernate.search.backend.lucene.analysis.LuceneAnalysisConfigurationContext;
import org.hibernate.search.backend.lucene.analysis.LuceneAnalysisConfigurer;

public class CustomAnalysisConfigurer implements LuceneAnalysisConfigurer {
    @Override
    public void configure(LuceneAnalysisConfigurationContext context) {
        context.analyzer( "english" ).custom()
                .tokenizer( "standard" )
                .charFilter( "htmlStrip" )
                .tokenFilter( "lowercase" )
                .tokenFilter( "snowballPorter" )
                .param( "language", "English" )
                .tokenFilter( "asciiFolding" );


        context.analyzer("korean").custom()
                .tokenizer(WhitespaceTokenizerFactory.class)
                .tokenFilter("lowercase");
        // 추가적인 토큰 필터 적용 가능
    }
}

Below is my Entity Class

package study.hibernateSearch;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.ToString;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;

@Entity
@Indexed // Hibernate Search에 의해 인덱싱됨을 나타냅니다.
@ToString
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @FullTextField(searchAnalyzer = "english")
    private String title;


    @FullTextField
    private String author;

    // 기타 필드 및 getter, setter
}

I wrote the application.properties as follows.

hibernate.search.default.directory_provider = filesystem
hibernate.search.default.indexBase=/usr/lucene/indexes
hibernate.search.Rules.directory_provider = ram
hibernate.search.Actions.directory_provider = com.acme.hibernate.CustomDirectoryProvider

and this is my gradle setting

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.3'
    id 'io.spring.dependency-management' version '1.1.4'
}

group = 'study'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '17'
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.hibernate.search:hibernate-search-mapper-orm:7.1.0.Final'
    implementation 'org.hibernate.search:hibernate-search-backend-lucene:7.1.0.Final'
    implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'org.postgresql:postgresql'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
// https://mvnrepository.com/artifact/org.apache.lucene/lucene-analyzers-nori
    implementation group: 'org.apache.lucene', name: 'lucene-analyzers-nori', version: '8.11.3'
}

tasks.named('test') {
    useJUnitPlatform()
}

and Below is the error description

2024-03-03T02:16:22.234+09:00 ERROR 30668 --- [  restartedMain] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.search.util.common.SearchException: HSEARCH000520: Hibernate Search encountered failures during bootstrap. Failures:

    Hibernate ORM mapping: 
        type 'study.hibernateSearch.Book': 
            path '.title': 
                index 'Book': 
                    failures: 
                      - HSEARCH000353: Unknown analyzer: 'english'. Make sure you defined this analyzer.
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1786) ~[spring-beans-6.1.4.jar:6.1.4]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.4.jar:6.1.4]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.4.jar:6.1.4]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) ~[spring-beans-6.1.4.jar:6.1.4]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.4.jar:6.1.4]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) ~[spring-beans-6.1.4.jar:6.1.4]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-6.1.4.jar:6.1.4]
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1231) ~[spring-context-6.1.4.jar:6.1.4]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:949) ~[spring-context-6.1.4.jar:6.1.4]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.4.jar:6.1.4]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.2.3.jar:3.2.3]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.2.3.jar:3.2.3]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.2.3.jar:3.2.3]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[spring-boot-3.2.3.jar:3.2.3]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[spring-boot-3.2.3.jar:3.2.3]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-3.2.3.jar:3.2.3]
    at study.hibernateSearch.HibernateSearchApplication.main(HibernateSearchApplication.java:10) ~[main/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) ~[spring-boot-devtools-3.2.3.jar:3.2.3]
Caused by: jakarta.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.search.util.common.SearchException: HSEARCH000520: Hibernate Search encountered failures during bootstrap. Failures:

    Hibernate ORM mapping: 
        type 'study.hibernateSearch.Book': 
            path '.title': 
                index 'Book': 
                    failures: 
                      - HSEARCH000353: Unknown analyzer: 'english'. Make sure you defined this analyzer.
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:421) ~[spring-orm-6.1.4.jar:6.1.4]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.1.4.jar:6.1.4]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:364) ~[spring-orm-6.1.4.jar:6.1.4]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1833) ~[spring-beans-6.1.4.jar:6.1.4]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782) ~[spring-beans-6.1.4.jar:6.1.4]
    ... 21 common frames omitted
Caused by: org.hibernate.search.util.common.SearchException: HSEARCH000520: Hibernate Search encountered failures during bootstrap. Failures:

    Hibernate ORM mapping: 
        type 'study.hibernateSearch.Book': 
            path '.title': 
                index 'Book': 
                    failures: 
                      - HSEARCH000353: Unknown analyzer: 'english'. Make sure you defined this analyzer.
    at org.hibernate.search.engine.reporting.spi.RootFailureCollector.checkNoFailure(RootFailureCollector.java:67) ~[hibernate-search-engine-7.1.0.Final.jar:7.1.0.Final]
    at org.hibernate.search.engine.common.impl.SearchIntegrationBuilder.prepareBuild(SearchIntegrationBuilder.java:183) ~[hibernate-search-engine-7.1.0.Final.jar:7.1.0.Final]
    at org.hibernate.search.mapper.orm.bootstrap.impl.HibernateSearchPreIntegrationService$NotBooted.doBootFirstPhase(HibernateSearchPreIntegrationService.java:279) ~[hibernate-search-mapper-orm-7.1.0.Final.jar:7.1.0.Final]
    at org.hibernate.search.mapper.orm.bootstrap.impl.HibernateOrmIntegrationBooterImpl.bootNow(HibernateOrmIntegrationBooterImpl.java:179) ~[hibernate-search-mapper-orm-7.1.0.Final.jar:7.1.0.Final]
    at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:646) ~[na:na]
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
    at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147) ~[na:na]
    at org.hibernate.search.mapper.orm.bootstrap.impl.HibernateSearchSessionFactoryObserver.sessionFactoryCreated(HibernateSearchSessionFactoryObserver.java:41) ~[hibernate-search-mapper-orm-7.1.0.Final.jar:7.1.0.Final]
    at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:315) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:450) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1507) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.1.4.jar:6.1.4]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:388) ~[spring-orm-6.1.4.jar:6.1.4]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.1.4.jar:6.1.4]
    ... 25 common frames omitted
    Suppressed: org.hibernate.search.util.common.SearchException: HSEARCH000353: Unknown analyzer: 'english'. Make sure you defined this analyzer.
Context: index 'Book'
        at org.hibernate.search.backend.lucene.types.dsl.impl.LuceneStringIndexFieldTypeOptionsStep.searchAnalyzer(LuceneStringIndexFieldTypeOptionsStep.java:95) ~[hibernate-search-backend-lucene-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.backend.lucene.types.dsl.impl.LuceneStringIndexFieldTypeOptionsStep.searchAnalyzer(LuceneStringIndexFieldTypeOptionsStep.java:54) ~[hibernate-search-backend-lucene-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.mapping.definition.programmatic.impl.PropertyMappingFullTextFieldOptionsStepImpl.lambda$searchAnalyzer$1(PropertyMappingFullTextFieldOptionsStepImpl.java:44) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.mapping.definition.programmatic.impl.PojoCompositeFieldModelContributor.contribute(PojoCompositeFieldModelContributor.java:46) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.bridge.binding.impl.ValueBindingContextImpl.createFieldReference(ValueBindingContextImpl.java:171) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.bridge.binding.impl.ValueBindingContextImpl.bridge(ValueBindingContextImpl.java:100) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.bridge.binding.impl.ValueBindingContextImpl.bridge(ValueBindingContextImpl.java:88) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.bridge.binding.impl.ValueBindingContextImpl.bridge(ValueBindingContextImpl.java:82) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.bridge.mapping.impl.StaticValueBinder.bind(StaticValueBinder.java:32) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.bridge.binding.impl.ValueBindingContextImpl.applyBinder(ValueBindingContextImpl.java:129) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.mapping.building.impl.PojoIndexModelBinder.bindValue(PojoIndexModelBinder.java:208) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.processing.building.impl.PojoIndexingProcessorValueNodeBuilderDelegate.valueBinder(PojoIndexingProcessorValueNodeBuilderDelegate.java:81) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.mapping.definition.programmatic.impl.AbstractPropertyMappingFieldOptionsStep.contributeIndexMapping(AbstractPropertyMappingFieldOptionsStep.java:56) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.mapping.definition.programmatic.impl.PropertyMappingFullTextFieldOptionsStepImpl.contributeIndexMapping(PropertyMappingFullTextFieldOptionsStepImpl.java:72) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.mapping.building.spi.ErrorCollectingPojoPropertyMetadataContributor.contributeIndexMapping(ErrorCollectingPojoPropertyMetadataContributor.java:32) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.mapping.definition.programmatic.impl.InitialPropertyMappingStep.contributeIndexMapping(InitialPropertyMappingStep.java:63) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.mapping.building.spi.ErrorCollectingPojoTypeMetadataContributor.contributeIndexMapping(ErrorCollectingPojoTypeMetadataContributor.java:33) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.mapping.definition.programmatic.impl.TypeMappingStepImpl.contributeIndexMapping(TypeMappingStepImpl.java:59) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.mapping.building.impl.PojoMapper.collectIndexMapping(PojoMapper.java:378) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.mapping.building.impl.PojoMapper.mapIndexedType(PojoMapper.java:288) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.mapper.pojo.mapping.building.impl.PojoMapper.mapTypes(PojoMapper.java:216) ~[hibernate-search-mapper-pojo-base-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.engine.common.impl.SearchIntegrationBuilder$MappingBuildingState.mapIndexedTypes(SearchIntegrationBuilder.java:282) ~[hibernate-search-engine-7.1.0.Final.jar:7.1.0.Final]
        at org.hibernate.search.engine.common.impl.SearchIntegrationBuilder.prepareBuild(SearchIntegrationBuilder.java:180) ~[hibernate-search-engine-7.1.0.Final.jar:7.1.0.Final]
        ... 38 common frames omitted

It worked normally before setting up the analyzer, but a problem occurred in the process of setting up the powder stone so that the search results could be obtained even if the user searched by wordwriting.

I would really appreciate it if you could help me.


Solution

  • You have to let Hibernate Search know about your CustomAnalysisConfigurer. To do that, use the spring.jpa.properties.hibernate.search.backend.analysis.configurer property. You can either specify an FQCN:

    spring.jpa.properties.hibernate.search.backend.analysis.configurer=study.hibernateSearch.CustomAnalysisConfigurer
    

    Or alternatively, make your configurer an injectable bean:

    @Component("customAnalysisConfigurer")
    public class CustomAnalysisConfigurer implements LuceneAnalysisConfigurer {
        @Override
        public void configure(LuceneAnalysisConfigurationContext context) {
            // .... 
        }
    }
    

    and use the name of your bean:

    spring.jpa.properties.hibernate.search.backend.analysis.configurer=customAnalysisConfigurer
    

    See also https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#backend-lucene-analysis-basics