FulltextField + Analyzer & Aggregation

The thing to keep in mind with String fields is that there are two ways to map them:

  • As text (@FullTextField). Then they can be tokenized (using an analyzer), which is best for search. They cannot be used for sorts or aggregations, however (at least not efficiently).
  • As keyword (@KeywordField). Then they cannot be tokenized, but can be normalized (using a normalizer), which is not great for search (you basically only get case-insensitive exact search). However they can be used for sorts and aggregations.

If you need the features from both… just declare two fields: one for search, the other for aggregation.

@FullTextField(name = "field_suggest", analyzer = "edge_ngram")
@KeywordField(name = "field_keyword", searchable = Searchable.NO, aggregable = Aggregable.YES)

Then do this:

AggregationKey<Map<String, Long>> suggestAgg = AggregationKey.of("suggestions");
SearchResult<Acme> result = Search.session( entityManager ).search(Acme.class)
    .where( f -> f.match().field( "field_suggest" ).matching( "xxx" ) )
    .aggregation( suggestAgg, f -> f.terms().field( "field_keyword" ).maxTermCount( 20 ) )
    .fetch( 20 ); // Use 0 if you don't need the top hits

List<Acme> topHits = result.getHits();
Map<String, Long> suggestionsAndCount = result.getAggregation( suggestAgg );