All skills
Skillintermediate

Lexical Search - Indexing

This guide covers how to configure MongoDB Atlas Search indexes. Use this reference to build index definitions with proper field types, analyzers, mappings, and optimization settings.

Claude Code Knowledge Pack7/10/2026

Overview

Lexical Search - Indexing

This guide covers how to configure MongoDB Atlas Search indexes. Use this reference to build index definitions with proper field types, analyzers, mappings, and optimization settings.

Table of Contents


Atlas Search Index Definition

Syntax

{
  "analyzer": "<analyzer-for-index>",
  "searchAnalyzer": "<analyzer-for-query>",
  "mappings": {
    "dynamic": <boolean> | {
      "typeSet": "<typeSet-name>"
    },
    "fields": {
      <field-definition>
    }
  },
  "numPartitions": <integer>,
  "analyzers": [ <custom-analyzer> ],
  "storedSource": <boolean> | {
    <stored-source-definition>
  },
  "synonyms": [
    {
      <synonym-mapping-definition>
    }
  ],
  "typeSets": [
    {
      "types": [
        {<field-types-definition>}
      ]
    }
  ]
}

Options

FieldTypeRequiredDescription
analyzerStringOptionalSpecifies the analyzer to apply to string fields when indexing. If set only at the top level and not specified for individual fields, applies to all fields. If omitted, defaults to Standard Analyzer.
searchAnalyzerStringOptionalSpecifies the analyzer to apply to query text before searching. If omitted, defaults to the analyzer option. If both omitted, defaults to Standard Analyzer.
mappingsObjectRequiredSpecifies how to index fields at different paths for this index.
mappings.dynamicBoolean or ObjectOptionalEnables dynamic mapping of field types or configures fields individually. Set to true to recursively index all indexable field types, false to only index fields specified in mappings.fields, or specify a typeSet for configurable dynamic indexing. If omitted, defaults to false. Note: Dynamic indexing automatically and recursively indexes all nested documents unless explicitly disabled.
mappings.dynamic.typeSetStringOptionalReferences the name of the typeSets object that contains the list of field types to automatically and recursively index. Mutually exclusive with mappings.dynamic boolean flag.
mappings.fieldsObjectConditionalSpecifies the fields that you want to index. Required only if dynamic is false. You can't index fields that contain the dollar ($) sign at the start of the field name.
numPartitionsIntegerOptionalSpecifies the number of sub-indexes to create if the document count exceeds two billion. Valid values: 1, 2, 4. If omitted, defaults to 1. Requires search nodes deployed in your cluster.
analyzersArray of Custom AnalyzersOptionalSpecifies the custom analyzers to use in this index. Reference by name in analyzer, searchAnalyzer, or field-level analyzer options.
storedSourceBoolean or ObjectOptionalSpecifies fields in documents to store for query-time look-ups using returnStoredSource. Can be true (store all fields), false (store no fields), or an object specifying fields to include/exclude. Available on clusters running MongoDB 7.0+. If omitted, defaults to false.
synonymsArray of Synonym Mapping DefinitionOptionalSpecifies synonym mappings to use in your index. An index definition can have only one synonym mapping.
typeSetsArray of ObjectsOptionalSpecifies the typeSets to use for dynamic mappings.
typeSets.[n].nameStringRequiredSpecifies the name of the typeSet configuration.
typeSets.[n].typesArray of ObjectsRequiredSpecifies the field types to index automatically using dynamic mappings.
typeSets.[n].types.[n].typeStringRequiredSpecifies the field type to automatically index (e.g., "string", "number", "date").

Basic Definition

Most indexes only need the mappings configuration:

{
  "mappings": {
    "dynamic": <boolean> | { <typeSet-definition> },
    "fields": { <field-definition> }
  }
}

Analyzer Selection

The analyzer determines how text is processed for indexing and searching.

Default behavior: Most queries don't specify an analyzer and use MongoDB's default standard analyzer, which:

  • Divides text into terms based on word boundaries (language-neutral)
  • Converts terms to lowercase and removes punctuation
  • Recognizes email addresses, acronyms, CJK characters, alphanumerics, and more

Common built-in analyzers:

AnalyzerUse CaseExample
lucene.standardGeneral text search (default)"The quick brown fox" → ["quick", "brown", "fox"]
lucene.simpleLowercase, no special chars"Hello-World!" → ["hello", "world"]
lucene.keywordExact matching, facets"Action" → ["Action"]
lucene.whitespaceSplit on spaces only"first-class" → ["first-class"]
Language-specificStemming, stop wordslucene.english, lucene.spanish

