Chapter 19 · Vector Search in Cassandra 5.0

Vector CQL Type: Fixed Dimensions, Embeddings, Similarity, and Schema Constraints

Define Cassandra 5.0 vector schema contracts, embedding provenance, fixed dimensions, normalization, similarity metrics, and failure boundaries before indexing.

Advanced110–150 minutesVector schema + dimension labApache Cassandra 5.0.9 · Java 17 · cqlsh/nodetool · Java Driver 4.19.3 optional · SAI Vector Search · RF=3 · LOCAL_QUORUMLast reviewed: September 2026

Learning outcomes

AtlasMart wants semantic support search, but its first design review contains a dangerous sentence: “Store whatever embeddings the AI team sends us and add vector search later.” Cassandra makes the schema contract explicit: vector dimensions are fixed, vector-search elements must be floats, every vector belongs to one embedding space, and the index metric is part of the index definition. This lesson establishes those invariants before any ANN query.

01

Explain the Cassandra 5.0 VECTOR type as a fixed-length, non-null-element value and distinguish general vector storage from vector-search-compatible float vectors.

02

Record embedding model/version/preprocessing provenance so incomparable embedding spaces are never mixed silently.

03

Explain cosine, dot product, Euclidean similarity, normalization, ANN, exact top-k, and recall@k before using them.

04

Create a bounded AtlasMart vector schema and observe dimension/type/index state with CQL and SAI virtual tables.

05

Diagnose dimension mismatch, model-space mixing, and “Cassandra generates embeddings” as application/schema errors.

Chapter 19 lab baseline

The mandatory labs continue the disposable AtlasMart course cluster: Apache Cassandra 5.0.9 in the pinned cassandra:5.0.9 container, Java 17 inside the image, cluster atlasmart-course, Docker network atlasmart-cassandra, nodes atlasmart-cass-1..3, datacenter dc1, racks rack1..rack3, and 16 virtual nodes per node. The chapter keyspace is atlasmart_vector with NetworkTopologyStrategy, RF=3, and normal reads/writes at LOCAL_QUORUM. Tables explicitly use UnifiedCompactionStrategy (UCS), no default TTL, and the Cassandra default gc_grace_seconds unless a lesson says otherwise. Authentication, client TLS, internode TLS, and remote JMX are disabled only inside this isolated local learning network; production systems must enforce appropriate authentication, authorization, encryption, and network boundaries. Apache Cassandra Java Driver 4.19.3 is optional for application-level latency/vector-codec examples. The mandatory embedding data is a deterministic four-dimensional pedagogical fixture—free, local, and intentionally not a production semantic model. No paid embedding or LLM API is required.

Execution and safety note

Run commands only against the disposable Apache Cassandra course lab or another explicitly approved non-production environment. Confirm node, keyspace, table, container, volume, path, and datacenter targets before destructive, failure-injection, cleanup, repair, restore, security, or topology operations. Capture current state and expected rollback/recovery evidence first; output and timings can differ by host, operating system, Java runtime, Docker/runtime, driver, and Cassandra configuration.

Terms and mental model

A vector is an ordered fixed-length array of numbers. An embedding is a vector produced by a model or deterministic transformation to represent an object in a geometric space. Its dimension is the number of elements. Embedding provenance means the model name/version, preprocessing, normalization policy, and source revision that explain how the vector was created. A similarity function or distance rule decides what “near” means. Cosine similarity compares direction, dot product combines direction and magnitude unless vectors are normalized, and Euclidean is based on geometric distance. Normalization usually means scaling a vector to unit length. Approximate Nearest Neighbor (ANN) retrieval searches an index for likely nearest vectors without guaranteeing the exact top-k ordering; exact retrieval evaluates every candidate under the chosen metric. Recall@k is the fraction of exact top-k items recovered by ANN. It measures retrieval fidelity, not whether the documents are useful to a user.

Storage-Attached Indexing (SAI) is Cassandra 5.0's storage-integrated secondary indexing framework; vector ANN search uses an SAI index on a vector<float,n> column. A coordinator is the Cassandra node handling a particular client request. A replica stores data for a token range according to the keyspace replication strategy. A partition is the Cassandra storage/routing unit selected by a partition key; the partitioner hashes that key to a token. An immutable SSTable is an on-disk Sorted String Table. A consistency level (CL) states the replica acknowledgments/responses required by an operation. RAG (Retrieval-Augmented Generation) is an application architecture that retrieves evidence and supplies it to a generative model; Cassandra can be the retrieval store but does not generate embeddings or model answers.

