Skip to main content

Command Palette

Search for a command to run...

Arabic search in production: normalization, stemming and relevance QA

A practical Elasticsearch and OpenSearch pipeline for Arabic normalization, protected terms, judged queries and rollback-safe releases.

Updated
9 min readView as Markdown
Arabic search in production: normalization, stemming and relevance QA
H
I have lead the Engineering for multiple startups in UAE. I also have my own agency qualascend.com.

Arabic search rarely fails because a team forgot to add a search box. It fails in the gap between what a user types, what the index stores, and what the analyzer quietly changes along the way.

A product name may contain an alef with hamza in the catalogue and a bare alef in the query. A user may include harakat, tatweel or Arabic-Indic digits. A light stemmer may improve recall for common words, then damage a brand or stock-keeping code. None of those failures is obvious from a green deployment.

The production job is to make transformations explicit, protect terms that must remain exact, and test ranking with the language people use in your market. This guide gives MENA product and engineering teams a workable starting point for Elasticsearch or OpenSearch. The configuration is a template, not a performance claim. Validate it against your own catalogue, query logs and human judgments before rollout.

Separate Unicode equivalence from search equivalence

Two strings can look the same while having different code-point sequences. Unicode Standard Annex #15 defines NFC, NFD, NFKC and NFKD. It explains that normalized strings give equivalent strings a unique binary representation. That solves a Unicode representation problem. It does not decide which different Arabic spellings your search product should treat as equivalent.

That distinction matters. Unicode normalization belongs near ingestion boundaries because it makes storage and comparison more predictable. Search normalization is a retrieval choice. It may collapse letters or marks to gain recall, and every collapse can introduce a false match.

Lucene makes those retrieval choices concrete. Its ArabicNormalizer maps alef with hamza or madda to bare alef, teh marbuta to heh, and alef maksura to yeh. It also removes harakat and tatweel. These rules explain why queries such as إمارات and امارات, or مدرسة and مدرسه, can converge after analysis.

They also expose a ranking risk. Mapping ى to ي can collapse على and علي, even though one may be a preposition and the other a person's name. Normalization is useful, but it is not harmless. Keep the original field available for exact matching, display and debugging.

Security needs a separate pass. Unicode Technical Standard #39 provides mechanisms for detecting confusables, mixed scripts and mixed numbers. Use that guidance to flag suspicious identifiers, seller names and account-facing input. Do not assume a language analyzer is also a spoofing control.

Start with the analyzer you can explain

Elasticsearch documents its Arabic analyzer as a standard tokenizer followed by lowercase, decimal_digit, Arabic stopwords, Arabic normalization, an optional keyword marker, and an Arabic stemmer. Lucene's ArabicAnalyzer likewise uses Arabic normalization, light stemming and Arabic stopwords. OpenSearch also documents an Arabic analyzer.

The following Elasticsearch-style configuration mirrors that documented sequence. It is a starting template for a disposable index. It has not been benchmarked against your data, so inspect token output and run relevance tests before using it in production.

PUT /products_ar_v1
{
  "settings": {
    "analysis": {
      "filter": {
        "ar_stop": {
          "type": "stop",
          "stopwords": "_arabic_"
        },
        "ar_protected": {
          "type": "keyword_marker",
          "keywords": ["سامسونج", "نون", "S24"]
        },
        "ar_stemmer": {
          "type": "stemmer",
          "language": "arabic"
        }
      },
      "analyzer": {
        "ar_product_text": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "decimal_digit",
            "ar_stop",
            "arabic_normalization",
            "ar_protected",
            "ar_stemmer"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "ar_product_text",
        "search_analyzer": "ar_product_text",
        "fields": {
          "raw": { "type": "keyword" }
        }
      }
    }
  }
}

The protected-term list is deliberately small. Populate it from real brands, model names, place names and domain terms for which stemming causes damage. Treat it as versioned search configuration, with an owner and regression tests, rather than an ever-growing exception dump.

Before indexing documents, inspect what the analyzer emits:

POST /products_ar_v1/_analyze
{
  "analyzer": "ar_product_text",
  "text": [
    "إمارات",
    "الإِمَارَات",
    "مدرسة",
    "مدرسه",
    "الـرياض",
    "الرياض",
    "هاتف ١٢٣",
    "هاتف 123"
  ]
}

Record the input, emitted tokens and the reason for every intended equivalence. If a reviewer cannot explain a transformation, it is not ready to become invisible production behavior.

Arabic search production pipeline: raw Arabic query passes through Unicode hygiene, tokenization, Arabic normalization, protected terms, light stemming, retrieval and relevance QA Credit: SultanByte. Based on the Unicode, Elastic and Lucene documentation linked in this guide.

Keep index-time and search-time behavior deliberate

Elastic says index and search analyzers usually should match. Matching analysis gives the query and stored text the same token rules, which is the safest default for a standard product or content field.

There are valid exceptions. Elastic identifies autocomplete and search-time synonyms as cases where the analyzers may differ. Make that difference field-specific and documented. Do not change the global search analyzer to fix one autocomplete requirement, then discover that normal queries now produce tokens the index never stored.

