# Vector Search
Typesense supports the ability to add embeddings generated by your Machine Learning models to each document, and then doing a nearest-neighbor search on them. This lets you build features like similarity search, recommendations, semantic search, visual search, etc.
# What is an embedding?
An embedding for a JSON document is just an array of floating point numbers (eg: [0.4422, 0.49292, 0.1245, ...]
), that is an alternate numeric representation of the document.
These embeddings are generated by Machine Learning models in such a way that documents that are "similar" to each other (for different definitions of similarity depending on the model used), have embeddings that are "closer" to each other (cosine similarity).
Here are some common models you can use to generate these document embeddings: Sentence-BERT, CLIP, OpenAI's Embeddings model, word2vec, etc.
Once you've generated these embeddings, you can import them into Typesense into a special vector field and then do a nearest neighbor search, giving another set of vectors or a document ID as the input, and get the documents that are closest (cosine similarity) to your input.
Here's an example practical application of vector search - a "Find Similar" feature in an ecommerce store: ecommerce-store.typesense.org (opens new window). (Click on Find Similar
below each product).
Here are two articles that go into more depth about embeddings:
- What Are Word and Sentence Embeddings? (opens new window)
- Getting Started With Embeddings (opens new window)
Let's now discuss how to do index and search embeddings in Typesense.
# Creating a collection with a vector field
We'll assume that you've already generated your embeddings using a machine learning model. If not, here's (opens new window) a quick example of how to use the Sentence-BERT model to generate embeddings.
Once your document embeddings are ready, you want to create a collection that contains a float[]
field
with a num_dim
property for indexing them. The num_dim
property specifies the number of
dimensions (length of the float array) that your embeddings contain.
Let's create a collection called docs
with a vector field called vec
that contains just 4 dimensions.
TIP
We create a vector with 4 dimensions to keep the code snippets readable. Depending on what model you use, real world use will require creating vector fields with at least 256 dimensions to produce good results.
Let's now index a document with a vector.
# Nearest neighbor vector search
We can now search for documents that contain a vec
field value "closest" to a given query vector.
We use the k
parameter to control the number of documents that are returned.
TIP
Since vector search queries tend to be large because of the large dimension of the query vector, we are using the multi_search end-point that sends the search parameters as a POST request body.
Every matching hit in the response will contain a vector_distance
field that indicates how "close" the document's
vector value is to the query vector. Typesense uses the cosine similarity, so this distance will be a value between
0
and 2
.
- If the document's vector perfectly matches the query vector, the distance will be
0
- If the document's vector is extremely different from the query vector, then the distance will be
2
.
The hits are automatically sorted in ascending order of the vector_distance
, i.e. best matching
documents appear first.
Sample Response
# Querying for similar documents
If you have a particular document id
and want to find documents that are "similar" to this document, you can
do a vector query that references this id
directly.
curl 'http://localhost:8108/multi_search?collection=docs' -X POST -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
--data-raw '{"searches":[{"q":"*", "vector_query": "vec:([], id: foobar)" }]}'
By specifying an empty query vector []
and passing an id
parameter, this query
would return all documents whose vec
value is closest to the foobar
document's vec
value.
TIP
The foobar
document itself will not be returned in the results.
# Brute-force searching
By default, Typesense uses the built-in HNSW index to do approximate nearest neighbor vector searches. This scales
well for large datasets. However, if you wish to bypass the HNSW index and do a flat / brute-force ranking of
vectors, you can do that via the flat_search_cutoff
parameter.
For example, if you wish to do brute-force vector search when a given query matches fewer than 20 documents, sending
flat_search_cutoff=20
will bypass the HNSW index when the number of results found is less than 20.
Here's an example where we are filtering on the category
field and asking the vector search to use direct
flat searching if the number of results produced by the filtering operation is less than 20 results.
curl 'http://localhost:8108/multi_search?collection=docs' -X POST -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
--data-raw '{"searches":[{"q":"*", "filter_by": "category:shoes", "vector_query": "vec:([0.96826, 0.94, 0.39557, 0.306488], k:100, flat_search_cutoff: 20)" }]}'
# UI Examples
Here's a demo that shows you how to implement a "Find Similar" feature using Vector Search in an ecommerce store: https://ecommerce-store.typesense.org/ (opens new window).
Click on "Find Similar" below each product tile for notes on how to implement this.
Here's a demo that shows you how to implement Semantic Search, using an embeddings API and Vector Search: https://github.com/typesense/typesense-instantsearch-semantic-search-demo (opens new window)