Domain 2 of 4 · Chapter 3 of 12

Store embeddings and run vector similarity search in Cosmos DB

Two declarations, one container, one chance

A document in a vector-enabled container is an ordinary Azure Cosmos DB for NoSQL item with one unusual property on it: a long array of numbers.

Listing 1: one item, holding its own text, its metadata, and its embedding

{
  "id": "handbook-2026-p42",
  "tenantId": "contoso",
  "title": "Employee handbook 2026",
  "page": 42,
  "text": "Expense claims must be submitted within 30 days...",
  "embedding": [0.0123, -0.0456, 0.0789]
}

The embedding array is truncated here; a real one from a text model carries 1,536 or 3,072 numbers. That colocation is the point of the feature. Microsoft describes it as storing vectors directly in the documents alongside your data, so that the vectors are stored in the same logical unit as the data they represent[1]. One query can therefore return both the similarity ranking and the title and page your application needs to cite the answer, with no second lookup into another store.

The previous page in this domain, querying Cosmos DB with the Python SDK, covers getting items out when you know an id or can write a predicate. This page covers the case where you can do neither, because the thing you are looking for is semantically like your question rather than equal to any value you can name. That is still a search, in the same sense that page uses the word: the engine has to consider candidates. It is not an address. Everything here assumes you can already write a Cosmos DB query in Python and bind a parameter to it; by the end you will be able to declare the two policies a vector container needs, pick an index type your embedding's dimension count actually permits, write and scope the query that ranks against it, and tell which of those decisions you cannot take back.

The feature is off until you turn it on

Vector indexing and search is an account capability, not something a container inherits. You enable it on the account page under Settings then Features, selecting Vector Search for NoSQL API, or from the CLI:

az cosmosdb update \
     --resource-group <resource-group-name> \
     --name <account-name> \
     --capabilities EnableNoSQLVectorSearch

Microsoft notes the registration request is autoapproved, but it might take 15 minutes to take effect[1], which is long enough to look like a broken deployment if you are not expecting it.

The container vector policy: what the vector IS

With the capability on, a container needs a vector policy before it can run a similarity search. It is a vectorEmbeddings array, one entry per vector property, and Microsoft documents four fields: path (required, the property holding the vector), datatype, dimensions, and distanceFunction. The defaults are float32, 1536 dimensions, and cosine.

One detail bites people before they ever run a query, so take it at first sight rather than in a troubleshooting list later. Microsoft's prose writes the second field as datatype, all lower case, while every JSON sample on the same pages writes it as dataType. JSON keys are case sensitive, so those are not interchangeable. Copy the spelling from the samples, dataType, which is what the Python walkthrough's own policy object[2] uses.

The accepted values for that field are the other place the documentation is not of one mind, and it is worth knowing which way it splits. The integrated vector store page[1] lists float32, float16, int8, and uint8, adds a note that using float16 instead of float32 can reduce the storage footprint of vectors by 50 percent with some reduction in accuracy, and ships a two-path policy sample whose second path is declared float16. The performance-tuning page[3] devotes a table row to float16 and calls it the first precision reduction to try. Meanwhile the shared Azure-and-Fabric vector database page[4] lists only float32 (default), int8, and uint8, with float16 absent. Two pages support it — one ships a working two-path policy sample declaring float16, the other recommends it as the first precision reduction to try — while the third page's parameter list omits it entirely. Treat float16 as available on the Azure Cosmos DB for NoSQL surface, and confirm against the page for your exact surface before you build a storage estimate on it. The page that omits it is the shared Azure-and-Fabric one, and that is the lesson worth carrying past this one field: these pages run near-identical prose under different path shapes, so a sentence that matches what you were looking for proves nothing about whether you are reading the page that governs your surface. Check the URL, not just the wording.

Listing 2: a container vector policy with two vector paths

{
    "vectorEmbeddings": [
        {
            "path": "/embedding",
            "dataType": "float32",
            "distanceFunction": "cosine",
            "dimensions": 1536
        },
        {
            "path": "/coverImageVector",
            "dataType": "float16",
            "distanceFunction": "dotproduct",
            "dimensions": 100
        }
    ]
}

Each path gets at most one policy, though a container may hold several policies as long as they all target different paths. The distanceFunction value is the metric the engine uses to compare two vectors, and Microsoft documents three for a container policy: cosine, which runs from −1 for least similar to +1 for most similar; dot product, from −∞ to +∞; and euclidean, where 0 is most similar. Match it to the metric your embedding model is scored with rather than picking by taste. For Azure OpenAI text embeddings that is normally cosine, which is also the field's default, and Microsoft's own embeddings article says Azure OpenAI embeddings often rely on cosine similarity[5] to compare a document with a query.

