Zalando search fundamentals: Lucene and Elasticsearch 12 questions in 4 chapters
- 01Indexing and retrieval4 questions
- 02Scoring and relevance4 questions
- 03Elasticsearch internals and performance4 questions
- 04Sources
Indexing and retrieval
The inverted index, postings operations, analysis and the terms dictionary.
1.1 What is an inverted index, and how does Lucene store it? Medium 3 min
Instead of storing each document's words, an inverted index maps each term to the documents that contain it, often with frequencies and positions. A query looks up its terms and combines their postings lists instead of scanning documents. Lucene writes it as immutable segments, each with a terms dictionary, postings, stored fields, doc values and a live-documents file for deletes.
Read the full answer1.2 How do you evaluate Boolean AND and OR over postings lists? Medium 3 min
Postings are sorted by document ID, so AND is a merge-like intersection: compare the heads, advance the smaller one, keep matches. Start with the rarest term so the candidate set shrinks fast. OR is a merge that keeps every ID once. Skip pointers let AND jump ahead in long lists, and phrase queries also need positions, not just document IDs.
Read the full answer1.3 What happens during text analysis, and why must index and query analysis match? Medium 2 min
Analysis turns text into the terms that are indexed and searched: character filters clean the text, a tokenizer splits it, and token filters lowercase, stem, remove stop words or add synonyms. A query only matches if it produces the same terms as the document did, so the analysis at index time and search time must be compatible. Stemming and synonyms raise recall but can hurt precision.
Read the full answer1.4 What is an FST, and why does Lucene use one for the terms dictionary? Hard 2 min
A finite-state transducer is a compact automaton that maps keys to outputs. It shares common prefixes and suffixes, so a large sorted set of terms fits in little memory. Lucene's BlockTree terms dictionary groups terms into blocks by shared prefix and keeps that prefix trie in memory as an FST, which points to the right block on disk.
Read the full answer
Scoring and relevance
BM25, filters versus scoring, top-K and debugging a bad ranking.
2.1 What is BM25, and how does it differ from TF-IDF? Hard 4 min
BM25 scores a document for each query term from three things: how rare the term is, how often it appears in the document, and how long the document is. Unlike raw TF-IDF, repeated occurrences saturate, so the tenth mention adds little, and long documents are normalised. In Lucene, k1 controls the saturation and b the length normalisation, defaulting to 1.2 and 0.75.
Read the full answer2.2 What is the difference between query context and filter context, and why does it matter? Medium 2 min
A clause in query context contributes to the relevance score. A clause in filter context only decides yes or no and is not scored. Exact constraints such as size, brand, in stock or price range belong in filters. They skip scoring, Elasticsearch can cache frequently used filters, and they keep the score about text relevance rather than about whether a filter matched.
Read the full answer2.3 How do you return the top K results without scoring and sorting every match? Medium 2 min
Keep a min-heap of size K while iterating matches: compare each score with the smallest in the heap and replace it if larger. That costs n log K instead of n log n and needs memory only for K. Search engines go further and skip documents that cannot beat the current Kth score, using per-block maximum scores, so many matches are never fully scored.
Read the full answer2.4 A relevant product ranks too low or does not appear. How do you debug it? Medium 3 min
First I separate matching from ranking. If the product does not match, I check analysis with the analyze API, the mapping, and any filters or redirects that excluded it. If it matches but ranks low, I read the explain output to see which clauses, field weights and term statistics produced the score, then look at boosts and later ranking stages. I fix the cause and add the query to a judgment set.
Read the full answer
Elasticsearch internals and performance
Segments and merges, near-real-time refresh, shards and replicas, and memory sizing.
3.1 What is a Lucene segment, and how do updates, deletes and merges work? Hard 2 min
A segment is an immutable mini index written once. Lucene never edits it: an update writes a new version of the document into a new segment and marks the old one deleted in the live-documents bitset. Searches read every segment and skip deleted documents. Background merges combine small segments into larger ones and drop deleted documents, which costs I/O and CPU.
Read the full answer3.2 Why is Elasticsearch near-real-time, and how do refresh, flush and indexing latency trade off? Hard 2 min
New documents become searchable only after a refresh opens a new segment, which by default happens every second, but only for indices searched in the last thirty seconds. A flush is about durability: it commits segments to disk and trims the translog. Refreshing more often makes updates visible sooner but creates more small segments and merge work, so bulk loads often relax it.
Read the full answer3.3 How do shards and replicas shape query execution and latency? Hard 3 min
An index is split into primary shards, and each document lives on one of them. A search without routing goes to one copy of every shard, each shard scores locally, and the coordinating node merges the top hits and then fetches the documents. Replicas add read capacity and availability but cost disk and indexing work. Too many small shards adds overhead to every query.
Read the full answer3.4 How much heap should an Elasticsearch node get, and why does the page cache matter? Hard 3 min
Less than you might think. Elastic's guidance is to set the minimum and maximum heap equal, at no more than half the node's memory, and below the compressed object pointer threshold, which is about 26 gigabytes on most systems. Lucene reads its immutable segment files through memory mapping, so the rest of the memory serves as the operating system's file cache, which is what keeps searches fast.
Read the full answer
Sources
Source: Zalando Jobs: Senior Search Engineer, Search and Browse Core (job ad) Source: Manning, Raghavan and Schütze: Introduction to Information Retrieval (Cambridge University Press, 2008) Source: Lucene 9.9 index file formats (segments, term dictionary, postings, live documents) Source: Elasticsearch docs: Anatomy of an analyzer Source: Lucene 9.11 org.apache.lucene.util.fst package Source: Lucene 9.11 BlockTree terms dictionary (prefix trie stored as an FST) Source: Lucene 9.11 BM25Similarity Javadoc (defaults and IDF formula) Source: Elasticsearch docs: Query and filter context Source: Lucene 9.11 Scorable.setMinCompetitiveScore Source: Elasticsearch docs: Paginate search results (search_after) Source: Elasticsearch API: Explain a document match result Source: Elasticsearch docs: Near real-time search Source: Elasticsearch docs: Tune for indexing speed Source: Elasticsearch docs: Translog Source: Elasticsearch docs: Merge settings Source: Elasticsearch docs: Clusters, nodes and shards Source: Elasticsearch docs: Search shard routing and adaptive replica selection Source: Elasticsearch docs: Size your shards Source: Elasticsearch docs: JVM settings (heap size) Source: Elasticsearch docs: Tune for search speed (filesystem cache) Source: Elasticsearch docs: Virtual memory (vm.max_map_count) Source: Zalando Engineering: The Day Our Own Queries DoS'ed Us: Inside Zalando Search (December 2025) Source: Zalando Engineering: Migrating From Elasticsearch 7.17 to Elasticsearch 8.x (November 2023) Source: MICES 2026: Zalando Search: Architecture Behind Product Discovery (video) Source: arXiv 2409.02856: Building a Scalable, Effective, and Steerable Search and Ranking Platform (Zalando, 2024)