Bring your own dense vector embeddings to Elasticsearch
editBring your own dense vector embeddings to Elasticsearch
editThis tutorial demonstrates how to index documents that already have dense vector embeddings into Elasticsearch.
You’ll also learn the syntax for searching these documents using a knn
query.
You’ll find links at the end of this tutorial for more information about deploying a text embedding model in Elasticsearch, so you can generate embeddings for queries on the fly.
This is an advanced use case. Refer to Semantic search for an overview of your options for semantic search with Elasticsearch.
Step 1: Create an index with dense_vector
mapping
editEach document in our simple dataset will have:
-
A review: stored in a
review_text
field -
An embedding of that review: stored in a
review_vector
field-
The
review_vector
field is defined as adense_vector
data type.
-
The
The dense_vector
type automatically uses int8_hnsw
quantization by default to reduce the memory footprint required when searching float vectors.
Learn more about balancing performance and accuracy in Dense vector quantization.
resp = client.indices.create( index="amazon-reviews", mappings={ "properties": { "review_vector": { "type": "dense_vector", "dims": 8, "index": True, "similarity": "cosine" }, "review_text": { "type": "text" } } }, ) print(resp)
const response = await client.indices.create({ index: "amazon-reviews", mappings: { properties: { review_vector: { type: "dense_vector", dims: 8, index: true, similarity: "cosine", }, review_text: { type: "text", }, }, }, }); console.log(response);
PUT /amazon-reviews { "mappings": { "properties": { "review_vector": { "type": "dense_vector", "dims": 8, "index": true, "similarity": "cosine" }, "review_text": { "type": "text" } } } }
The |
|
The |
|
The |
Step 2: Index documents with embeddings
editIndex a single document
editFirst, index a single document to understand the document structure.
resp = client.index( index="amazon-reviews", id="1", document={ "review_text": "This product is lifechanging! I'm telling all my friends about it.", "review_vector": [ 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8 ] }, ) print(resp)
const response = await client.index({ index: "amazon-reviews", id: 1, document: { review_text: "This product is lifechanging! I'm telling all my friends about it.", review_vector: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], }, }); console.log(response);
PUT /amazon-reviews/_doc/1 { "review_text": "This product is lifechanging! I'm telling all my friends about it.", "review_vector": [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8] }
Bulk index multiple documents
editIn a production scenario, you’ll want to index many documents at once using the _bulk
endpoint.
Here’s an example of indexing multiple documents in a single _bulk
request.
resp = client.bulk( operations=[ { "index": { "_index": "amazon-reviews", "_id": "2" } }, { "review_text": "This product is amazing! I love it.", "review_vector": [ 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8 ] }, { "index": { "_index": "amazon-reviews", "_id": "3" } }, { "review_text": "This product is terrible. I hate it.", "review_vector": [ 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1 ] }, { "index": { "_index": "amazon-reviews", "_id": "4" } }, { "review_text": "This product is great. I can do anything with it.", "review_vector": [ 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8 ] }, { "index": { "_index": "amazon-reviews", "_id": "5" } }, { "review_text": "This product has ruined my life and the lives of my family and friends.", "review_vector": [ 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1 ] } ], ) print(resp)
const response = await client.bulk({ operations: [ { index: { _index: "amazon-reviews", _id: "2", }, }, { review_text: "This product is amazing! I love it.", review_vector: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], }, { index: { _index: "amazon-reviews", _id: "3", }, }, { review_text: "This product is terrible. I hate it.", review_vector: [0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1], }, { index: { _index: "amazon-reviews", _id: "4", }, }, { review_text: "This product is great. I can do anything with it.", review_vector: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], }, { index: { _index: "amazon-reviews", _id: "5", }, }, { review_text: "This product has ruined my life and the lives of my family and friends.", review_vector: [0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1], }, ], }); console.log(response);
POST /_bulk { "index": { "_index": "amazon-reviews", "_id": "2" } } { "review_text": "This product is amazing! I love it.", "review_vector": [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8] } { "index": { "_index": "amazon-reviews", "_id": "3" } } { "review_text": "This product is terrible. I hate it.", "review_vector": [0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1] } { "index": { "_index": "amazon-reviews", "_id": "4" } } { "review_text": "This product is great. I can do anything with it.", "review_vector": [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8] } { "index": { "_index": "amazon-reviews", "_id": "5" } } { "review_text": "This product has ruined my life and the lives of my family and friends.", "review_vector": [0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1] }
Step 3: Search documents with embeddings
editNow you can query these document vectors using a knn
retriever.
knn
is a type of vector search, which finds the k
most similar documents to a query vector.
Here we’re simply using a raw vector for the query text, for demonstration purposes.
resp = client.search( index="amazon-reviews", retriever={ "knn": { "field": "review_vector", "query_vector": [ 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8 ], "k": 2, "num_candidates": 5 } }, ) print(resp)
const response = await client.search({ index: "amazon-reviews", retriever: { knn: { field: "review_vector", query_vector: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], k: 2, num_candidates: 5, }, }, }); console.log(response);
POST /amazon-reviews/_search { "retriever": { "knn": { "field": "review_vector", "query_vector": [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], "k": 2, "num_candidates": 5 } } }
In this simple example, we’re sending a raw vector as the query text. In a real-world scenario, you’ll need to generate vectors for queries using an embedding model. |
|
The |
|
The |
Learn more
editIn this simple example, we’re sending a raw vector for the query text. In a real-world scenario you won’t know the query text ahead of time. You’ll need to generate query vectors, on the fly, using the same embedding model that generated the document vectors.
For this you’ll need to deploy a text embedding model in Elasticsearch and use the query_vector_builder
parameter. Alternatively, you can generate vectors client-side and send them directly with the search request.
Learn how to use a deployed text embedding model for semantic search.
If you’re just getting started with vector search in Elasticsearch, refer to Semantic search.