Search code examples
springspring-bootelasticsearchhibernate-search

Impossible to configure my own analyzer in spring boot Hibernate Search Elasticsearch


Please help. It's many days i try to configure an elasticsearch indexation in my Spring Boot application, certainly i missed something in the documentation but i dont find what.

I am relatively new with spring, days from days i found it very powerful, and it is my first very long problem.

Description of the problem I have a simple object Book indexed with a @FullTextField on my own analyzer

@Entity
@Indexed
@Data
public class Book {
    @Id
    @GeneratedValue
    private Long id;

    @FullTextField(analyzer = "my_analyze_french")
    private String name;

With my config

package com.mycompany.jpa.elasticsearch;
import org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurationContext;
import org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurer;

// https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#getting-started-analysis
public class CVAnalysisConfigurer implements ElasticsearchAnalysisConfigurer {
    @Override
    public void configure(ElasticsearchAnalysisConfigurationContext context) {
        System.out.println("DEFINE a NEW ANALYZER");
        // define each analyzer + associated token filter parameters
        context.analyzer("my_analyze_french").custom()
                .tokenizer("standard")
                .tokenFilters("lowercase", "snowball_french", "asciifolding"); // warning order

        // stemmer for french : regroup words by family
        context.tokenFilter("snowball_french")
                .type("snowball")
                .param("language", "French");

        System.out.println("DEFINE a NEW ANALYZER:DONE");
    }
}

In my application.properties i indicate the name of the bean to configure my analyzer

# cf https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#backend-elasticsearch-analysis
hibernate.search.backend.analysis.configurer=class:com.mycompany.jpa.elasticsearch.CVAnalysisConfigurer

When i start my application i have the error

2021-06-21 11:37:05.685 ERROR 17135 --- [port thread - 1] o.h.s.e.r.spi.RootFailureCollector       : HSEARCH000521: Hibernate Search encountered a failure during bootstrap; continuing for now to list all problems, but the process will ultimately be aborted.
Context: Hibernate ORM mapping, type 'com.mycompany.jpa.elasticsearch.Book'
Failure:

org.hibernate.search.util.common.SearchException: HSEARCH400007: Elasticsearch request failed: HSEARCH400090: Elasticsearch response indicates a failure.
Request: PUT /book-000001 with parameters {}
Response: 400 'Bad Request' from 'http://localhost:9200' with body 
{
  "error": {
    "root_cause": [
      {
        "type": "mapper_parsing_exception",
        "reason": "Failed to parse mapping [_doc]: analyzer [my_analyze_french] has not been configured in mappings"
      }
    ],
    "type": "mapper_parsing_exception",
    "reason": "Failed to parse mapping [_doc]: analyzer [my_analyze_french] has not been configured in mappings",
    "caused_by": {
      "type": "illegal_argument_exception",
      "reason": "analyzer [my_analyze_french] has not been configured in mappings"
    }
  },
  "status": 400
}

    at org.hibernate.search.backend.elasticsearch.work.impl.AbstractNonBulkableWork.handleResult(AbstractNonBulkableWork.java:84) ~[hibernate-search-backend-elasticsearch-6.0.4.Final.jar:6.0.4.Final]
    at org.hibernate.search.backend.elasticsearch.work.impl.AbstractNonBulkableWork.lambda$execute$3(AbstractNonBulkableWork.java:66) ~[hibernate-search-backend-elasticsearch-6.0.4.Final.jar:6.0.4.Final]
    at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:642) ~[na:na]
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506) ~[na:na]
    at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2073) ~[na:na]
    at org.hibernate.search.backend.elasticsearch.client.impl.ElasticsearchClientImpl$1.onFailure(ElasticsearchClientImpl.java:127) ~[hibernate-search-backend-elasticsearch-6.0.4.Final.jar:6.0.4.Final]
    at org.elasticsearch.client.RestClient$FailureTrackingResponseListener.onDefinitiveFailure(RestClient.java:644) ~[elasticsearch-rest-client-7.13.2.jar:7.13.2]
    at org.elasticsearch.client.RestClient$1.completed(RestClient.java:386) ~[elasticsearch-rest-client-7.13.2.jar:7.13.2]
    at org.elasticsearch.client.RestClient$1.completed(RestClient.java:370) ~[elasticsearch-rest-client-7.13.2.jar:7.13.2]
    at org.apache.http.concurrent.BasicFuture.completed(BasicFuture.java:122) ~[httpcore-4.4.14.jar:4.4.14]
    at org.apache.http.impl.nio.client.DefaultClientExchangeHandlerImpl.responseCompleted(DefaultClientExchangeHandlerImpl.java:181) ~[httpasyncclient-4.1.4.jar:4.1.4]
    at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.processResponse(HttpAsyncRequestExecutor.java:448) ~[httpcore-nio-4.4.14.jar:4.4.14]
    at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.inputReady(HttpAsyncRequestExecutor.java:338) ~[httpcore-nio-4.4.14.jar:4.4.14]
    at org.apache.http.impl.nio.DefaultNHttpClientConnection.consumeInput(DefaultNHttpClientConnection.java:265) ~[httpcore-nio-4.4.14.jar:4.4.14]
    at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:81) ~[httpasyncclient-4.1.4.jar:4.1.4]
    at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:39) ~[httpasyncclient-4.1.4.jar:4.1.4]
    at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:114) ~[httpcore-nio-4.4.14.jar:4.4.14]
    at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162) ~[httpcore-nio-4.4.14.jar:4.4.14]
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337) ~[httpcore-nio-4.4.14.jar:4.4.14]
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315) ~[httpcore-nio-4.4.14.jar:4.4.14]
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276) ~[httpcore-nio-4.4.14.jar:4.4.14]
    at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104) ~[httpcore-nio-4.4.14.jar:4.4.14]
    at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591) ~[httpcore-nio-4.4.14.jar:4.4.14]
    at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]
