Atlas M10+ only. Atlas Search index management requires MongoDB Atlas M10 or above. Shared-tier clusters (M0, M2, M5) do not support programmatic search index creation. Attempting this operation on a self-hosted MongoDB instance returns a driver error.
When to Use
- Automated environment provisioning: A DevOps provisioning workflow creates a new tenant environment — it creates the MongoDB databases, inserts seed documents, and then creates the required Atlas Search indexes. This ensures a fully functional search experience from the first user login without manual Atlas console steps.
- Search feature activation: When a SaaS tenant upgrades to a plan that includes full-text search, a workflow automatically creates the required Atlas Search indexes on their data collections, then sends a confirmation notification when the indexes are READY.
- Vector search index provisioning: An AI-powered search feature deployment workflow creates a
knnVector type index on the embedding field after populating a collection with AI-generated vector embeddings. Subsequent workflows use the index for semantic similarity search in $search pipeline stages.
- CI/CD infrastructure as code: A deployment pipeline applies a JSON-defined index schema to a target Atlas collection, creating all required search indexes as part of the release process. Idempotent — paired with searchindex/list to check existence before creating.
- Multi-language search index setup: When a product catalogue is localised for a new market, a workflow creates a new Atlas Search index with a language-specific analyzer (e.g.
lucene.french or lucene.japanese) on the same collection, enabling native-language relevance ranking without duplicating data.
Configuration
Connection
| Field | Required | Description |
ConnectionUri | Required | MongoDB Atlas connection string. Reference via {{ $credentials.mongodbAtlas }}. |
Database | Required | Database containing the collection to index. |
Collection | Required | Collection to create the Atlas Search index on. |
Operation
| Field | Required | Description |
IndexName | Required | Unique name for the search index within the collection. Must be unique per collection — attempting to create a duplicate name raises an error. Use descriptive names that reflect the index purpose, e.g. product_text_search, ticket_fulltext_v2. Atlas Search always creates a default index named "default" if none exists. |
Definition | Optional | The Atlas Search index definition as a JSON object. Default {} creates a dynamic index that automatically indexes all string fields. For production use, provide an explicit mappings object to control which fields are indexed, their types, and the analyzers applied. Supports all Atlas Search mapping types: string, number, date, boolean, document, embeddedDocuments, knnVector, token. |
Asynchronous build. The index is created immediately and the operation returns the
indexId. However the index will be in
PENDING or
BUILDING state and not queryable until the build completes. Build time depends on the number of existing documents in the collection — small collections may build in seconds; millions of documents may take minutes. Always poll
searchindex/list for
status: "READY" before running
$search queries.
Sample Configuration
Create a product full-text search index with explicit field mappings for an e-commerce collection:
{
"resource": "search-index",
"operation": "create",
"ConnectionUri": "{{ $credentials.mongodbAtlas }}",
"Database": "ecommerce",
"Collection": "products",
"IndexName": "product_fulltext_search",
"Definition": {
"mappings": {
"dynamic": false,
"fields": {
"name": {
"type": "string",
"analyzer": "lucene.standard",
"searchAnalyzer": "lucene.standard"
},
"description": {
"type": "string",
"analyzer": "lucene.standard"
},
"category": {
"type": "string",
"analyzer": "lucene.keyword"
},
"brand": {
"type": "string",
"analyzer": "lucene.keyword"
},
"price": {
"type": "number"
},
"inStock": {
"type": "boolean"
},
"tags": {
"type": "string",
"analyzer": "lucene.standard"
}
}
}
}
}
Validation Errors
| Error | Cause |
connectionUri is required | ConnectionUri is empty or not provided. |
database is required | Database field is empty. |
collection is required | Collection field is empty. |
indexName is required | IndexName is empty or whitespace. |
Output — Success Port
| Field | Type | Description |
indexId | string | The unique Atlas Search index ID assigned by MongoDB. Use this to reference the index in operational logging. The index may still be PENDING or BUILDING — use searchindex/list to check readiness. |
status | string | Always "success" on the success port — this means the creation request was accepted, not that the index is ready for queries. |
resource | string | Always "search-index". |
operation | string | Always "create". |
Sample Output
{
"indexId": "64f2a0e1c3b4d5e6f7a8b903",
"status": "success",
"resource": "search-index",
"operation": "create"
}
Expression Reference
| Expression | Returns | Description |
{{ $output.CreateSearchIndex.indexId }} | string | The Atlas-assigned index ID. Store in a variable for downstream reference. |
{{ $output.CreateSearchIndex.status }} | string | Confirm "success" — means the creation request was accepted by Atlas. |
Node Policies & GuardRails
| Policy | Recommendation |
| Credential storage | Always reference ConnectionUri from BizFirst Credentials Manager. Atlas Search index management uses the same credentials as document operations. |
| Check before create | Run searchindex/list before creating an index to check whether an index with the same name already exists. Attempting to create a duplicate name on the same collection raises an error. Implement the list-then-create pattern to make provisioning workflows idempotent. |
| Always poll for READY | The success port returning means the request was accepted, not that the index is available. Always follow index creation with a polling loop using searchindex/list that checks for status == "READY" before continuing downstream steps that issue $search queries. |
| Explicit mappings over dynamic | Use "dynamic": false with explicit field mappings for production indexes. Dynamic mapping automatically indexes every field and can consume excessive memory and compute on documents with many fields or deeply nested objects. |
| Choose the right analyzer | Use lucene.standard for general text fields (tokenised, lowercased). Use lucene.keyword for exact-match fields like category, status, or brand. Use language-specific analyzers (lucene.french, lucene.japanese) for internationalised content. Wrong analyzer selection leads to poor search relevance. |
| knnVector requires dimensions match | When creating a vector search index, the dimensions value must exactly match the dimensionality of the vectors stored in the field. A mismatch causes all vector searches to return no results or raise errors. |
| Error port handling | Connect the error port to an alert node. A failed index creation in a deployment workflow must be surfaced immediately — silent failures leave the application running without search functionality. |
Workflow Examples
Example 1 — Dynamic Index (Quick Setup)
{
"resource": "search-index",
"operation": "create",
"ConnectionUri": "{{ $credentials.mongodbAtlas }}",
"Database": "support",
"Collection": "tickets",
"IndexName": "default",
"Definition": {}
}
Example 2 — Vector Search Index for AI Embeddings
{
"resource": "search-index",
"operation": "create",
"ConnectionUri": "{{ $credentials.mongodbAtlas }}",
"Database": "knowledge_base",
"Collection": "articles",
"IndexName": "article_vector_search",
"Definition": {
"mappings": {
"dynamic": false,
"fields": {
"embedding": {
"type": "knnVector",
"dimensions": 1536,
"similarity": "cosine"
},
"title": {
"type": "string",
"analyzer": "lucene.standard"
},
"tenantId": {
"type": "string",
"analyzer": "lucene.keyword"
}
}
}
}
}
Example 3 — Idempotent Provisioning Pattern
// Step 1: List existing indexes
{
"resource": "search-index",
"operation": "list",
"ConnectionUri": "{{ $credentials.mongodbAtlas }}",
"Database": "ecommerce",
"Collection": "products"
}
// Step 2: IfCondition — check if "product_search" already exists
// indexes.some(i => i.name === "product_search")
// true → skip to poll loop
// false → proceed to create
// Step 3: Create the index
{
"resource": "search-index",
"operation": "create",
"ConnectionUri": "{{ $credentials.mongodbAtlas }}",
"Database": "ecommerce",
"Collection": "products",
"IndexName": "product_search",
"Definition": {
"mappings": { "dynamic": true }
}
}