Index Analyzer (Applied at Index Time)

Specify per-field or top-level for all fields:

// Field-level (add to mappings.fields):
{
  "title": {
    "type": "string",
    "analyzer": "lucene.standard"  // Or omit to use default
  },
  "category": {
    "type": "token"   // Exact matching — use token, not string with lucene.keyword
  }
}

// Top-level (applies to all fields unless overridden):
{
  "analyzer": "lucene.standard",
  "mappings": {
    "fields": {
      "title": { "type": "string" }  // Uses top-level analyzer
    }
  }
}

Search Analyzer (Applied at Query Time)

Apply different analysis to queries than to indexed content:

// Top-level searchAnalyzer:
{
  "searchAnalyzer": "lucene.simple",  // Query-time analyzer
  "mappings": {
    "fields": {
      "description": {
        "type": "string",
        "analyzer": "lucene.standard"  // Index-time analyzer
      }
    }
  }
}

Use case: Index with standard analysis, but search with simpler/synonym-aware analyzer.

If omitted, uses the index analyzer. If both omitted, defaults to lucene.standard.


Multi Analyzer (Alternate Analyzers for Same Field)

Index the same field with multiple analyzers:

// Field configuration (add to mappings.fields):
{
  "title": {
    "type": "string",
    "analyzer": "lucene.standard",  // Default analyzer
    "multi": {
      "keywordAnalyzer": {
        "type": "string",
        "analyzer": "lucene.keyword"  // Alternate analyzer
      }
    }
  }
}

Use case: Support both fuzzy matching and exact matching on the same field.

To query using the alternate analyzer, specify the path as fieldName.alternateAnalyzerName (e.g., title.keywordAnalyzer).


Custom Analyzers

Define custom tokenization and filtering:

// Index definition with custom analyzer:
{
  "analyzers": [
    {
      "name": "customAnalyzer",
      "tokenizer": {
        "type": "standard"
      },
      "charFilters": [],
      "tokenFilters": [
        { "type": "lowercase" },
        { "type": "stop", "tokens": ["the", "a", "an"] }
      ]
    }
  ],
  "mappings": {
    "fields": {
      "content": {
        "type": "string",
        "analyzer": "customAnalyzer"  // Reference custom analyzer
      }
    }
  }
}

Use case: Need specific tokenization or filtering not provided by built-in analyzers.


Normalizers (Token Type Only)

Normalizers produce a single token (used with token field type):

// Field configuration (add to mappings.fields):
{
  "username": {
    "type": "token",
    "normalizer": "lowercase"  // Options: "lowercase", "none"
  }
}

Normalizers:

  • lowercase: Transforms to lowercase, creates single token
  • none: No transformation, creates single token

Use case: Exact matching with case normalization for token fields.


Decision Guide

  • lucene.standard (or omit): Default for most text fields
  • lucene.keyword: Categories, tags
  • Language-specific: When you know the content language
  • searchAnalyzer: Different analysis for queries vs indexed content (e.g., synonyms)
  • multi: Support multiple search patterns on same field
  • Custom: Need specific tokenization/filtering logic
  • Normalizers: Token fields requiring case normalization

Field Types

Dynamic mapping includes: boolean, date, number, objectId, string, uuid Must configure explicitly: autocomplete, token, geo, embeddedDocuments, vector

Quick Reference

