Integrating Apache Lucene for Bean Search — Part 4: Suggest

Saturday, Sep 12, 2026 | 3 minute read

David Pilato
Integrating Apache Lucene for Bean Search — Part 4: Suggest

This post is part of a series:

Part 3 turned a q string into a list of beans. The search box still feels incomplete until typing sin offers Sinclar before the user hits Enter.

You already declared lucene-suggest in Part 1. Skip this post entirely if you only need search and filters — the index in Parts 1–3 does not depend on it.

A second Directory

AnalyzingInfixSuggester is not an IndexSearcher on the track index. It is a dictionary with its own Directory (in-memory is fine for a local library). Use the same analyzer as TextField so prefixes match how titles were tokenized.

// Dedicated Directory — do not share it with the track IndexWriter
Directory suggestionDirectory = new ByteBuffersDirectory();
AnalyzingInfixSuggester suggester =
        new AnalyzingInfixSuggester(suggestionDirectory, TrackAnalyzers.searchAnalyzer());

Keep it next to TrackSearchIndex, behind the same write lock as rebuild / upsert. The writer is thread-safe; the suggester rebuild is not something you want racing a lookup.

Rebuild after writes

Incremental delete on an infix suggester is awkward. For a few thousand beans, rebuild the dictionary from the current map after every mutation — the same budget as the ~400 ms track-index rebuild in Part 2.

// InputIterator: text to suggest + optional payload (field name, id, …)
suggester.build(new TrackSuggestionIterator(tracks));

Feed title, artist, and genre (whatever you show in the dropdown). Deduplicate if the same string appears on several tracks.

Lookup

// prefix → up to 10 suggestions; last two flags: allTermsRequired, highlight
List<Lookup.LookupResult> matches =
        suggester.lookup(prefix, Set.of(), 10, false, false);

Payloads can carry metadata — for example which field matched (title / artist / genre) so the UI can group or iconify rows. The lookup string is a prefix, not the full q grammar from Part 3: keep “typeahead” and “run search” as two calls.

On the web side, debounce the input and hit a small endpoint that returns those rows. Selecting a suggestion either fills the box or navigates straight to filter(corpus, suggestedText).

Close

Close the suggester when you close the index (Part 2), after the IndexWriter. A process restart rebuilds both from the source of truth anyway.

Next

Autocomplete sits beside search, not instead of it. Part 5 will count facet buckets under the same q so a filter panel can show Club (12) instead of a blind checkbox list.

Series

© 2010 - 2026 David Pilato

Search is powered by Pagefind. Just hit CTRL+K or CMD+K to start searching.

Powered by Hugo with Dream and Devrel themes.

Details

I discovered Elasticsearch project in 2011. After contributed to the project and created open source plugins for it, David joined elastic the company in 2013 where he is Developer and Evangelist. He also created and still actively managing the French spoken language User Group. At elastic, he mainly worked on Elasticsearch source code, specifically on open-source plugins. In his free time, he likes talking about elasticsearch in conferences or in companies (Brown Bag Lunches AKA BBLs). He is also author of FSCrawler project which helps to index your pdf, open office, whatever documents in elasticsearch using Apache Tika behind the scene.

Who am I?

Developer | Evangelist at elastic and creator of the Elastic French User Group. Frequent speaker about all things Elastic, in conferences, for User Groups and in companies with BBL talks. In my free time, I enjoy coding and deejaying as DJ Elky, just for fun. Living with my children in Cergy, France.

Social Links