Autocomplete with Hibernate Search 6

Okay then… this may work for small indices, but it will perform rather badly on large ones.

In general, I’d recommend a different approach involving a query. The main difference is that it doesn’t suggest terms, but entities. So if you type “jo” you won’t get “John” as a suggestion, but the entity representing the person “John Smith”. Then you can decide to display that entity as you see fit.

If you’re interested by this approach, read on.

If you really want to use Terms to collect indexed terms with Hibernate Search 6, you will have to wait until HSEARCH-4065 gets fixed (or come and discuss on Zulip how to contribute a patch to fix it :wink: ).

Alternatively, you can also switch to Elasticsearch, where you can use suggesters, which I covered briefly here.

Now, to implement autocomplete with queries:

  1. Declare an appropriate analyzer in your analysis configurer:
public class MyAnalysisConfigurer implements LuceneAnalysisConfigurer {
	@Override
	public void configure(LuceneAnalysisConfigurationContext context) {
		context.analyzer( "autocomplete-indexing" ).custom()
				.tokenizer( StandardTokenizerFactory.class )
				.tokenFilter( LowerCaseFilterFactory.class )
				.tokenFilter( SnowballPorterFilterFactory.class )
						.param( "language", "English" )
				.tokenFilter( ASCIIFoldingFilterFactory.class )
				.tokenFilter( EdgeNGramFilterFactory.class )
						.param( "minGramSize", "3" )
						.param( "maxGramSize", "7" );

		// Same as "autocomplete-indexing", but without the edge-ngram filter
		context.analyzer( "autocomplete-query" ).custom()
				.tokenizer( StandardTokenizerFactory.class )
				.tokenFilter( LowerCaseFilterFactory.class )
				.tokenFilter( SnowballPorterFilterFactory.class )
						.param( "language", "English" )
				.tokenFilter( ASCIIFoldingFilterFactory.class );
	}
}
  1. Declare full-text fields with the appropriate analyzers:
@Indexed
public class MyEntity {

    @FullTextField(name = "firstEntityField_autocomplete,
            analyzer = "autocomplete-indexing", searchAnalyzer = "autocomplete-query")
    private String firstEntityField;

    @FullTextField(name = "secondEntityField_autocomplete,
            analyzer = "autocomplete-indexing", searchAnalyzer = "autocomplete-query")
    private String secondEntityField;

    @FullTextField(name = "thirdEntityField_autocomplete,
            analyzer = "autocomplete-indexing", searchAnalyzer = "autocomplete-query")
    private String thirdEntityField;

    // ... getters, setters ...
}
  1. Reindex your data
  2. Query like this to retrieve only entities that match the given terms:
String terms = ...; // User input
List<MyEntity> hits = Search.session( entityManager )
		.search( MyEntity.class )
		.where( f -> f.simpleQueryString()
				.fields( "firstEntityField_autocomplete",
						"secondEntityField_autocomplete",
						"thirdEntityField_autocomplete" )
				.matching( terms )
				.defaultOperator( BooleanOperator.AND ) )
		.fetchHits( 20 );
2 Likes