# 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:

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 atleast 256 dimensions to produce good results.

Let's now index a document with a vector.

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

Last Updated: 4/4/2023, 5:25:48 AM