An embedding is a numeric vector capturing meaning; a vector database stores and searches those vectors by similarity, not exact match. The two ideas are one capability: a model turns text (or images, or audio) into a list of numbers positioned so that similar meaning lands nearby, and a vector database finds the nearest points fast. That’s the whole trick behind semantic search, recommendations, and RAG — and for an app developer the practical headline is that embeddings are just a field you store next to the data they describe.
Key takeaways
| Question | Answer |
|---|---|
| Embedding | A vector where similar meaning → nearby points |
| Vector database | Stores vectors + answers “nearest to this” via ANN indexes |
| The metric | Cosine (usual for text) · dot product · Euclidean |
| Exact vs. ANN | Perfect-but-linear vs. ~99% recall, orders of magnitude faster |
| Do you need a dedicated one? | Usually not until ~10M+ vectors — your database likely does it |
Embeddings, concretely
Forget 1,536 dimensions for a moment and picture three: a toy model that scores each word on is-it-animal, is-it-vehicle, is-it-small.
animal vehicle small
kitten 0.95 0.02 0.90
cat 0.93 0.01 0.60
truck 0.03 0.96 0.05
motorcycle 0.04 0.94 0.55
"kitten" sits near "cat" (both high-animal) and far from "truck."
distance(kitten, cat) → small → similar
distance(kitten, truck) → large → unrelated
Real models use hundreds or thousands of dimensions instead of three,
learned from data rather than hand-labeled — but the intuition is exactly this:
meaning becomes geometry, and "similar" becomes "close."
The famous demonstration that this captures real structure is arithmetic on the vectors: in trained word embeddings, king − man + woman lands near queen. Meaning turned into coordinates does algebra.
How similarity search works
Three distance metrics dominate: cosine measures the angle between vectors (ignoring length), dot product folds in magnitude, and Euclidean is straight-line distance. The insight that saves confusion: for normalized vectors all three rank results identically, and since text-embedding models typically output normalized vectors tuned for cosine, “which metric?” is usually already answered for you.
Exact vs. approximate: the ANN trade-off
| Exact (kNN) | Approximate (ANN) | |
|---|---|---|
| Compares against | Every vector | A clever subset |
| Recall | 100% | ~95–99%, tunable |
| Speed | Linear — O(n) | Near-logarithmic |
| Fine until | ~1M vectors | Tens of millions+ |
| Cost | CPU per query | RAM for the graph |
Exact search checks the query against every stored vector — perfect, and perfectly fine up to around a million vectors. Past that, HNSW — the dominant approximate index — builds a layered proximity graph (sparse top layers for long jumps, a dense bottom layer holding everything) and greedily descends from coarse to fine, skipping most comparisons. It is a semantic cousin of the B-tree: where a B-tree indexes “equal to” and “less than,” an ANN index indexes “similar to.” The honest caveat competitors soften: ANN can miss the true nearest neighbor, its tunables trade recall against speed and memory, and the graph lives in RAM — which is why vector-database sizing is really a memory conversation.
Vector index vs. vector database
A distinction the term-soup obscures: an in-memory index library (like FAISS) computes nearest neighbors but doesn’t persist, update, or filter — reindex on restart, and there’s no WHERE clause. A vector database wraps the index with durability, incremental updates, and metadata filtering. That gap is exactly why “just use a library” fails in production and why your existing database with a vector index is often the right answer — it already has the durability, the transactions, and the permissions the library lacks.
Do you actually need a dedicated vector database?
The neutral answer the vendor pages can’t give. A general-purpose database with vector support — Postgres via pgvector, MongoDB’s HNSW vector indexes over array fields — comfortably serves roughly up to a million vectors per node, sitting right beside your application data and its permissions. A dedicated engine earns its operational cost and its dual-system sync tax (keeping the vector store consistent with the source-of-truth database) at genuinely large scale: tens of millions of vectors, very high query rates, or high recall demanded under heavy filtering. Below about a million vectors, a specialized store usually costs more in glue code than it returns in performance — the arithmetic most “you need a vector database” pages structurally can’t run, because they are the vector database.
Metadata filtering is the security boundary
Vector similarity ranks by meaning and knows nothing about who may read what — so the production query is never “the ten nearest neighbors,” it’s “the ten nearest neighbors this user is allowed to see.” Get the order wrong and it’s not a relevance bug, it’s a data leak: pre-filtering narrows candidates to the permitted set before the ANN search (correct); post-filtering trims after and both leaks the existence of forbidden items and silently returns fewer than k. When the filter is ACLs, pre-filtering is the difference between a semantic search and a breach — the same lesson RAG learns the hard way. Storing embeddings in the database that already enforces your permissions is how the filter comes for free.
Embeddings from real code
// JavaScript — Cloud Code (cloud/main.js)
// Embeddings live as a field next to the data they describe
Parse.Cloud.beforeSave('Doc', async (req) => {
if (req.object.dirty('text')) {
// generate server-side; embedding API key stays on the server
req.object.set('embedding', await embed(req.object.get('text')));
req.object.set('embedModel', 'text-embed-v3'); // version it — models differ
}
});
// Similarity search, ACL-filtered: "nearest neighbors this user MAY see"
Parse.Cloud.define('semanticSearch', async (req) => {
const qVec = await embed(req.params.q);
return vectorSearch('Doc', qVec, { limit: 10, aclUser: req.user });
// The pre-filter is the security boundary, not a relevance tweak.
}); // Flutter / Dart — Back4app Flutter SDK
// The client searches by meaning — the vector math is server-side
final results = await ParseCloudFunction('semanticSearch')
.execute(parameters: {'q': 'how do refunds work?'});
for (final doc in results.result) print(doc['title']);
// "refund policy" matches "money back" though they share no keywords —
// because their embeddings are near each other. Filtered to your ACL. // iOS / Swift — Back4app Swift SDK
// The client searches by meaning — the vector math is server-side
let results: [[String: Any]] = try await Cloud.run(
name: "semanticSearch",
parameters: ["q": "how do refunds work?"])
for doc in results { print(doc["title"] ?? "") }
// "refund policy" matches "money back" though they share no keywords —
// because their embeddings are near each other. Filtered to your ACL. // Android / Kotlin — Back4app Android SDK
// The client searches by meaning — the vector math is server-side
val params = mapOf("q" to "how do refunds work?")
val results = ParseCloud.callFunction<List<Map<String, Any>>>(
"semanticSearch", params)
results.forEach { println(it["title"]) }
// "refund policy" matches "money back" though they share no keywords —
// because their embeddings are near each other. Filtered to your ACL. Note the embedModel field: vectors from different models — or different versions of one model — are not comparable, so changing your embedding model means re-embedding the entire corpus. Store the model name with the vectors, or a silent upgrade turns your index into noise.
Common use cases
- Semantic search — “money back” finds “refund policy” though they share no keywords.
- RAG retrieval — fetching the chunks that ground an LLM’s answer.
- Recommendations — items near a user’s history in embedding space.
- Deduplication and clustering — near-identical content as near-identical vectors.
- Anomaly and fraud detection — outliers are vectors far from every normal example.
Where should your vectors live? A decision matrix
| Situation | Reach for |
|---|---|
| App-scale corpus (< ~1M vectors) | Your database’s vector index — beside the data |
| Vectors must respect user permissions | Wherever the ACLs already are |
| Tens of millions of vectors, high QPS | A dedicated vector engine |
| Exact terms and meaning both matter | Hybrid search with rank fusion |
| A quick prototype | pgvector, or an embedded store — don’t over-build |
| One process, no persistence needed | An index library (FAISS) — accept its limits |
Limitations and trade-offs
- Embeddings encode the model’s worldview. Bias, blind spots, and the training cutoff ride along; the geometry is only as good as the model that drew it.
- ANN is approximate on purpose. Tune recall to your stakes; “usually finds the best match” is the deal you signed for the speed.
- Model version is a schema. Re-embedding a large corpus on a model change is a real migration, not a config flip.
- Memory is the ceiling. HNSW graphs live in RAM; per-node vector limits are memory limits wearing a different label.
- Similarity isn’t relevance. Nearby in vector space is a strong signal, not a guarantee — hybrid search and re-ranking exist because pure vector search misses exact terms.
Vectors on Back4app
Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. Because it runs on MongoDB, the storage story is the one this article recommends: an embedding is an array field on the object it describes — the document and its vector in the same record — with HNSW vector indexing as the real mechanism underneath. The consequences chain cleanly: the ACLs that already govern every query become the pre-filter on similarity search for free, so retrieval can’t cross a permission boundary; embeddings are generated in a Cloud Code beforeSave trigger calling an embedding model with the key held server-side; and there is no separate vector service to keep in sync, secure, and pay for. For the app-scale corpora most products actually have, “vector database” isn’t a system you adopt — it’s a field you add.
Frequently asked questions
What is an embedding in simple terms?
A list of numbers a model assigns to a piece of data — text, an image, audio — so that things with similar meaning get similar numbers. Two documents about the same topic end up as nearby points in a high-dimensional space, which is what lets a computer measure "similar."
What is a vector database?
A system that stores embeddings and answers "which stored items are most similar to this one" quickly, using approximate-nearest-neighbor indexes instead of exact-match lookups. The "database" part — durability, updates, metadata filtering — is what separates it from a bare in-memory index library.
How does similarity search work?
The query is embedded with the same model, then compared to stored vectors by a distance metric: cosine (angle only), dot product (angle and magnitude), or Euclidean (straight-line distance). For normalized vectors all three rank results identically, and text-embedding models are usually tuned for cosine.
What is the difference between exact and approximate search?
Exact (kNN) compares the query against every vector — perfect recall, but linear time, fine up to around a million vectors. Approximate (ANN, usually HNSW) skips most comparisons for near-logarithmic speed, trading a few percent of recall for orders-of-magnitude faster queries. Missing the true nearest neighbor occasionally is usually acceptable.
What is HNSW?
Hierarchical Navigable Small World — the dominant production ANN index. It builds a multi-layer proximity graph: sparse top layers give long-range shortcuts, the dense bottom layer holds every vector, and search greedily descends from coarse to fine. Its tunables trade recall against speed and memory, and the graph lives in RAM.
What does "1536 dimensions" mean?
Each item is represented by that many floating-point numbers — a size popularized by a widely used commercial embedding model. More dimensions capture more nuance at more storage and compute cost; a 1,536-dimension float32 vector is about six kilobytes, which is why memory drives vector-database sizing.
Do I need a dedicated vector database?
Usually not at app scale. A general database with vector-index support — Postgres with pgvector, MongoDB's vector search — handles roughly up to a million vectors per node comfortably, right beside your app data and permissions. Dedicated engines earn their keep at tens of millions of vectors, very high query rates, or high recall under heavy filtering.
What is metadata filtering, and why does it matter?
Constraining similarity search by fields — tenant, category, date, permissions. Pre-filtering restricts candidates before the ANN search; post-filtering trims after and can silently return fewer than you asked for. Production queries are almost always filtered, and when the filter is permissions, it is a security boundary.
What is hybrid search?
Running keyword search (which nails exact terms, names, and IDs) and vector search (which nails meaning) together, then fusing the ranked lists — typically with reciprocal rank fusion, since keyword and cosine scores are not directly comparable. It fixes vector search's blind spot for rare and exact tokens.
What are vector databases used for besides RAG?
Semantic search, recommendations, near-duplicate detection, and anomaly or fraud detection (outliers are vectors far from everything else), plus image and audio similarity and clustering. Retrieval-augmented generation is the famous use; similarity search is the general capability underneath it.