One name collision is worth flagging here. Microsoft also publishes a distance functions explainer[6] that defines Manhattan distance alongside Euclidean, cosine and dot product. Manhattan is explained there as mathematics, not offered as a distanceFunction value; the container policy accepts the three named above.

The indexing policy: how the vector is SEARCHED

The vector policy alone will let a similarity query run, but not efficiently. The container's indexing policy carries a separate vectorIndexes array that names the same path and picks an index type, and Microsoft is direct about what it buys: vector searches have lower latency, higher throughput, and less RU consumption when applying a vector index[7].

The same policy should also push that path out of the ordinary index. An embedding is a large array that no WHERE clause will ever filter on, and the default policy indexes every property of every item. Microsoft states the consequence plainly: the vector path is added to the excludedPaths section to ensure optimized performance for insertion, and not adding the vector path to excludedPaths results in a higher request unit charge and latency for vector insertions[2]. Excluding the path from the standard index does not disable the vector index on it. They are different index structures over the same property, which is exactly what the figure below lays out.

Listing 3: creating the container with both policies, in Python

# Both policies are passed at CREATE time. There is no later edit for them.
container = db.create_container_if_not_exists(
    id=CONTAINER_NAME,
    partition_key=PartitionKey(path='/tenantId'),
    indexing_policy=indexing_policy,          # excludedPaths + vectorIndexes
    vector_embedding_policy=vector_embedding_policy)   # vectorEmbeddings

That single call is where the page's title earns the word chance. Both objects are supplied at creation, and the Python walkthrough[2] says why in one sentence: you set both the container vector policy and any vector indexing policy when you create the container because you can't modify it later. How firm that really is turns out to be the one thing the documentation argues with itself about, and the last section of this page takes it apart properly. For now, plan every choice below as permanent.

