Missing or stale search results: Japanese analysis, refresh, and index rebuilds
Diagnose product search across inverted indexes, Japanese tokenization, delivery and refresh delay, and alias cutovers. Four tips connect troubleshooting with a rebuild that preserves updates and deletes.
Investigate missing and stale search results in layers
In a fictional product search, newly added items are absent, model-number queries miss results, and old prices return after an analyzer change. Do not group these symptoms under one “search quality” problem. Separate registration, delivery, analysis, query execution, and index cutover. Four tips use Elasticsearch documentation to follow those boundaries.
| Check | Evidence | Possible cause |
|---|---|---|
| Exists at source | Product ID, version, authorized lookup | Failed save or wrong tenant |
| Reached the index | Delivery position, failures, indexed ID and version | Delivery lag, transformation failure, missing delete |
| Tokenization | Index-time and query-time analysis output | Dictionary, normalization, or field mismatch |
| Actual search target | Alias, filters, and search visibility | Old index, access filters, or refresh delay |
An inverted index maps terms to documents
An inverted index keeps lists of document IDs for terms. Intersecting the lists for Redis and cache narrows candidates for an AND query. Real search also involves ranking and query semantics; it does not always return only a simple intersection.
Term A → documents 1, 3, 5 → Term B → documents 2, 3, 5 → Shared candidates: documents 3, 5 → Retrieve required documents and rank
An ordinary B-tree generally cannot selectively narrow a leading-wildcard LIKE predicate, while PostgreSQL pg_trgm provides GIN/GiST support for LIKE and ILIKE. Do not claim every LIKE requires a full scan; check matching requirements and the actual plan. If database search meets the requirements, a separately synchronized engine may be unnecessary.
Japanese analysis: evaluate misses and false matches
Character bi-grams can extract both Tokyo and Kyoto character pairs from the Japanese spelling of Tokyo Metropolis. That is a false match for someone seeking Kyoto information. Morphological analysis may keep Tokyo Metropolis as one term, depending on dictionary, mode, and filters. Inspect actual tokens, including new product names and model numbers.
POST /_analyze
{
"analyzer": "kuromoji",
"text": "東京都に住む"
}Compare index-time and query-time analysis, using an evaluation set with spelling variants, new words, model numbers, and place names. Separate n-gram and morphological fields are an option, with extra storage and relevance tuning. For an index-time analyzer change, check whether existing documents must be rebuilt.
Successful saves and search visibility: separate delivery from refresh
Elasticsearch refresh makes indexed changes searchable. Delivery from the source database happens before that. refresh=wait_for waits for visibility of that index write; it does not accelerate a change still in a queue. Check the chosen deployment’s settings rather than assuming identical defaults everywhere.
If one request takes two seconds to reach the index and another 0.7 seconds to become searchable, its serial delay is 2.7 seconds. This is an illustrative request, not a bound combining an average and a maximum. Return the registration confirmation from the authorized source and show pending search visibility on the list to reduce accidental resubmission.
Rebuild an index through copy, catch-up, and cutover
Prepare new analysis and mappings in products-v2 while searches use a products-read alias pointing to products-v1. After the full copy, replay updates and deletes made during copying. Connect the source snapshot to a log position, with version rules that prevent stale values overwriting newer ones.
- Prepare analysis and mappings, checking capacity and load headroom.
- Secure a consistent copy and a change-log range covering it.
- Catch up on updates and deletes; reconcile failed events, keys, and versions.
- Check search evaluations and visibility lag, establishing a cutover boundary without missed updates.
- Switch the alias in one operation and inspect the full response and action failures.
Elasticsearch supports alias changes in one request; multiple actions can partially succeed, so check conditions such as must_exist=true that make the full request fail on an action failure, and inspect the per-action results. Keeping the old index is not enough for a safe later rollback: keep it updated or catch it up before switching back. Do not assume in-flight requests all move to the new index at the same instant.
Validate expected results as well as updates and deletes
Compare expected top results, misses, and false matches using known model numbers, unwanted Kyoto matches, and new names. Update and delete during copying, then verify versions and absence after cutover. Matching counts can hide substituted documents, so reconcile IDs and versions at a common cutoff.
Primary sources
The official sources below were checked on October 27, 2026. Scenarios and numerical examples are illustrative assumptions. Configuration examples are explanatory; they have not been run against live services or benchmarked.
- PostgreSQL 18 — GIN
- PostgreSQL 18 — pg_trgm
- Elasticsearch — Kuromoji tokenizer
- Elasticsearch — Near real-time search
- Elasticsearch — refresh parameter
- Elasticsearch — Aliases
Tips this post digs into
- Speed up full-text search with an inverted index that maps each term to its documents
- Japanese has no spaces between words, so morphological analysis and n-grams change what is found and missed
- A successful write to a search engine does not appear in results immediately. Design for the delay
- Rebuild a search index by loading a new index, catching up, and then switching an alias