1. The vector column is a schema contract

Cassandra 5.0 introduced VECTOR<type,dimension>. The value is fixed length and flattened as one value; elements cannot be null. Cassandra can store vectors of several element types, but vector search requires a floating-point vector. The documented dimension ceiling is 8192 elements. A schema such as vector<float,4> therefore rejects a three- or five-element value before it can poison an ANN index.

Contract Why it matters AtlasMart decision
dimension = 4 all vector arithmetic requires equal length toy lab only; production dimension comes from model
float elements SAI vector search operates on floating-point embeddings reject int/bigint search vectors
no null element geometry is undefined for missing coordinates validate embedding pipeline before write
model/version columns same dimension does not imply same embedding space store provenance beside every vector
similarity_function ANN ordering depends on one index metric choose and evaluate before build

2. Reproducible local schema and validation

bash · verify or recreate the disposable three-node cluster
# Verify the shared course lab first.docker exec atlasmart-cass-1 nodetool versiondocker exec atlasmart-cass-1 nodetool statusdocker exec atlasmart-cass-1 java -version# Recreate only if the disposable course cluster does not exist.docker network inspect atlasmart-cassandra >/dev/null 2>&1 || docker network create atlasmart-cassandradocker volume create atlasmart-cass-1-datadocker volume create atlasmart-cass-2-datadocker volume create atlasmart-cass-3-datadocker inspect atlasmart-cass-1 >/dev/null 2>&1 || docker run -d --name atlasmart-cass-1 --hostname atlasmart-cass-1 --network atlasmart-cassandra -e CASSANDRA_CLUSTER_NAME=atlasmart-course -e CASSANDRA_DC=dc1 -e CASSANDRA_RACK=rack1 -e CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch -e CASSANDRA_NUM_TOKENS=16 -v atlasmart-cass-1-data:/var/lib/cassandra cassandra:5.0.9# Wait until node 1 is UN before starting peers.docker exec atlasmart-cass-1 nodetool statusdocker inspect atlasmart-cass-2 >/dev/null 2>&1 || docker run -d --name atlasmart-cass-2 --hostname atlasmart-cass-2 --network atlasmart-cassandra -e CASSANDRA_CLUSTER_NAME=atlasmart-course -e CASSANDRA_DC=dc1 -e CASSANDRA_RACK=rack2 -e CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch -e CASSANDRA_NUM_TOKENS=16 -e CASSANDRA_SEEDS=atlasmart-cass-1 -v atlasmart-cass-2-data:/var/lib/cassandra cassandra:5.0.9docker inspect atlasmart-cass-3 >/dev/null 2>&1 || docker run -d --name atlasmart-cass-3 --hostname atlasmart-cass-3 --network atlasmart-cassandra -e CASSANDRA_CLUSTER_NAME=atlasmart-course -e CASSANDRA_DC=dc1 -e CASSANDRA_RACK=rack3 -e CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch -e CASSANDRA_NUM_TOKENS=16 -e CASSANDRA_SEEDS=atlasmart-cass-1 -v atlasmart-cass-3-data:/var/lib/cassandra cassandra:5.0.9# Continue only after all three nodes are UN.docker exec atlasmart-cass-1 nodetool statusdocker exec -it atlasmart-cass-1 cqlsh
CQL · create the bounded AtlasMart vector schema
CREATE KEYSPACE IF NOT EXISTS atlasmart_vectorWITH replication = {'class':'NetworkTopologyStrategy','dc1':3};CREATE TABLE IF NOT EXISTS atlasmart_vector.documents (    tenant_id text,    corpus_bucket tinyint,    document_id uuid,    title text,    body text,    locale text,    doc_type text,    embedding_model text,    embedding_version text,    embedding vector<float,4>,    updated_at timestamp,    PRIMARY KEY ((tenant_id,corpus_bucket),document_id)) WITH compaction = {'class':'UnifiedCompactionStrategy'};CONSISTENCY LOCAL_QUORUM;
CQL · load deterministic four-dimensional teaching vectors
INSERT INTO atlasmart_vector.documents (tenant_id,corpus_bucket,document_id,title,body,locale,doc_type,embedding_model,embedding_version,embedding,updated_at) VALUES ('tenant-a',0,00000000-0000-0000-0000-000000001901,'Return policy','How to return an unopened product within the return window.','en','support','atlasmart-toy','v1',[1.0,0.0,0.0,0.0],'2026-09-08T07:00:00Z');INSERT INTO atlasmart_vector.documents (tenant_id,corpus_bucket,document_id,title,body,locale,doc_type,embedding_model,embedding_version,embedding,updated_at) VALUES ('tenant-a',0,00000000-0000-0000-0000-000000001902,'Refund delay','Why approved refunds can take several days to appear.','en','support','atlasmart-toy','v1',[0.9,0.2,0.1,0.0],'2026-09-08T07:01:00Z');INSERT INTO atlasmart_vector.documents (tenant_id,corpus_bucket,document_id,title,body,locale,doc_type,embedding_model,embedding_version,embedding,updated_at) VALUES ('tenant-a',0,00000000-0000-0000-0000-000000001903,'Shipping tracker','Track a parcel after warehouse dispatch.','en','support','atlasmart-toy','v1',[0.0,1.0,0.0,0.0],'2026-09-08T07:02:00Z');INSERT INTO atlasmart_vector.documents (tenant_id,corpus_bucket,document_id,title,body,locale,doc_type,embedding_model,embedding_version,embedding,updated_at) VALUES ('tenant-a',0,00000000-0000-0000-0000-000000001904,'Password reset','Recover access to an AtlasMart account.','en','security','atlasmart-toy','v1',[0.0,0.0,1.0,0.0],'2026-09-08T07:03:00Z');INSERT INTO atlasmart_vector.documents (tenant_id,corpus_bucket,document_id,title,body,locale,doc_type,embedding_model,embedding_version,embedding,updated_at) VALUES ('tenant-a',1,00000000-0000-0000-0000-000000001905,'Invoice copy','Download a VAT invoice for an order.','en','billing','atlasmart-toy','v1',[0.0,0.0,0.0,1.0],'2026-09-08T07:04:00Z');INSERT INTO atlasmart_vector.documents (tenant_id,corpus_bucket,document_id,title,body,locale,doc_type,embedding_model,embedding_version,embedding,updated_at) VALUES ('tenant-a',1,00000000-0000-0000-0000-000000001906,'Exchange item','Exchange an eligible item for another size.','en','support','atlasmart-toy','v1',[0.8,0.1,0.0,0.2],'2026-09-08T07:05:00Z');INSERT INTO atlasmart_vector.documents (tenant_id,corpus_bucket,document_id,title,body,locale,doc_type,embedding_model,embedding_version,embedding,updated_at) VALUES ('tenant-a',1,00000000-0000-0000-0000-000000001907,'Cancel order','Cancel before warehouse fulfillment begins.','en','support','atlasmart-toy','v1',[0.7,0.0,0.2,0.1],'2026-09-08T07:06:00Z');INSERT INTO atlasmart_vector.documents (tenant_id,corpus_bucket,document_id,title,body,locale,doc_type,embedding_model,embedding_version,embedding,updated_at) VALUES ('tenant-a',1,00000000-0000-0000-0000-000000001908,'Delivery delay','Investigate a shipment that missed its delivery date.','en','support','atlasmart-toy','v1',[0.1,0.9,0.0,0.0],'2026-09-08T07:07:00Z');INSERT INTO atlasmart_vector.documents (tenant_id,corpus_bucket,document_id,title,body,locale,doc_type,embedding_model,embedding_version,embedding,updated_at) VALUES ('tenant-b',0,00000000-0000-0000-0000-000000001909,'Tenant B private return note','Private tenant B support content.','en','support','atlasmart-toy','v1',[0.99,0.01,0.0,0.0],'2026-09-08T07:08:00Z');

