
New in 7.0: RavenDB’s Vector Search!
We’re happy to announce that starting with version v7.0, RavenDB now supports vector indexing and querying, enabling you to utilize AI capabilities to enhance your search experience. In this article, we’ll guide you through what you can achieve by using this feature and how to set it up quickly.
Table of contents
Introduction
Vector search is a method for searching related vectors (in a defined meter) to the vector provided in the query. The idea of distance between vectors is that similar vectors are close to each other, aggregated in clusters.
For example, vectors generated for the words “tea”, “coffee”, and “beverage” would be relatively close to each other since tea and coffee are beverages.
In this article, we will cover vector search used for semantic search. However, vector search can be used in various ways like recommendation systems, pattern recommendation and many more[1].
A Glimpse of a Use Case: What I Can Achieve with It.
For just the beginning, let’s look at a small example. One of the most significant vector search usages is semantic search. It allows the user to find semantically related text in your documents.
In our sample database, we have a collection of documents called Products. It contains the names of the products. Until now, to make a field “searchable”, we’d need to use a full-text search feature. It won’t cover synonyms, just text similarity. Take a look:

Knowing that there are chocolate products in our database, we will find them if we search for the ‘choc’ prefix.
However, let’s imagine our user wants to search for any candies. Directly searching for the word candies will return no documents. However, with vector search, we can find them:

If this interests you, we will discuss all the details in the following chapters of this article.
Indexing
Under the hood, RavenDB uses a HNSW algorithm[2] to store vectors in the index. It is an approximate nearest neighbor search technique for searching vectors that are relatively close to the searched vector. Advantages of this method include the speed of search since it is optimized to give approximate results instead of going through all elements in the index, which can hold millions of elements.
To mark a field to be processed as a vector, you need to use the CreateVector method, which can look like this:
from doc in docs.Products
select new
{
Vector = CreateVector(doc.Name)
}
Or accordingly in JavaScript:
map('Products', function (e) {
return {
Vector: createVector(e.Name)
};
})
By default, no more configuration is required.
The parameter passed into CreateVector may be either:
- Numerical array – we will index the array as a single vector
- Textual values – we will automatically generate the embedding of the input
Let’s talk more about textual values (b). A nice addition to the VectorSearch feature is the auto-generation of embeddings on your instance without the need for any external tools.
Internally, we’re using the bge-micro-v2[3] model, which is sufficient for certain use cases.
We’ve shared multiple configuration options to match user needs. Let’s take a look at them.
SourceEmbeddingType and DestinationEmbeddingType
SourceEmbeddingType is used to define the underlying data type stored in the document. This setting accepts the following values:
- VectorEmbeddingType.Single – when your field contains array(s) of floats
- VectorEmbeddingType.Text – when field contains textual value(s). This will force embedding generation during the indexing via the model described above.
- VectorEmbeddingType.Int8 – when your field contains array(s) of signed bytes as result of quantized singles
- VectorEmbeddingType.Binary – when your field contains array(s) of bytes as result of quantized singles
RavenDB supports indexing the quantized[4] values by methods provided in Clients. Example how to create quantized values can be found in our documentation. The main reason that you may want to store quantized values is saving storage space. You can gain:
- Int8 quantization: it’s saving 75% storage space compared to single.
- Binary quantization: it is saving around 96% storage space compared to singles
However; please take in mind that quantization is losing precision.
DestinationEmbeddingType defines the indexing output form. You can store non-quantized vectors in the document, but to save space in the index itself, you can configure that indexed values will be quantized.
Dimensions
The number of dimensions can be configured at indexing time to ensure consistency across all document vectors. If not provided, the first encountered vector will determine the dimension size of embeddings.
HNSW – Number of edges
The HNSW algorithm is graph-based. All the vectors inside it have connections to their neighbors. In our conventions, the number of neighbor links is called NumberOfEdges
.
A larger number of edges increases the searchability (accuracy) of the graph search; however, it also means that indexing and querying will be slower – during traversal, we will check more vectors.
HNSW – Number of candidates
We’re also providing the ability to configure a number of candidates (both querying and indexing), but let’s focus on the indexing part. When a new vector is added to the graph, we have to find its neighbors to store them in the graph and create links between them. The NumberOfCandidatesForIndexing defines how many vectors we need to consider when finding the best neighbors for our new vector. A larger number will increase indexing time; however, it should provide a better neighbor list (see NumberOfEdges description above), which can improve the search quality.
Querying
We introduced new methods in RQL to extend its ability to handle vector searches.
The basic method looks like this:
from index MyIndex where vector.search(FieldName, QueriedValue, [OPTIONAL: float] MinimumMatch, [OPTIONAL: int] NumberOfCandidates)
where:
FieldName: is the name of the vector field in the index
QueriedValue: the value we're looking for where:
a) Textual values - can be direct string or parameter
b) Numerical values -> the vector must be passed as a parameter, there is no direct support for passing vector (as array) in the RQL
MinimumMatch: minimum similarity score that indicates if the vector is included to the result set, value between 0 and 1.0.
NumberOfCadidates: max amount of vectors that we will consider to return from graph search
For example:
Textual:
from index MyIndex where vector.search(VectorFromText, "name")
Numerical:
$p0 = [0.1, 0.2]
from index MyIndex where vector.search(VectorFromArray, $p0)
Default values for optional parameters:
MinimumMatch: 0.75
NumberOfCandidates: 16
Default values can be changed via the configuration ([docs link]). If we do not want to override the default value, we can safely pass a `null` value instead. This indicates to the query builder that the default value should be used.
AutoIndexes
VectorSearch is also available via AutoIndexes search. To use it, the user simply needs to define the data source in the `FieldName` of the query. Additionally, the user must specify the source embedding type in the query, as it is not possible to pass the configuration the way it is done with static indexes.
To do so, we have introduced new methods in the RQL with the `embedding`[5] prefixes.
It is easier to explain it in examples, so let’s dive deep into the syntax.
For AutoIndexes, the underlying values are considered numerical singles when the field name is not wrapped in any methods.
$p0 = [0.1, 0.2]
from Products where vector.search(FieldName, $p0)
However, when we want to create a vector field from textual value, we simply need to wrap it in a method:
from Products where vector.search(embedding.text(Name), "candy")
We also support the quantization feature, which can be specified directly within the embedding method.
from Products where vector.search(embedding.text_i8(Name), "Name")
As you can see, the embedding method follows a specific pattern. The pattern works as follows:
– Only datasource: for defining only the data source, use `embedding.DataSource`, such as `embedding.text`, `embedding.i8`, or `embedding.i1`
– Apply quantization: to define quantization, use the pattern `embedding.DataSource_DataDestination`.
Supported methods:
- embedding.f32_i8
- embedding.f32_i1
- embedding.i8
- embedding.i1
- embedding.text
- embedding.text_i8
- embedding.text_i1
Example of use case
Nothing is better than an example of how we can use the feature. I want to show how easily you can use semantic search with our sample data.
To create sample data, follow this instruction. We are using the Nordwind database as a sample database containing a collection called Products. Those documents include the product names – they look like a good candidate for creating a semantic search with those values.
In the index view, in the New index view, let’s define the vector field:

