Vector search
Vector search is a technique for retrieving contextual and semantically related items based on the similarity of their vector representations. It operates on numerical vectors, which represent data such as text, images, or audio. These vectors, known as vector embeddings, are typically generated using embedding models such as Word2Vec, GloVe, BERT, and GPT for text data, ResNet for image data, or Wav2Vec for audio data.
Vector search is widely used in various applications, especially to power generative AI workflows, such as conversational agents and chatbots. It is also used for building recommendation systems for personalized content and product suggestions, and to enable semantic search for context-aware document retrieval.
Vector indexes
Every vector search application requires a method to calculate the similarity between two vectors. Similarity metrics such as Cosine similarity and Euclidean distance help calculate the distance between vectors and are essential for finding the nearest neighbors of a query from the same embedding model.
FerretDB supports the following vector index kinds:
- Hierarchical Navigable Small World (HNSW): HNSW is a graph-based index that uses a hierarchical structure to store vectors, suitable for high-speed vector search in memory.
- Inverted File (IVF): IVF is an inverted file index that partitions the vector space (data sets) into clusters (inverted lists) and performs approximate nearest neighbor search within selected clusters.
Creating an index
Vector index can be created using the usual createIndexes
command with the following syntax:
db.runCommand({
createIndexes: "<collectionName>",
indexes: [
{
name: "<indexName>",
key: {
"<path>": "cosmosSearch",
},
cosmosSearchOptions: {
kind: "<kind>",
similarity: "<similarity>",
dimensions: "<dimensions>",
// HNSW only
m: "<m>",
efConstruction: "<efConstruction>",
// IVF only
numLists: "<numLists>",
},
},
],
});
A vector index definition can take the following parameters, depending on the kind of index:
Field | Type | Description | Index kind |
---|---|---|---|
<path> | string | The path to the field containing the vector embeddings. The field must be mapped to "cosmosSearch" to enable creation of a vector index. | All |
cosmosSearchOptions | document | It specifies the configuration parameters (kind , similarity , dimensions , etc.) for the vector index to suit your specific use case | All |
kind | string | The kind of index to create. Possible values: vector-hnsw , vector-ivf . | All |
similarity | string | The similarity metric to use for nearest neighbor search. Option could be COS for cosine similarity, L2 for Euclidean distance, or IP for inner product. | All |
dimensions | integer | The number of dimensions in the vector. Note that all vectors in the collection must have the same dimensionality. Must range from 2-16000 | All |
m | integer | The maximum number of connections per layer. Typically range from 2 to 100 (default: 16). | HNSW |
efConstruction | integer | The number of neighbors or dynamic candidate list to search during graph construction. Typically 4 to 1000 (default: 64). Must be at least 2 * m . | HNSW |
numLists | integer | The number of lists to store in the index. Ranges from 1 to 32768 (default: 100). | IVF |
Index creation example
In the following sections, we will demonstrate how to create and use vector indexes by inserting the following documents with vector embeddings into a collection:
db.runCommand({
insert: "books",
documents: [
{
_id: "pride_prejudice_1813",
title: "Pride and Prejudice",
author: "Jane Austen",
summary:
"The novel follows the story of Elizabeth Bennet, a spirited young woman navigating love, " +
"societal expectations, and family drama in 19th-century England.",
vector: [
0.014391838572919369, -0.07001544535160065, 0.03249300271272659, 0.017455201596021652, -0.012363946065306664,
0.04970458894968033, 0.05334962531924248, -0.04171367362141609, -0.042840130627155304, 0.038735587149858475,
-0.036975011229515076, 0.02225673384964466,
],
},
{
_id: "moby_dick_1851",
title: "Moby Dick",
author: "Herman Melville",
summary:
"The narrative follows Ishmael and his voyage aboard the whaling ship Pequod, commanded by Captain Ahab, " +
"who is obsessed with hunting the elusive white whale, Moby Dick.",
vector: [
-0.0016038859030231833, 0.08863562345504761, 0.006037247832864523, 0.044850509613752365, -0.019985735416412354,
-0.017665650695562363, 0.07435955852270126, 0.0025448515079915524, -0.08427142351865768, 0.07445722818374634,
-0.02302693948149681, -0.0778273269534111,
],
},
{
_id: "frankenstein_1818",
title: "Frankenstein",
author: "Mary Shelley",
summary:
"Victor Frankenstein, driven by an unquenchable thirst for knowledge, creates a living being, " +
"only to face tragic consequences as his creation turns monstrous.",
vector: [
-0.010190412402153015, 0.049356549978256226, -0.012309172190725803, 0.10420369356870651, 0.010599562898278236,
0.057357728481292725, 0.02385704033076763, 0.04186723381280899, 0.003379989881068468, 0.02957085147500038,
-0.08477196842432022, -0.0017921233084052801,
],
},
],
});
The vector
field represents the vector embeddings generated for the summary
field using the SentenceTransformer("all-MiniLM-L6-v2") model.
You can use any embedding model to generate the vectors.
- HNSW
- IVF
To create an HNSW index, set the kind
field to vector-hnsw
when creating the vector index.
db.runCommand({
createIndexes: "books",
indexes: [
{
name: "vector_hnsw_index",
key: {
vector: "cosmosSearch",
},
cosmosSearchOptions: {
kind: "vector-hnsw",
similarity: "COS",
dimensions: 12,
m: 16,
efConstruction: 64,
},
},
],
});
To create an IVF index, set the kind
field to vector-ivf
when creating the vector index.
db.runCommand({
createIndexes: "books",
indexes: [
{
name: "vector_ivf_index",
key: {
vector: "cosmosSearch",
},
cosmosSearchOptions: {
kind: "vector-ivf",
similarity: "COS",
dimensions: 12,
numLists: 3,
},
},
],
});
Performing a vector search
Once an index is created, you can perform vector searches using the $search
stage in the aggregation pipeline:
db.runCommand({
aggregate: "collectionName",
pipeline: [
{
$search: {
cosmosSearch: {
vector: "<vector>",
path: "<path>",
k: "<k>",
// HNSW only
efSearch: "<efSearch>",
},
},
},
],
cursor: {},
});
A typical vector search query includes the query vector, the path to the field containing the vector, and the number of neighbors to return, as described below:
Field | Type | Description | Applicable Index |
---|---|---|---|
cosmosSearch | document | An operator that specifies query parameters (vector , path , k , efSearch , etc.) for the vector search. | All |
vector | array | The vector to search for. | All |
path | string | The path to the field containing the vector. | All |
k | integer | The number of neighbors (results) to return. | All |
efSearch | integer | The number of neighbors or dynamic candidate list to search (default: 40). Must range from 1 to 1000 | HSNW |
Vector search example
- HNSW
- IVF
Using the same embedding model used for generating the vectors, a query vector is generated for An exciting tale of adventure and exploration at sea
.
Next, a vector search is performed on the vector
field in the books
collection to find the two nearest neighbors of the query vector.
db.runCommand({
aggregate: "books",
pipeline: [
{
$search: {
cosmosSearch: {
vector: [
0.02232860028743744, 0.06849973648786545, 0.030828291550278664, 0.0903232991695404, -0.028270352631807327,
-0.036311957985162735, 0.02430308423936367, -0.051550041884183884, -0.06737732142210007,
0.011019553989171982, -0.013402754440903664, -0.004793450236320496,
],
path: "vector",
k: 2,
efSearch: 40,
},
},
},
],
cursor: {},
});
The query returns the two nearest neighbors of the query vector.
[
{
"_id": "moby_dick_1851",
"title": "Moby Dick",
"author": "Herman Melville",
"summary": "The narrative follows Ishmael and his voyage aboard the whaling ship Pequod, commanded by Captain Ahab, who is obsessed with hunting the elusive white whale, Moby Dick.",
"vector": [
-0.0016038859030231833, 0.08863562345504761, 0.006037247832864523, 0.044850509613752365, -0.019985735416412354,
-0.017665650695562363, 0.07435955852270126, 0.0025448515079915524, -0.08427142351865768, 0.07445722818374634,
-0.02302693948149681, -0.0778273269534111
]
},
{
"_id": "frankenstein_1818",
"title": "Frankenstein",
"author": "Mary Shelley",
"summary": "Victor Frankenstein, driven by an unquenchable thirst for knowledge, creates a living being, only to face tragic consequences as his creation turns monstrous.",
"vector": [
-0.010190412402153015, 0.049356549978256226, -0.012309172190725803, 0.10420369356870651, 0.010599562898278236,
0.057357728481292725, 0.02385704033076763, 0.04186723381280899, 0.003379989881068468, 0.02957085147500038,
-0.08477196842432022, -0.0017921233084052801
]
}
]
Using the embeddings model used for generating the vectors, a query vector is generated for "A science fiction tale exploring the boundaries of human knowledge"
.
The following aggregation query performs a vector search on the vector
field in the books
collection:
db.runCommand({
aggregate: "books",
pipeline: [
{
$search: {
cosmosSearch: {
vector: [
0.030855651944875717, 0.038530610501766205, 0.000790110498201102, 0.06512122601270676, 0.009281659498810768,
-0.05678277835249901, 0.029056841507554054, 0.0216375719755888, 0.012258200906217098, 0.055315714329481125,
-0.009759286418557167, 0.06137007102370262,
],
path: "vector",
k: 2,
},
returnStoredSource: true,
},
},
],
cursor: {},
});
By setting returnStoredSource: true
, the query returns the similarity score along with matching documents.
Otherwise, only the similarity score is returned, and the actual document content is excluded.
The query returns the two nearest neighbors of the query vector.
[
{
"_id": "frankenstein_1818",
"title": "Frankenstein",
"author": "Mary Shelley",
"summary": "Victor Frankenstein, driven by an unquenchable thirst for knowledge, creates a living being, only to face tragic consequences as his creation turns monstrous.",
"vector": [
-0.010190412402153015, 0.049356549978256226, -0.012309172190725803, 0.10420369356870651, 0.010599562898278236,
0.057357728481292725, 0.02385704033076763, 0.04186723381280899, 0.003379989881068468, 0.02957085147500038,
-0.08477196842432022, -0.0017921233084052801
]
},
{
"_id": "moby_dick_1851",
"title": "Moby Dick",
"author": "Herman Melville",
"summary": "The narrative follows Ishmael and his voyage aboard the whaling ship Pequod, commanded by Captain Ahab, who is obsessed with hunting the elusive white whale, Moby Dick.",
"vector": [
-0.0016038859030231833, 0.08863562345504761, 0.006037247832864523, 0.044850509613752365, -0.019985735416412354,
-0.017665650695562363, 0.07435955852270126, 0.0025448515079915524, -0.08427142351865768, 0.07445722818374634,
-0.02302693948149681, -0.0778273269534111
]
}
]