Two declarations, supplied together at container creationContainer vector policyvectorEmbeddings arraypath: /embeddingdataType: float32dimensions: 1536distanceFunction: cosinewhat the vector ISIndexing policyone policy, two entriesexcludedPaths: /embedding/*standard index skips itvectorIndexes: /embeddingtype: diskANNhow the vector is SEARCHEDOne item property/embeddingan array of 1536 numbersBoth declarations name the same path; neither one's settings can be edited in place afterwards.
The two policies a vector-enabled container carries, and the single item path they both point at.

Choosing the index type: the cap decides before the cost does

Read the dimension cap before you read anything else about the three index types, because it is the only one of their differences that can remove an option outright.

Azure Cosmos DB for NoSQL publishes three vector index types with a maximum dimension count each: flat at 505, quantizedFlat at 4,096, and diskANN at 4,096. Those numbers appear in the same table on both the indexing policy reference[7] and the integrated vector store page[1]. Set them beside the shape of a real embedding and the consequence is immediate: the vector policy's own default of 1,536 dimensions already exceeds 505, and text-embedding-3-large runs as high as 3,072, so flat cannot index either at any corpus size. The exact-recall option that reads so attractively in the comparison table is unavailable to most text-retrieval workloads before cost, latency or corpus size enter the conversation at all. The figure below places both of those sizes on a single axis against the three caps; everything to the right of the 505 mark is flat's blind spot.

What each type actually does

All three answer the same question, which stored vectors are nearest to this one, and they differ in what they consult to answer it.

  • flat stores the vectors on the same index as other indexed properties and scans them. Microsoft calls searches against it brute-force, producing 100 percent accuracy or recall, meaning it is guaranteed to find the most similar vectors in the dataset.
  • quantizedFlat also scans, but over quantized (compressed) vectors. Accuracy might be slightly less than 100 percent because the vectors are compressed before being added to the index, and in exchange the search should have lower latency, higher throughput and lower RU cost than the same search on a flat index.
  • diskANN is a separate index built for vectors using DiskANN, a suite of high-performance vector indexing algorithms developed by Microsoft Research. It is an approximate nearest neighbors index, so the accuracy might be lower than quantizedFlat or flat[7], and it offers some of the lowest latency, highest throughput and lowest RU cost queries in exchange. Microsoft's performance page describes it as a graph the search walks, which is why the sharding option later in this page talks about one graph per tenant.

That ordering is a real trade, not a ranking. Exact, compressed-and-exact, and approximate are three different answers to "how sure do you need to be", and only the first is a guarantee.

The 50,000 figure, and the honest version of it

Beyond the caps, Microsoft points quantizedFlat and diskANN at different corpus sizes using a threshold of roughly 50,000 vectors. Below it, quantizedFlat; above it, diskANN.

The threshold is genuinely useful and its denominator is not settled, so do not build a precise rule on it. The integrated vector store page[1] counts the vectors scoped to your search. The shared Azure-and-Fabric page[4] counts them per physical partition. The performance-tuning page[3] hedges across both, writing the sweet spot as fewer than about 50,000 vectors per search scope or partition. Those are not the same measurement: a large container that every query scopes tightly is under the threshold on the first reading and possibly over it on the second. Two of the three — the integrated vector store page and the shared Azure-and-Fabric page — also call the number a general guideline and tell you to test your own scenario, which is the instruction to follow when the denominator matters to your decision.

The floor nobody mentions until it bites

Both compressed types have a minimum. quantizedFlat and diskANN require that at least 1,000 vectors are inserted[1] so the quantization stays accurate, and below that a full scan is executed instead. A development container with 50 test documents therefore does not exercise the index you declared, and its RU numbers tell you nothing about production. Benchmark above the floor or do not benchmark.

Precision, as a second lever on the same axis

The dataType you declared in the vector policy is a cost lever as well as a description. Microsoft's published trade-off is float32 at baseline storage with full precision, float16 at half the storage with a small recall impact for most text workloads, and int8 at a quarter of the storage with a moderate impact, aimed at write-heavy workloads where insert cost matters most. The recommendation is to try float16 first and move to int8 only after validating recall on your own benchmark set[3]. Some of the lost recall can be bought back at query time rather than at write time, which the next section covers.

The build-time knobs, briefly

Both compressed types accept optional index build parameters. quantizationByteSize sets the bytes used per quantized vector, range 1 to 512, defaulting to a system-decided value, and larger values raise accuracy at the cost of RU and latency. indexingSearchListSize sets how many vectors are searched during index build, range 10 to 500, default 100, and applies to diskANN only. A quantizerType of product is the default; spherical is documented as public preview, so treat it as something to watch rather than something to standardise on.

Vector dimensions each index type can coverflatup to 505quantizedFlatup to 4096diskANNup to 40960505153630724096vector policy default1536 dimensionstext-embedding-3-largeup to 3072 dimensionsBoth common text sizessit past the flat cap.
The published maximum dimensions per index type, with two common text embedding sizes placed on the same axis.

Writing the query: ORDER BY is the search

A vector search is one system function used twice in one statement, and the second use is the one that matters.

Listing 4: the baseline indexed vector search

SELECT TOP 10 c.title, VectorDistance(c.embedding, @embedding) AS score
FROM c
ORDER BY VectorDistance(c.embedding, @embedding)

Microsoft calls this the simplest indexed vector search query[3] and says it uses the vector index and the distance function configured in the container's vector embedding policy. The occurrence in SELECT projects a number your application can rank or threshold on. The occurrence in ORDER BY is what makes the statement a nearest-neighbor search at all: ordering by distance is the operation an index over vectors exists to accelerate, and the function's own optional flag, described below, is defined in terms of how the value is used in an ORDER BY expression. The diagram below traces that one statement end to end, from the user's question to the ranked rows, so you can see which stage each clause is answerable for.

One thing about that listing is not optional. Microsoft attaches an Important callout to it: always use a TOP N clause[1], because otherwise the vector search tries to return many more results and the query costs more RUs and has higher latency than necessary. A vector query without TOP N is not a query that returns everything cheaply; it is the same ranking work spread over a far larger result set.

A note on how the function is spelled

You will meet this function under two spellings and they are one function. The Azure documentation set writes it VectorDistance, in the integrated vector store page[1], the indexing reference, the hybrid search page and the Python walkthrough alike. The query-language reference titles it VECTORDISTANCE in upper case, and then uses VectorDistance in one of its own examples. This guide uses VectorDistance, the form that appears in every Azure-scoped page and in the Python query strings you would copy. Do not read the upper-case spelling as a different or newer function.

The full signature

VectorDistance(<vector_expr_1>, <vector_expr_2>, <bool_expr>, <obj_expr>)

The query-language reference[8] gives the two required arguments as the stored vector path and the query vector, and two optional ones:

Argument Default What it does
bool_expr false true forces a brute-force search. false uses any index defined on the vector property, if one exists.
obj_expr none An object literal of query-time options, listed below.

The options object accepts distanceFunction (Cosine, DotProduct, Euclidean) to override the metric for this query, dataType (Float32, Float16, Int8, Uint8), and three tuning numbers. searchListSizeMultiplier raises the DiskANN search list size, quantizedVectorListMultiplier raises the quantized candidate list, and both improve recall at the cost of RU and latency; Microsoft gives 5, 10 and 20 as typical values. filterPriority takes a float between 0.0 and 1.0 and shifts a filtered diskANN query's balance between matching the WHERE clause and searching the vectors, with a higher priority biasing toward filter matches[3] for fewer RUs and a small recall cost.

One notation detail here will look like a typo the first time you meet it, and it is not. The option values are capitalised — Cosine, Float32 — while the container vector policy takes the same values in lower case, cosine and float32, in every JSON sample Microsoft ships. The Python walkthrough muddies it further: its parameter table writes the policy defaults as Float32 and Cosine while the policy sample directly below it uses lower case. Copy the case from a sample of the surface you are actually writing, lower case in the policy and capitalised in this options object, rather than from a prose table.

Those overrides are the query-time half of the precision decision from the previous section: cheaper vectors on disk, then a larger candidate list at read time when a particular query needs the recall back.

Sort direction is not yours to reason out

Every published example orders by a bare VectorDistance(...) with no ASC or DESC, and Microsoft describes the result as sorted in order of most similar to least similar. It is tempting to add a direction by reasoning about the metric, since cosine reports +1 for the most similar pair while euclidean reports 0, so the two would seem to need opposite sorts. Do not. Follow the published form, which is the same in every example regardless of which metric the policy declares.

Passing the query vector from Python

The query vector is an ordinary query parameter carrying a list of numbers. You embed the user's question with the same model that produced the stored vectors, then bind the result.

Listing 5: the same search from the Python SDK

# query_embedding comes from the SAME model and dimension count as the stored vectors.
query_embedding = embed("how long do I have to file an expense claim?")

for item in container.query_items(
        query=(
            'SELECT TOP 10 c.title, c.page, '
            'VectorDistance(c.embedding, @embedding) AS score '
            'FROM c '
            'ORDER BY VectorDistance(c.embedding, @embedding)'
        ),
        parameters=[{"name": "@embedding", "value": query_embedding}],
        partition_key="contoso"):     # scope the search; see the next section
    print(item["title"], item["page"], item["score"])

That mirrors the Python walkthrough's own query[2], which binds the embedding as @embedding through the parameters list exactly the way any other Cosmos DB parameter is bound. Two things in it are worth naming. The dimension count of query_embedding has to match the dimensions the policy declares: each of the three metrics compares two vectors coordinate by coordinate, and the policy fixes the length on the stored side, so a query vector of any other length has nothing to line up against. And score is your own alias on the projected value, not a system field, so an application that thresholds on it is reading a number it asked for.

What happens between a question and a ranked answerQuestionplain text from a userEmbedsame model, same dimensionsBind parameter@embedding, a number arrayScope and filterpartition key plus WHERE predicateORDER BY VectorDistancewalks the vector indexTOP Ntruncates the rankingRows back to the appscore plus title, page, idThe ORDER BY clause is the search. The SELECT projection is only the number you get to read.
One vector query end to end, from the user's question to a ranked page of items carrying their own metadata.

Scoping and filtering: tenants, predicates, and citable rows

The cheapest vector search is the one that never looks at another tenant's vectors, and the partition key is still how you arrange that.

Nothing about vector search changes the scope rungs this domain runs on. A query that supplies the partition key value is an in-partition query; one that does not fans out. Microsoft puts a number on why that matters here: a vector search of TOP 10 against a diskANN index scoped to a single partition key is estimated at up to about 45 RUs, and the same search cross-partition costs that much multiplied by the number of partitions hit[3], with the guidance that fan-out dominates cost and you should scope by partition key whenever possible. Those RU figures are Microsoft's own rough estimates and the page says so; the ratio is the durable part, not the number.

Partition key design for a multitenant corpus

The obvious key, one partition per tenant, runs into a hard ceiling: a logical partition holds at most 20 GB, and an embedding-heavy tenant reaches that faster than a document store would. Microsoft's recommended shape for this is a hierarchical partition key, for example /tenantId then /userId then /documentId, which removes the per-tenant ceiling because each combination is its own logical partition while still letting a query prune other tenants' partitions.

Scope the search to the full hierarchical key when the application knows all its levels, and to at least the top-level path when it does not. The figure below sets the three cases side by side: how much of the container the engine still has to consider when a query supplies none of the key, only its top level, or all of it. There is one piece of operational fine print worth carrying into a design review: Microsoft asks you to contact the team at cosmossearch@microsoft.com to configure the account[1] if you want vector search on collections with hierarchical partition keys. That is a lead time, not a config flag.

A diskANN index can then be sharded to match, using the vectorIndexShardKey property on the index entry. Microsoft describes sharded DiskANN as building a separate DiskANN index graph per logical partition key value rather than one global graph[3], so a tenant-scoped search stays inside that tenant's graph. The shard key can be the container's partition key, the first level of a hierarchical key, or a separate property.

Listing 6: a diskANN index sharded by tenant

{
    "indexingMode": "consistent",
    "automatic": true,
    "includedPaths": [{ "path": "/*" }],
    "excludedPaths": [
        { "path": "/_etag/?" },
        { "path": "/embedding/*" }
    ],
    "vectorIndexes": [
        {
            "path": "/embedding",
            "type": "diskANN",
            "vectorIndexShardKey": ["/tenantId"]
        }
    ]
}

JSON does not support comments, so nothing above is annotated; note that excludedPaths and vectorIndexes name the same /embedding path, which is the arrangement the first section described.

Filtering by metadata inside the search

Scope narrows which partitions are consulted. A WHERE clause narrows which items inside them qualify. Microsoft states that vector search can be combined with all other supported query filters and indexes by using WHERE clauses[1], which is what makes a filtered retrieval a single round trip rather than a fetch-then-filter in application code.

Listing 7: filtered semantic retrieval with the fields a citation needs

SELECT TOP 5 c.id, c.title, c.page, c.text,
       VectorDistance(c.embedding, @embedding) AS score
FROM c
WHERE c.tenantId = @tenantId AND c.category = 'policy'
ORDER BY VectorDistance(c.embedding, @embedding)

On a filtered diskANN query the filterPriority option from the previous section is the knob for rebalancing the two halves when results come back weak. Microsoft suggests 0.0 as a starting point that runs the similarity search and post-filters the candidates, 0.5 as balanced, and 1.0 as prioritising candidates that match the filter, which tends to converge faster at lower RU cost.

Why the projection list is part of the retrieval design

The SELECT list in Listing 7 is doing more work than it looks. Because the embedding lives in the same item as title, page and text, one query returns the ranking and everything the application needs to quote a passage and attribute it. Drop those fields from the projection to save bandwidth and you have signed up for a second round of reads keyed by id before you can show a citation, which costs more than the bytes you saved.

The practical rule for a retrieval-augmented generation pipeline is to project exactly the fields the answer will cite, plus the score, and nothing else. The score earns its place because it is your only means of dropping weak matches before they reach the model: a top-5 query always returns five rows, however unrelated they are to the question, and only a threshold on the projected value stops the fifth from being passed off as evidence.

How much the engine still considers, by how much of the key you supplylogical partitions in the containerCross-partition queryno partition key value suppliedIn-partition query, top level/tenantId suppliedIn-partition query, full key/tenantId + /userId + /documentIdconsidered by the enginepruned before the search runsSharded DiskANN narrows it once more: with vectorIndexShardKey on /tenantId,each tenant gets its own index graph rather than a share of one global graph.
The same vector search under three scopes: supplying more of the hierarchical partition key leaves the engine less of the container to consider.

Hybrid search: when semantic ranking alone misses the term

Semantic similarity is bad at exact tokens. Ask a vector index for "error MFA-4013" and it will happily return passages about authentication failures generally, because that is what the embedding of the phrase is near. Hybrid search is Azure Cosmos DB for NoSQL's answer, and it works by running both kinds of search and fusing their rankings.

Microsoft describes the two halves plainly. Vector search uses machine learning models to understand the semantic meaning of queries and documents. Full-text search scores documents on the presence and frequency of words and terms using Best Matching 25 (BM25), which the page calls effective for straightforward keyword searches. The results are then combined using the RRF function[9], Reciprocal Rank Fusion, described as a rank aggregation method that merges the rankings from multiple search algorithms to produce a single unified ranking.

The word rank is the part to hold on to. RRF combines positions in two ranked lists, not raw scores, which is what lets a cosine distance and a BM25 score be merged at all when they share no units and no scale. The diagram below shows that shape: one query scored down two independent paths, and a fusion step that reads list positions rather than either score.

What the container needs first

Hybrid search is not a query you can simply write against a vector container. Microsoft's own sequence is: enable the vector indexing and search feature; create a container with a vector policy, a full text policy, a vector index and a full text index; insert data with text and vector properties; then run hybrid queries. The indexing reference adds the second capability toggle, noting you must enable the Full Text & Hybrid Search for NoSQL API feature to specify a full text index[7].

Listing 8: the full text policy, alongside the vector policy already on the container

{
    "defaultLanguage": "en-US",
    "fullTextPaths": [
        {
            "path": "/text",
            "language": "en-US"
        }
    ]
}

The indexing policy then carries a fullTextIndexes array naming that same /text path, next to the vectorIndexes array naming /embedding. Four declarations in total, and Microsoft's requirement is symmetric for both: a full text indexing policy must be on the path defined in the container's full text policy, exactly as a vector index must be on the path in the vector policy.

The query shape

SELECT TOP 10 *
FROM c
ORDER BY RANK RRF(VectorDistance(c.embedding, @queryVector),
                  FullTextScore(c.text, @term1, @term2))

ORDER BY RANK is a distinct clause from the plain ORDER BY used for a pure vector search, and RRF takes the component scoring functions as its arguments. FullTextScore takes the text path followed by the search terms.

Weights are optional and go last, as an array with one number per component score. Microsoft's example for weighting the vector half twice as heavily as BM25 adds the array [2, 1] as the final argument to RRF[9]:

SELECT TOP 10 *
FROM c
ORDER BY RANK RRF(VectorDistance(c.embedding, @queryVector),
                  FullTextScore(c.text, @term1, @term2),
                  [2, 1])

The positional pairing is the thing to get right: the first weight applies to the first component score listed, so reordering the functions without reordering the weights silently reverses your intent. Nothing errors, the ranking just changes.

Reach for hybrid when your corpus contains identifiers, product names, error codes or other tokens a reader would type verbatim, and stay with a pure vector query when queries are natural-language paraphrases of the content. The cost of hybrid is not only RU; it is two more declarations on a container that, as the next section explains, you may not be able to amend later.

One query, scored two ways, fused by rank positionOne user queryterms plus a query vectorVectorDistance on /embeddingsemantic rankingFullTextScore on /textBM25 keyword rankingORDER BY RANK RRF(...)merges rank positions, optional weightsTwo scores with noshared scale.One unified ranking.
Hybrid search fuses a semantic ranking and a BM25 keyword ranking by rank position, not by raw score.

What you cannot change later, and where the docs disagree

Almost everything in a Cosmos DB indexing policy is editable on a live container. The vector parts are the exception, and the size of that exception is the one question this feature's documentation answers two different ways.

Start with the part that is not in dispute, because it is the frame that makes the rest legible. A container's indexing policy can be updated at any time[7], and the update triggers an online, in-place index transformation that does not affect write availability, read availability or provisioned throughput. The hybrid search page states the carve-out from the other direction, noting that other indexes still remain mutable. So the default expectation for anything in an indexing policy is changeable, and vectors are the thing that breaks it.

The two readings, side by side

Five Microsoft pages speak to whether a vector policy or vector index can change after the container exists, and they fall into two camps.

Reading Pages What they say
Create a new container index-policy, gen-ai/hybrid-search, how-to-python-vector-index-query Vector policies and vector indexes are immutable after creation; to make changes, create a new collection. The Python page puts it as supported on new containers only, because you can't modify it later.
Drop and re-add vector-search, gen-ai/vector-search-performance-tips You can add new path configurations or remove existing ones, but you cannot change the settings of a vector embedding policy or vector indexing policy directly; to do so you must first drop the existing vector policy or index, then add it back with new configuration.

Both camps agree on the sharp bit: there is no in-place edit of a vector policy's or vector index's settings. They disagree on what remains available instead, and specifically on whether adding or removing a vector path, and dropping and re-adding a policy, are things you can do to a container that already exists.

Do not settle this by publication date. The drop-and-re-add wording currently sits on the most recently updated page in the set, and it is still the minority reading; the other four interleave behind it rather than falling neatly into an older block. A single recent page contradicting three consistent siblings is at least as likely to be a documentation error as a behaviour change, and treating recency as evidence is precisely how a reader ends up planning a migration around a capability that may not be there.

Notice instead where the hedging sits, because it is not symmetric. All three pages on the create-a-new-container side qualify the rule with currentlyCurrently, vector policies and vector indexes are immutable after creation — which is Microsoft signalling that this particular answer is expected to move. Neither page on the drop-and-re-add side hedges at all, and the performance-tuning page states its version with an always: you can always add new path configurations or remove existing ones, but you cannot change the settings directly. So the reading this page tells you to design around is the one its own sources mark as provisional, and the reading you cannot yet rely on is the one stated flatly. Read that as a reason to re-check the wording before you migrate, not as a reason to prefer whichever sentence sounds more confident.

The engineering position that is correct under both readings

Design as though the vector policy and the vector index are fixed at container creation. That assumption costs you nothing if the drop-and-re-add path does exist and saves you an unrecoverable position if it does not. Concretely:

  • Decide the embedding model, and therefore the dimensions, before you create the container. A later model change is a new container and a full re-embed of the corpus.
  • Pick the index type for the corpus you expect, not the one you have on day one. A container that grows past the point where quantizedFlat is the right answer is the exact scenario the disagreement above leaves you unable to plan for confidently.
  • Build the re-ingest path early. If it is routine to create a container, replay the corpus and switch an alias in configuration, the question of whether the policy is mutable stops being load bearing.
  • Check the current wording on the integrated vector store page[1] before you commit a plan that needs the drop-and-re-add behaviour to be real.

The other things you cannot undo or exceed

Microsoft publishes a short list of current limitations on the same page, and three of them shape designs rather than merely constraining them.

  • Enabling vector indexing and search on a container cannot be reversed. It is a one-way door on the container, separate from the account-level capability.
  • Shared throughput is out. At this time vector indexing and search are not supported on accounts with shared throughput, so a database-level RU allocation is incompatible with the feature.
  • Wildcards and array nesting are out. Wildcard characters and vector paths nested inside arrays are not supported in the vector policy or the vector index, so the embedding has to be a plain top-level property.

One more is a throughput planning note rather than a limit: Microsoft says the rate of vector insertions should be limited, and that very large ingestion in excess of 5 million vectors in a short period might require more index build time. A backfill that finishes without errors can still leave the index catching up behind it.

Exam-pattern recognition

Questions on this subtopic tend to hand you a scenario with one number in it that has already decided the answer. Find the number first.

The dimension count is the most common decider

A stem that names an embedding model or its output size, then asks which vector index type to configure, is usually testing the 505-dimension cap on flat. Any dimension count above 505, which includes every common text embedding, eliminates flat outright, and the remaining choice between quantizedFlat and diskANN is made on corpus size around the 50,000-vector mark. An option offering flat for its exact recall on a 1,536-dimension vector is the distractor built for people who read the recall column and stopped.

Watch for its mirror image too: a stem with a small corpus and a small vector, where flat genuinely is available and exact recall is genuinely wanted. The cap removes options, it does not remove the type.

"We need to change it" stems

A scenario where a team wants to swap the distance function, change dimensions or move from quantizedFlat to diskANN on a container already holding data is testing whether you know this is not an ordinary indexing policy update. The answer that survives is the one involving a new container and re-ingesting the data. Be careful with any option asserting flatly that the policy simply can be edited in place; no reading of the documentation supports that, which is what makes it a clean distractor even while the docs remain split on the drop-and-re-add path.

Query-shape stems

Given a query that projects VectorDistance in SELECT but sorts by something else, or one that omits TOP N, the defect is in the clause, not the function. Ordering by VectorDistance is what performs the nearest-neighbor search, and Microsoft's Important callout about always using TOP N is a favourite source of "why is this query so expensive" scenarios. A missing TOP N costs RUs and latency; it does not produce an error, so the symptom in the stem will be cost, not failure.

If the query returns unrelated results with no error at all, look at whether the query vector came from the same model and dimension count as the stored ones, and look at whether the container is scoped correctly.

Cost and latency stems

Three causes cover most of them. A query with no partition key value is a cross-partition fan-out, and Microsoft's guidance is to scope by partition key whenever possible. A container whose vector path is missing from excludedPaths pays a higher request unit charge and latency on every insert, which shows up as expensive writes rather than expensive reads. And a container under 1,000 vectors is running a full scan regardless of the index type declared on it, which is the answer to a stem where a development environment's RU numbers do not match production's.

Retrieval-quality stems

When semantic search misses an exact identifier, product code or error string, the answer is hybrid search: a full text policy and full text index alongside the vector ones, and an ORDER BY RANK RRF(...) query fusing VectorDistance with FullTextScore. When results are merely a little weak on a diskANN index, the answer is query-time tuning, searchListSizeMultiplier or quantizedVectorListMultiplier, before anything structural. When a filtered diskANN query returns poor matches, filterPriority is the named knob.

And when the scenario is about citing sources in a generated answer, the answer is in the projection: the embedding and its source fields live in one item, so the query that ranks the chunks is the same query that returns the id, title and page the citation needs.

The three vector index types, and what actually decides between them

ConsiderationflatquantizedFlatdiskANN
What it storesThe vectors themselves, on the same index as other indexed propertiesQuantized (compressed) vectors on the indexA separate DiskANN index graph built specifically for vectors
Max dimensions5054,0964,096
How the search runsBrute-force scan of every candidate vector, exact k-nearest-neighborBrute-force scan, but over compressed vectorsApproximate nearest neighbor over the graph
Recall100 percent, guaranteed to find the most similar vectorsSlightly under 100 percent, because vectors are quantized before indexingHigh but approximate, and tunable at query time
Where Microsoft points itSmall, focused searches, especially combined with query filters and partition keysAround 50,000 vectors or fewer in the set your query coversMore than about 50,000 vectors in the set your query covers, up to multi-million scale
Minimum vectors to indexNone1,000, below which a full scan runs instead1,000, below which a full scan runs instead
Where it is the wrong choiceAny embedding over 505 dimensions, which includes most text modelsA corpus that keeps growing past the scoped thresholdA container too small to fill a graph, or one needing guaranteed exact recall

Decision tree

Choosing a vector index typeDimensions above 505?the cap decides before the costNo, 505 or fewerYes, flat is ruled outExact recall required?100 percent, guaranteedOver about 50,000 vectors?counted in the search scopeNo, size decidesYesflatexact nearest-neighbor scansmall, focused searchesquantizedFlatscan over compressed vectorsup to 4,096 dimensionsdiskANNapproximate, graph-basedup to 4,096 dimensionsNoYesUnder 1,000 vectors, quantizedFlat and diskANN run a full scan instead.Benchmark above that floor or the numbers mean nothing.

Sharp facts the exam loves — give these one last read before exam day.

Cheat sheet

Sharp facts the exam loves — scan these before test day.

A container's vector embedding policy declares the vector path, data type, distance function, and dimensions

To store embeddings you define a vector embedding policy at container creation listing each vector's path, dataType (for example float32), distanceFunction (cosine, dotproduct, or euclidean), and dimensions. The dimensions must equal the embedding model's output size, and the policy is set when the container is created.

Trap Planning to attach or change a vector embedding policy on a container that already exists.

5 questions test this
The policy's distance function must match how similarity is computed and the model's recommended metric

The distanceFunction chosen in the embedding policy (cosine, dotproduct, euclidean) defines how VectorDistance ranks similarity, so it must align with the embedding model's recommended metric — for example cosine for normalized text embeddings. Mismatching it degrades retrieval relevance.

Trap Assuming any distance function ranks a given model's embeddings equally well.

2 questions test this
Embeddings are stored as an array property in the same item as their source content and metadata

A vector is a numeric array field on the document, co-located with the chunk text and its metadata (id, title, page). Storing them together lets one query return both the similarity ranking and the fields needed to ground and cite an answer.

5 questions test this
The indexing policy defines a vector index of type flat, quantizedFlat, or diskANN

Separate from the embedding policy, the indexing policy's vectorIndexes specifies a vector index type per path: flat (exact brute-force), quantizedFlat (compressed vectors for less storage and faster scan), or diskANN (graph-based approximate nearest neighbor for large collections).

Trap Assuming the vector embedding policy also creates the vector index.

7 questions test this
diskANN is a graph ANN; quantizedFlat is a compressed scan; flat is exact

flat computes exact distances by scanning every candidate vector — accurate but costly at scale. quantizedFlat still scans, but over quantized (compressed) vectors, cutting RU and latency at a small accuracy cost. diskANN builds a true graph-based approximate-nearest-neighbor index that scales to millions of vectors at low latency. Choose diskANN for very large collections, quantizedFlat for a mid-size compressed scan, and flat only for small sets or when exact recall is required.

Trap Choosing flat for a million-vector collection because it is the most accurate.

7 questions test this
The vector path is typically excluded from the standard index while covered by the dedicated vector index

Because a raw embedding array is large, teams add its path to excludedPaths in the standard index to avoid needless write RU, while defining the vectorIndexes entry that actually powers similarity search on that same path.

VectorDistance() computes similarity between a stored vector and a query vector in a SQL query

The VectorDistance(c.embedding, @queryVector) system function returns the distance or similarity between a stored embedding and a supplied query vector using the container's configured metric, and can be projected as a score in the SELECT clause.

5 questions test this
ORDER BY VectorDistance(...) with TOP k performs the k-nearest-neighbor search using the vector index

A query of the form SELECT TOP @k c.text, VectorDistance(c.embedding, @q) AS score FROM c ORDER BY VectorDistance(c.embedding, @q) returns the k most semantically similar items and engages the vector index for efficient nearest-neighbor retrieval - approximate on a diskANN index, exact on a flat one.

Trap VectorDistance in SELECT alone does not use the vector index for ranking; the ORDER BY VectorDistance clause is what drives the k-NN search.

4 questions test this
The query vector is passed as a parameter and the projected score enables ranking and thresholding

You pass the query embedding as a query parameter (a numeric array) and project VectorDistance as an aliased score, letting the application rank matches and optionally drop results beyond a similarity threshold before sending context to the model.

5 questions test this

To restrict semantic retrieval to a subset (for example one tenant or document category), add a WHERE clause on indexed metadata alongside ORDER BY VectorDistance. Cosmos applies the filter and returns the top-k nearest within that scope — the core metadata-filtered RAG pattern.

8 questions test this
Returning source metadata (id, title, page) with each match is what enables grounded citations

Because embeddings live in the same document as their source fields, the vector query projects document id, title, and page next to the score. The application uses that returned metadata to ground the generated answer and attach an accurate citation to each retrieved chunk.

3 questions test this
Cosmos supports hybrid search that fuses full-text and vector ranking with Reciprocal Rank Fusion

Beyond pure vector search, Cosmos DB for NoSQL offers full-text search (for example FullTextScore) and hybrid queries that blend keyword and vector relevance using Reciprocal Rank Fusion (RRF), improving recall when lexical and semantic matches differ.

Also tested in

References

  1. Integrated vector store in Azure Cosmos DB for NoSQL
  2. Index and query vector data in Python - Azure Cosmos DB for NoSQL
  3. Tips for optimizing vector indexing and search performance in Azure Cosmos DB for NoSQL
  4. Vector database - Cosmos DB (in Azure and Fabric)
  5. Understand embeddings in Azure OpenAI
  6. Vector distance functions in Azure Cosmos DB for NoSQL
  7. Indexing policies in Azure Cosmos DB
  8. VECTORDISTANCE - Azure Cosmos DB for NoSQL query language reference
  9. Use hybrid search in Azure Cosmos DB for NoSQL