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 );