The four numbers here are not generated by a language model. They are an intentionally transparent teaching embedding: the first axis roughly represents returns/refunds, the second shipping, the third account/security, and the fourth billing. That makes expected neighborhoods understandable without a paid API or opaque model. Production embeddings require a real model and a documented preprocessing/version contract.

CQL · deliberate dimension/type boundary tests
-- Valid: exactly four float-compatible values.INSERT INTO atlasmart_vector.documents (tenant_id,corpus_bucket,document_id,title,body,locale,doc_type,embedding_model,embedding_version,embedding,updated_at)VALUES ('tenant-a',0,00000000-0000-0000-0000-000000001910,'Boundary valid','dimension four','en','lab','atlasmart-toy','v1',[0.25,0.25,0.25,0.25],toTimestamp(now()));-- Deliberately wrong: dimension three for vector<float,4>.INSERT INTO atlasmart_vector.documents (tenant_id,corpus_bucket,document_id,title,body,locale,doc_type,embedding_model,embedding_version,embedding,updated_at)VALUES ('tenant-a',0,00000000-0000-0000-0000-000000001911,'Wrong dimension','must fail','en','lab','atlasmart-toy','v1',[0.1,0.2,0.3],toTimestamp(now()));-- Inspect the schema instead of inferring dimension from one row.DESCRIBE TABLE atlasmart_vector.documents;
Expected failure, exact wording version-dependent.

