= Auto Embeddings :nav-title: Auto Embeddings :description: Rules that give every written document a vector for one of its fields, computed by your provider, one rule per vector field; under each rule, the steps to a semantic search, and a guided demo on the starter's catalog. :keywords: auto embedding, vectorSearch metadata, embedding field, $vectorScan, $vectorize, semantic search, vector index, RAG on MongoDB :group: AI :order: 25 Under **AI → Auto Embeddings** every collection of the service is listed, and each one may carry one or more rules: a text field, the field the vector goes to, and the provider and model that compute it. From then on, every document written with that text gets its vector, on your provider's bill. A second rule embeds another field with its own model, `summary` with one and `body` with another, each searchable. The rules live in the collection's `vectorSearch` metadata, so your own application can write them too. The page needs a key: without one, a bar at the top says so and takes you to xref:ai-keys.adoc[AI Keys], and back here once the key is saved. With one, the bar says which provider and model the service embeds with by default. == The rule Open a collection and **Add embedding**: * **Text field**: what the vector is computed from. The fields of a stored document are suggested. * **Vector field**: where the vector is stored, `embedding` by default. * **Provider** and **model**: among the providers saved under AI Keys, with the vendor's models; the default is what the service embeds with. * **Dimensions**: the vector's length, chosen among what the model allows. A vector index on the field must declare the same. * **Group by**, with a contextual model such as `voyage-context-4`: the documents of one write that share this field are embedded together, each vector aware of the others. `fileId` for the chunks of a file, as xref:auto-chunking.adoc[Auto Chunking] writes them. **Save** writes the rule into `vectorSearch: [ { textField, embeddingField, provider, model, dimensions }, … ]` on the collection, beside the collection's other rules; one rule per vector field. **Edit** with another model or length warns first: the vectors already stored compare with nothing, so saving unsets them, and the rule then offers to embed the documents again. **Remove** forgets the rule, leaves the other rules and the vectors where they are. A search picks the rule of the field it searches: `$vectorize` as the `queryVector` of a `$vectorScan` or `$vectorSearch` reads the stage's `path` and embeds the question with that field's model. Elsewhere, `{ "$vectorize": { "text": …, "field": "bodyVector" } }` names the field. == Under the rule Three lines say what is still missing for the collection to be searchable, and where it is done: * **Documents**: how many have the text and no vector, written before the rule or by something that bypassed it. **Embed them** patches each one with its own text, which is enough for the service to embed it: one call to the provider per document, with a counter. The console does this for up to 1,000 documents, one request at a time in the browser, and stops at the first document that gets no vector, with the provider's error. Past 1,000, **Show a script** gives a Python script that runs the same loop from your machine. * **Search**: the aggregation that searches the vectors, if there is one, with **Try it** to run it on the Aggregations page. Otherwise **Add search aggregation** opens Aggregations on the collection with the **Semantic search** preset filled in: `$vectorize` on the parameter `q`, `$vectorScan` on the vector field, no index needed, reranked when the service reranks. Save, and you are back here. * **Index**: the vector index on the field, if there is one, with its length. Otherwise **Add vector index** opens Indexes on the collection with the **Vector search** template filled in, name, field and dimensions. An index is optional: `$vectorScan` searches without one, and pays off as the collection grows. It needs a cluster with vector search; the search then switches to `$vectorSearch`. == Demo setup Under the list, **Demo setup** takes you from a rule to a semantic search on `catalog`, the collection of the ecommerce starter and of the xref:mcp.adoc#demo-setup[MCP demo]: the same products for the agent, the search and the shop. The demo embeds with a cheap model of a vendor you have a key for, `voyage-4-lite` with Voyage AI, `text-embedding-3-small` with OpenAI, the default model with any other provider, and writes it on the collection's rule: the default stays what you chose. Each step has a checkbox that says whether the thing exists, a button, and **Do all** runs the missing ones in order, stopping at the first error. [cols="1,2"] |=== | Step | What it does | Collection `catalog` with auto-embedding on `description` | Creates the collection if missing, and writes the rule on it with the demo's model: from then on every product written gets the vector of its description. When the collection's model differs from the demo's, the step reads **Switch model**: it drops the old vectors, and the index when the length changed, and the steps below compute them again. | Sample products of the ecommerce starter | Shown only while the collection is empty. Loads a hundred or so products from the starter's repository, each embedded as it is written. | Vectors of the products already stored | Shown only when the collection holds something. Products written before the rule have none: each is patched with its own description, which is enough for the service to embed it. One call to the provider per product. | Aggregation `search`, published on MCP | The question vectorized with `$vectorize` from the parameter `q`, then `$vectorScan` over the vectors, or `$vectorSearch` on the index once there is one. With a reranking model saved, a `rerank` block keeps the best five of the ten closest. Published on MCP with its parameter, so an agent can run it. | Vector index `catalog_vectors`, optional | A `vectorSearch` index on `embedding`, cosine similarity, with the dimensions of the demo's model. Where the cluster has vector search; the search step then reads **Rewrite**, to switch it to `$vectorSearch`. |=== Then ask: `GET /catalog/_aggrs/search?q=a gift for a gardener` returns the products closest in meaning to the question. == By hand Under **By hand** the same steps are HTTP calls, in curl, HTTPie, JavaScript and Python, with a temporary admin token in them, so each one runs as it is. Pick a tool on one block and every block follows. The calls are the ones your own application makes: [source] ---- PUT /catalog { "vectorSearch": [ { "textField": "description", "embeddingField": "embedding", "provider": "voyageEmbeddingProvider", "model": "voyage-4-lite", "dimensions": 1024 } ] } With an OpenAI-compatible provider the rule names it by URL, and the service attaches that provider's key. A single rule may also be written as the object alone, without the list: { "vectorSearch": { "textField": "description", "embeddingField": "embedding", "provider": "openAIEmbeddingProvider", "base-url": "https://openrouter.ai/api/v1", "model": "perplexity/pplx-embed-v1-4b", "dimensions": 1024 } } PATCH /catalog { "aggrs": [ { "uri": "search", "type": "pipeline", "stages": [ { "$vectorScan": { "path": "embedding", "queryVector": { "$vectorize": { "$var": "q" } }, "similarity": "cosine", "limit": 10 } }, { "$project": { "embedding": 0 } } ] } ] } GET /catalog/_aggrs/search?q=a%20gift%20for%20a%20gardener Optional, when the collection grows: PUT /catalog/_indexes/catalog_vectors { "type": "vectorSearch", "fields": [ { "type": "vector", "path": "embedding", "numDimensions": 1024, "similarity": "cosine" } ] } and in the search, in place of $vectorScan, with the score MongoDB keeps aside set on each result: { "$vectorSearch": { "index": "catalog_vectors", "path": "embedding", "queryVector": { "$vectorize": { "$var": "q" } }, "numCandidates": 100, "limit": 10 } }, { "$set": { "score": { "$meta": "vectorSearchScore" } } } ---- On a collection that already exists, send the first body with `PATCH`: `PUT` replaces the whole set of properties, `PATCH` merges. A vector index takes a few seconds to be ready after it is created; a search before that returns nothing, without an error. == What it costs The provider bills every embedding, one per document written and one per question, to your account. Ulabase adds nothing. **Embed them** and the sample products embed one document per call; the page says how many before you click. `$vectorScan` reads the vectors it compares on your service: fine for thousands of documents, an index for more.