TypeWhen to UseRequired FieldsOptional Fields (with valid values)Notes
stringFull-text search, phrase matching, fuzzy searchtypeanalyzer, searchAnalyzer, indexOptions: "docs" or "freqs" or "positions" or "offsets", store: true or false, multiDefault for text. For sorting use token instead.
tokenSort/facet on text, exact matchingtypenormalizer: "lowercase" or "none" (default: "none")Required for sorting or faceting strings. Max 8181 chars.
autocompleteSearch-as-you-type, typeahead, partial or substring matchingtypeanalyzer, tokenization: "edgeGram" or "rightEdgeGram" or "nGram" (default: "edgeGram"), minGrams (default: 2), maxGrams (default: 15), foldDiacritics: true or falseNot included in "dynamic: true". Recommend maxGrams ≤ 15.
booleanTrue/false filterstypeNoneIncluded in "dynamic: true".
dateDate ranges, timestampstypeNoneIncluded in "dynamic: true".
numberNumeric queries, ranges, sortingtyperepresentation: "int64" or "double" (default: "double"), indexIntegers: true or false, indexDoubles: true or falseIncluded in "dynamic: true". Use int64 for large integers.
objectIdQuery by _idtypeNoneIncluded in "dynamic: true". Standard MongoDB ObjectIds.
uuidUUID identifierstypeNoneIncluded in "dynamic: true". BSON Binary Subtype 4.
geoLocation search, geographic queriestypeindexShapes: true or false (default: false)Included in "dynamic: true". Requires GeoJSON. Set indexShapes=true for polygons.
embeddedDocumentsSearch in arrays of objects, independent scoringtypedynamic: true or false or {typeSet: "name"} (default: false), fields, storedSource: true or false or {include/exclude}Not included in "dynamic: true". Max 5 nesting levels. Each nested document counts toward 2.1B limit.
vectorLexical prefilters for semantic searchtype, numDimensions (1-8192), similarity: "cosine" or "dotProduct" or "euclidean"quantization: "none" or "scalar" or "binary" (default: "none"), hnswOptions.maxEdges: 16-64, hnswOptions.numEdgeCandidates: 100-3200Not included in "dynamic: true". For hybrid search. See vector-search.md and hybrid-search.md.

Field definition structure:

{
  "mappings": {
    "fields": {
      "<field-name>": {
        "type": "<field-type>",
        // type-specific options here
      }
    }
  }
}

Multiple types on same field:

"<field-name>": [
  { "type": "string" },
  { "type": "token", "normalizer": "lowercase" }
]

Arrays: MongoDB Search automatically flattens arrays during indexing. Specify only the element type, not that it's an array.


Dynamic vs Explicit Mappings

Choose based on user's needs:

Use dynamic (true) when:

  • User is prototyping or exploring data with unknown schema
  • Need to get started quickly without defining all fields
  • All or most fields need to be searchable
  • Accept larger index size and slower performance for convenience

Use explicit (dynamic: false) (recommended for production) when:

  • User has completed early stages of prototyping and knows exactly which fields to search
  • Performance and index size are priorities
  • Schema is stable and well-defined
  • Only a subset of fields need to be searchable

Use typeSets (recommended for production) when:

  • User wants automatic indexing but with control over which types
  • Document schema is dynamic and new fields need to be indexed automatically without an index rebuild
  • Want different indexing strategies for different nested documents
  • Balance between convenience and performance is important

Dynamic mappings automatically index all fields:

{
  "mappings": {
    "dynamic": true  // Index everything
  }
}
  • Pros: Quick setup, works immediately
  • Cons: Larger index, slower queries, wastes resources on unused fields

Explicit mappings define exactly what to index:

{
  "mappings": {
    "dynamic": false,  // Only index specified fields
    "fields": {
      "title": { "type": "string" },
      "genre": { "type": "token" }
    }
  }
}
  • Pros: Smaller index, faster queries, precise control
  • Cons: Requires knowing your schema

Configurable dynamic with typeSets (recommended middle ground):

{
  "mappings": {
    "dynamic": {
      "typeSet": "customTypes"
    },
    "fields": {
      "metadata": {
        "type": "document",
        "dynamic": {
          "typeSet": "metadataTypes"
        }
      }
    }
  },
  "typeSets": [
    {
      "name": "customTypes",
      "types": [
        { "type": "string" },
        { "type": "number" }
      ]
    },
    {
      "name": "metadataTypes",
      "types": [
        {
          "type": "string",
          "analyzer": "lucene.standard"
        }
      ]
    }
  ]
}
  • Pros: Automatically indexes specified field types, more control than full dynamic, can configure different typeSets for sub-documents
  • Cons: Still indexes all fields of specified types

Recommendation: Use static mappings or a dynamic typeSet (within a specific path, not at the root document level) in production for optimized index size and performance.


Stored Source

Store frequently accessed fields directly in the search index (mongot) to avoid full document lookups from the database. This dramatically improves query performance, especially when filtering or sorting.

Requirements:

  • Available on clusters running MongoDB 7.0+
  • Stored fields must still be indexed separately to query them
  • Retrieve stored fields at query time using returnStoredSource: true (see lexical-search-querying.md)

Syntax:

{
  "storedSource": true | false | {
    "include" | "exclude": ["<field-name>", ...]
  }
}

Options:

true - Store all fields in documents. Not supported if index contains vector type field. Can significantly impact performance.

false - Don't store any fields (default behavior).

{ "include": [...] } - Store only specified fields. MongoDB Search a