A practical mapping often keeps several representations:

  • an analyzed Arabic text field for recall;
  • a raw keyword subfield for exact values and diagnosis;
  • a separate autocomplete field if the product needs prefix behavior;
  • the untouched source string for display.

This separation makes failures easier to trace. If an exact title exists but the analyzed query misses it, compare the _analyze output on both sides. If an analyzed match ranks poorly, the problem is probably scoring, field weighting or competing documents rather than Unicode storage.

The Elastic ICU analysis plugin adds Unicode normalization, case folding, collation and transliteration. Those capabilities can help a multilingual index, but installing ICU is not a licence to fold every distinction. Choose each transformation for a named use case. Elastic also warns that ICU upgrades may require reindexing, so pin the plugin with the search stack and include analyzer output in upgrade tests.

Arabic is not one market setting

The W3C Arabic and Persian Layout Requirements covers Standard Arabic and Persian, and notes significant regional differences, including numeral conventions. Search QA should reflect that reality.

A GCC commerce catalogue, an Egyptian classifieds site and a Persian knowledge base do not share one complete query profile. Even where the same script appears, teams should build test cases from local content, keyboards, numeral habits, names and product terminology. Do not route Persian text through an Arabic configuration simply because the letters look related. Test each supported language and market as its own retrieval surface.

The same principle applies outside the index. A multilingual site must preserve language and direction across routes, rendering and caches. SultanByte's guide to Arabic and English routing, RTL and cache behavior in Next.js covers that frontend boundary. Search results are only useful if the destination page retains the intended locale.

Build a query set that can catch regressions

Start with a compact judged set that engineers can run on every analyzer or relevance change. Give each query an intent, expected documents, unacceptable documents and notes about the variation being tested.

Use pairs and edge cases such as:

Query Variant or comparison What to inspect
إمارات امارات Alef normalization and ranking stability
الإِمَارَات الامارات Harakat removal plus stopword behavior
مدرسة مدرسه Teh marbuta mapping and false positives
على علي Collision risk between a word and a name
الـرياض الرياض Tatweel removal
هاتف ١٢٣ هاتف 123 Decimal digit handling
سامسونج S24 unprotected stemmed form Brand and model protection
Arabic text with Latin product code Arabic-only version Mixed-script intent, token boundaries and spoof review

Add three result expectations to each case: the top result that should win, other acceptable results, and a result that must not enter the first page. That last category catches normalization collisions that recall-only tests miss.

Run the set against a fixed index snapshot. Capture analyzer tokens, ordered result IDs and scores. Review diffs rather than only pass/fail totals. A ranking change may be correct, but it should never be unexplained.

For judged evaluation, report Mean Reciprocal Rank when the first relevant result is the main concern. Use NDCG when relevance has grades or several results matter. Neither metric replaces query review. Segment both by language, market, query type and device input where the data supports it, otherwise a large easy-query bucket can hide a weak Arabic segment.

Join offline judgments to production signals

Production telemetry answers different questions from a judged set. Track at least:

  • Zero-result rate: searches that return no results divided by all searches. Break it down by normalized form and original query so folding does not hide demand.
  • Reformulation rate: sessions where a user changes the query soon after searching. Store the sequence so reviewers can see whether users removed harakat, changed numerals, switched scripts or corrected spelling.
  • Click-through rate: searches with a result click divided by searches with results. Read it beside rank position and result type. A click does not prove relevance, but a sudden fall after an analyzer change deserves inspection.
  • MRR and NDCG: offline ranking measures computed from the judged query set. Keep judgments and index snapshots versioned so releases remain comparable.

Do not publish one blended dashboard number and call the system healthy. Create slices for Arabic-only queries, mixed-script queries, brand and model searches, names, numeral variants, and queries affected by each normalization rule. Review high-volume queries and expensive failures, but retain a stable regression set so rare names and edge cases do not disappear from QA.

Ship analyzer changes like schema changes

An analyzer change alters indexed tokens. Treat it as a migration: create a versioned index, reindex the same source documents, run the judged set, compare production-like query slices, and switch an alias only after review. Keep the previous index available for a controlled rollback.

The release checklist is short enough to enforce:

  1. Compare _analyze output for the regression queries on old and new indices.
  2. Review every new zero result and every changed top result.
  3. Check protected brands, models, names and exact-match fields.
  4. Test Arabic-Indic and European digits, harakat, tatweel and mixed scripts.
  5. Calculate MRR or NDCG on the fixed judgments, then inspect the largest per-query losses.
  6. Watch zero-result, reformulation and click-through rates after the alias switch.

Arabic search does not need a mysterious relevance layer. It needs visible transformations, market-specific judgments and releases that can be rolled back. Start with the documented analyzer, preserve the original text, and make every extra equivalence earn its place in the query set.

Builder Guides

Part 1 of 12

Practical architecture, engineering, security and implementation guides for teams building technology in MENA.

Up next

Arabic and English in one Next.js app: routing, RTL and cache QA

A production guide to locale routes, RTL rendering, metadata, cache isolation and bilingual QA.