We may want to configure the vector field explicitly:

First, click the “Add field” button, then “Enable Vector Search” on the right side of the widget.
Let’s set source embedding as “Text” (since we’ve only textual values in documents) and output as Single (we do not want to perform quantization yet)

Click save, and we’re ready to go after the index is built. So let’s play with the queries. We can look for similar things by name:

But the hidden power behind the vector search in this case is the semantic search, so let’s run another query:

We searched for Italian, which can be Italian food or Italian products. Our embedder generated vectors of items that seem to be close to the word Italian if we increase the minimumSimilarity to be more exact:
The embedding generated on the CPU produces excellent results using a simpler model. You may want to find embedders from other providers to find better, more accurate vectors.
The same results can also be achieved by using the AutoIndexes feature without any other configuration and static index:

Summary
In this article, we discussed the vector.search feature, with explanations of the options we are sharing. We go through all possibilities vector.search and basic examples of how semantic search could be achieved without needing external tools within a single query.
Bibliography
[1] https://en.wikipedia.org/wiki/Nearest_neighbor_search
[2] https://en.wikipedia.org/wiki/Hierarchical_navigable_small_world
[3] https://huggingface.co/TaylorAI/bge-micro-v2
[4] https://en.wikipedia.org/wiki/Quantization_(signal_processing)
[5] For semantic search: https://en.wikipedia.org/wiki/Word_embedding
Woah, already finished? 🤯
If you found the article interesting, don’t miss a chance to try our database solution – totally for free!