Caused by: org.hibernate.search.util.common.SearchException: HSEARCH400090: Elasticsearch response indicates a failure.
    at org.hibernate.search.backend.elasticsearch.work.impl.DefaultElasticsearchRequestSuccessAssessor.checkSuccess(DefaultElasticsearchRequestSuccessAssessor.java:103) ~[hibernate-search-backend-elasticsearch-6.0.4.Final.jar:6.0.4.Final]
    at org.hibernate.search.backend.elasticsearch.work.impl.DefaultElasticsearchRequestSuccessAssessor.checkSuccess(DefaultElasticsearchRequestSuccessAssessor.java:86) ~[hibernate-search-backend-elasticsearch-6.0.4.Final.jar:6.0.4.Final]
    at org.hibernate.search.backend.elasticsearch.work.impl.AbstractNonBulkableWork.handleResult(AbstractNonBulkableWork.java:79) ~[hibernate-search-backend-elasticsearch-6.0.4.Final.jar:6.0.4.Final]
    ... 23 common frames omitted

2021-06-21 11:37:05.741 ERROR 17135 --- [           main] j.LocalContainerEntityManagerFactoryBean : Failed to initialize JPA EntityManagerFactory: [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 'com.mycompany.jpa.elasticsearch.Book': 
            failures: 
              - HSEARCH400007: Elasticsearch request failed: HSEARCH400090: Elasticsearch response indicates a failure.
Request: PUT /book-000001 with parameters {}
Response: 400 'Bad Request' from 'http://localhost:9200' with body 
{
  "error": {
    "root_cause": [
      {
        "type": "mapper_parsing_exception",
        "reason": "Failed to parse mapping [_doc]: analyzer [my_analyze_french] has not been configured in mappings"
      }
    ],
    "type": "mapper_parsing_exception",
    "reason": "Failed to parse mapping [_doc]: analyzer [my_analyze_french] has not been configured in mappings",
    "caused_by": {
      "type": "illegal_argument_exception",
      "reason": "analyzer [my_analyze_french] has not been configured in mappings"
    }
  },
  "status": 400
}

2021-06-21 11:37:05.742  WARN 17135 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.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 'com.mycompany.jpa.elasticsearch.Book': 
            failures: 
              - HSEARCH400007: Elasticsearch request failed: HSEARCH400090: Elasticsearch response indicates a failure.
Request: PUT /book-000001 with parameters {}
Response: 400 'Bad Request' from 'http://localhost:9200' with body 
{
  "error": {
    "root_cause": [
      {
        "type": "mapper_parsing_exception",
        "reason": "Failed to parse mapping [_doc]: analyzer [my_analyze_french] has not been configured in mappings"
      }
    ],
    "type": "mapper_parsing_exception",
    "reason": "Failed to parse mapping [_doc]: analyzer [my_analyze_french] has not been configured in mappings",
    "caused_by": {
      "type": "illegal_argument_exception",
      "reason": "analyzer [my_analyze_french] has not been configured in mappings"
    }
  },
  "status": 400
}

2021-06-21 11:37:05.743  INFO 17135 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2021-06-21 11:37:05.758  INFO 17135 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
2021-06-21 11:37:05.761  INFO 17135 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]

My config I use spring Boot 2.3.11, my database if h2

Main dependencies are

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- for use of elastic search, automatic indexation with jpa -->
    <dependency>
        <groupId>org.hibernate.search</groupId>
        <artifactId>hibernate-search-mapper-orm</artifactId>
        <version>6.0.4.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate.search</groupId>
        <artifactId>hibernate-search-backend-elasticsearch</artifactId>
        <version>6.0.4.Final</version>
    </dependency>

I specify the version of elastic search

<properties>
    <elasticsearch.version>7.13.2</elasticsearch.version>
</properties>

Version of my server is the same, i use a simple docker to test, and i delete nodes from the volume before each launch

version: '3'
services:
  elasticsearchstudy:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.13.2
    container_name: elasticsearchstudy
    environment:
      - "discovery.type=single-node"
    volumes:
      - ./elasticsearch:/usr/share/elasticsearch/data:rw
    ports:
      - 9200:9200
    restart: unless-stopped

What i tried ? To avoid some possible problems with Spring Boot i tried to deferred the bootstrapping, but with or without it it change nothing

@SpringBootApplication
@EnableJpaRepositories(bootstrapMode = BootstrapMode.DEFERRED)
public class HibernateSearchStudy {

I tried to configure my analysis with the REST API on Postman, it is ok to analyse a document when i work on a new index created with the REST API localhost:9200/book/_analyze but impossible to do it on my application index Book-000001 (i can't modify the analyzer of a previous created index)

Firstly i tried to do all that in a @DataJpaTest but as it was not working (same errors), i try to test it now in a CommandLineRunner that insert data in my repo, and after that i do a search

I think my ElasticsearchAnalysisConfigurer initialisation is never called (the trace i put in configure never appear). How doing it ?

Any help would be greatly appreciated !!


Solution

  • application.properties is a Spring Boot configuration file, not a Hibernate Search configuration file. You cannot just dump Hibernate Search properties in there.

    Instead, prefix your Hibernate Search properties with spring.jpa.properties., so that Spring Boot passes along the properties to Hibernate ORM, which will pass them along to Hibernate Search. For example:

    # cf https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#backend-elasticsearch-analysis
    spring.jpa.properties.hibernate.search.backend.analysis.configurer=class:com.mycompany.jpa.elasticsearch.CVAnalysisConfigurer
    

    EDIT: See also the section dedicated to Spring Boot in the Hibernate Search reference documentation.