The second insert should fail because the value does not match vector<float,4>. The exact cqlsh/native-protocol message can vary. A rejection proves schema validation; it does not prove your embedding is semantically meaningful.

3. Embedding provenance is part of correctness

Two models can both produce 384-dimensional vectors and still define unrelated coordinate systems. Comparing them is as invalid as measuring a temperature in Celsius against an unrelated identifier that happens to be numeric. Record at least model name, model/version/hash, preprocessing/tokenization, normalization policy, source revision, and embedding timestamp. If the embedding space changes, treat migration as a search-index/data migration—not an in-place semantic overwrite hidden behind the same version string.

CQL · prove provenance is queryable data
SELECT tenant_id,corpus_bucket,document_id,title,embedding_model,embedding_version,embeddingFROM atlasmart_vector.documentsWHERE tenant_id='tenant-a' AND corpus_bucket=0;
Wrong approach: mix v1 and v2 because the dimensions match.

Dimension compatibility is syntactic, not semantic. A safer migration writes v2 embeddings into a separate versioned table/column/index, evaluates v1 and v2 against the same ground-truth queries, cuts traffic deliberately, and retains rollback until the new space is proven.

4. Verification and reset

CQL · remove only the deliberate boundary row and verify baseline
DELETE FROM atlasmart_vector.documentsWHERE tenant_id='tenant-a' AND corpus_bucket=0  AND document_id=00000000-0000-0000-0000-000000001910;SELECT count(*) FROM atlasmart_vector.documentsWHERE tenant_id='tenant-a' AND corpus_bucket=0;
  • All three Cassandra nodes are UN.
  • The schema is exactly vector<float,4>.
  • The three-element insert is rejected.
  • Every retained row has explicit embedding model and version metadata.
  • No external embedding API is required for the mandatory lab.

Check your understanding

  1. Does equal vector dimension prove two embeddings can be compared?
  2. Can Cassandra vector search use vector?
  3. What does recall@k measure?
  4. Why store embedding_version in the row?
  5. Does Cassandra generate embeddings?
Review the answers

1. No. They must also belong to the same compatible embedding space and preprocessing/normalization contract.

2. Cassandra can store vector values of other element types, but vector search is defined for floating-point vectors.

3. How many exact top-k neighbors are recovered by ANN; it does not measure whether the retrieved documents answer the business question.

4. It makes semantic provenance observable and supports safe dual-version migration/evaluation.

5. No. Cassandra stores/indexes/searches vectors; the application or external/local model pipeline generates them.

Production judgment

Decide whether Cassandra vector search fits by measuring the whole retrieval system: corpus size and growth; vector dimension and bytes; embedding generation/update rate; embedding provenance; tenant and authorization model; query filters and bucket fanout; RF/CL and node/DC failures; ANN recall@k against a fixed exact baseline; application relevance metrics; p50/p95/p99 retrieval latency; result payload size; write amplification; SAI disk and memory footprint; SSTable count/compaction; vector overwrite/delete rate; index build/rebuild/streaming time; repair/backup/restore behavior; JVM/off-heap/chunk-cache pressure; driver timeouts/retries/idempotency; guardrails; observability; and operator skill. Do not report ANN latency without recall, and do not call recall “answer quality.”

Security is not a similarity metric. Cassandra role permissions are table/keyspace oriented, not row-level authorization; an application must derive allowed tenant/corpus scope before retrieval and encode that scope in the data model/query. Post-filtering unauthorized ANN results can leak information or reduce useful top-k results. Managed services and Cassandra-compatible APIs may differ in vector syntax, limits, index lifecycle, metrics, encryption, billing, and topology. Lesson 2 adds the SAI vector index and traces actual ANN queries, build state, limits, and failure behavior.

Summary and next bridge

Vector search begins with a stable embedding-space contract, not with CREATE INDEX. Cassandra enforces dimension/type structure; your application owns semantic provenance and preprocessing. Next, build the SAI ANN index and observe how Cassandra serves approximate neighbors.

Authoritative references

Vector search and SAI are version-sensitive. Re-check these sources when regenerating the lesson rather than freezing a 2026 assumption.

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.