Vector Search using a Static Index
-
This article explains how to perform a vector search using a static index.
Prior to this article, it is recommended to get familiar with the Vector search using a dynamic query article. -
A static index allows you to define a vector index-field, enabling you to execute vector searches while leveraging the advantages of RavenDB's indexes.
-
The vector search feature is only supported by indexes that use the Corax search engine.
-
In this article:
Indexing a vector field - Overview
Defining a vector field in a static index
To define a vector index-field in your static-index definition:
-
From the Client API:
When indexing pre-made text-embeddings generated by RavenDB's Embeddings generation tasks,
use theLoadVector()
method in your index definition.
An example is available in Indexing pre-made text-embeddings.When indexing your own data (textual or numerical) that was not generated by these tasks,
use theCreateVector()
method in your index definition.
An example is available in Indexing raw text. -
From the Studio:
See Define a vector field in the Studio.
The source data types that can be used for vector search are detailed in Data types for vector search.
Parameters defined at index definition
The following params can be defined for the vector index-field in the index definition:
Source embedding type -
RavenDB supports performing vector search on TEXTUAL values or NUMERICAL arrays.
This param specifies the embedding format of the source data to be indexed.
Options include Text
, Single
, Int8
, or Binary
.
Destination embedding type -
Specify the quantization format for the embeddings that will be generated.
Read more about quantization in Quantization options.
Dimensions -
For numerical input only - define the size of the array from your source document.
-
If this param is Not provided -
the size will be determined by the first document indexed and will apply to all subsequent documents. -
Ensure the dimensionality of these numerical arrays (i.e., their length) is consistent across all source documents for the indexed field. An index error will occur if a source document has a different dimension for the indexed field.
Number of edges -
Specify the number of edges that will be created for a vector during indexing.
If not specified, the default value is taken from the following configuration key: Indexing.Corax.VectorSearch.DefaultNumberOfEdges.
Number of candidates for indexing time -
The number of candidates (potential neighboring vectors) that RavenDB evaluates during vector indexing.
If not specified, the default value is taken from the following configuration key: Indexing.Corax.VectorSearch.DefaultNumberOfCandidatesForIndexing.
(Note, this param differs from the number of candidates for query time).
Behavior during indexing
-
Raw textual input:
When indexing raw textual input from your documents, RavenDB generates embedding vectors using the built-in
bge-micro-v2 sentence-transformer model, which are then indexed. -
Pre-made text-embeddings input:
When indexing embeddings that are pre-generated from your documents' raw text by RavenDB's
Embeddings generation tasks, RavenDB indexes them without additional transformation, unless quantization is applied. -
Raw numerical input:
When indexing pre-made numerical arrays that are already in vector format but were Not generated by these tasks,
such as numerical arrays you created externally, RavenDB indexes them without additional transformation,
unless quantization is applied.
The embeddings are indexed on the server using the HNSW algorithm.
This algorithm organizes embeddings into a high-dimensional graph structure,
enabling efficient retrieval of Approximate Nearest Neighbors (ANN) during queries.
Parameters used at query time
Minimum similarity -
You can specify the minimum similarity to use when searching for related vectors. Can be a value between 0.0f
and 1.0f
.
A value closer to 1.0f
requires higher similarity between vectors, while a value closer to 0.0f
allows for less similarity.
If not specified, the default value is taken from the following configuration key: Indexing.Corax.VectorSearch.DefaultMinimumSimilarity.
Number of candidates at query time -
You can specify the maximum number of vectors that RavenDB will return from a graph search.
The number of the resulting documents that correspond to these vectors may be:
-
lower than the number of candidates - when multiple vectors originated from the same document.
-
higher than the number of candidates - when the same vector is shared between multiple documents.
If not specified, the default value is taken from the following configuration key: Indexing.Corax.VectorSearch.DefaultNumberOfCandidatesForQuerying.
Search method -
You can specify the search method at query time:
-
Approximate Nearest-Neighbor search (Default):
Search for related vectors in an approximate manner, providing faster results. -
Exact search:
Perform a thorough scan of the vectors to find the actual closest vectors,
offering better accuracy but at a higher computational cost.
To ensure consistent comparisons -
the search term is transformed into an embedding vector using the same method as the vector index-field.
Search results -
The server will search for the most similar vectors in the indexed vector space, taking into account all the parameters described.
The documents that correspond to the resulting vectors are then returned to the client.
By default, the resulting documents will be ordered by their score.
You can modify this behavior using the Indexing.Corax.VectorSearch.OrderByScoreAutomatically configuration key.
In addition, you can apply any of the 'order by' methods to your query, as explained in sort query results.
Vector behavior when documents are deleted
-
RavenDB's implementation of the HNSW graph is append-only.
-
When all documents associated with a specific vector are deleted, the vector itself is Not physically removed but is soft-deleted. This means the vector is marked as deleted and will no longer appear in query results.
Currently, compaction is not supported.
Indexing vector data - TEXT
Indexing raw text
The index in this example indexes data from raw text.
For an index that indexes pre-made text-embeddings see this example below.
This index defines a vector field named VectorfromText
.
It indexes embeddings generated from the raw textual data in the Name
field of all Product documents.
public class Products_ByVector_Text :
AbstractIndexCreationTask<Product, Products_ByVector_Text.IndexEntry>
{
public class IndexEntry()
{
// This index-field will hold the embeddings that will be generated
// from the TEXT in the documents
public object VectorFromText { get; set; }
}
public Products_ByVector_Text()
{
Map = products => from product in products
select new IndexEntry
{
// Call 'CreateVector' to create a VECTOR FIELD.
// Pass the document field containing the text
// from which the embeddings will be generated.
VectorFromText = CreateVector(product.Name)
};
// Customize the vector field options:
VectorIndexes.Add(x => x.VectorFromText,
new VectorOptions()
{
// Define the source embedding type
SourceEmbeddingType = VectorEmbeddingType.Text,
// Define the quantization for the destination embedding
DestinationEmbeddingType = VectorEmbeddingType.Single,
// Optionally, set the number of edges
NumberOfEdges = 20,
// Optionally, set the number of candidates
NumberOfCandidatesForIndexing = 20
});
// The index MUST use the Corax search engine
SearchEngineType = Raven.Client.Documents.Indexes.SearchEngineType.Corax;
}
}
public class Products_ByVector_Text_JS : AbstractJavaScriptIndexCreationTask
{
public Products_ByVector_Text_JS()
{
Maps = new HashSet<string>()
{
$@"map('Products', function (product) {{
return {{
VectorFromText: createVector(product.Name)
}};
}})"
};
Fields = new();
Fields.Add("VectorFromText", new IndexFieldOptions()
{
Vector = new VectorOptions()
{
SourceEmbeddingType = VectorEmbeddingType.Text,
DestinationEmbeddingType = VectorEmbeddingType.Single,
NumberOfEdges = 20,
NumberOfCandidatesForIndexing = 20
}
});
SearchEngineType = Raven.Client.Documents.Indexes.SearchEngineType.Corax;
}
}
var indexDefinition = new IndexDefinition
{
Name = "Products/ByVector/Text",
Maps = new HashSet<string>
{
@"
from product in docs.Products
select new
{
VectorFromText = CreateVector(product.Name)
}"
},
Fields = new Dictionary<string, IndexFieldOptions>()
{
{
"VectorFromText",
new IndexFieldOptions()
{
Vector = new VectorOptions()
{
SourceEmbeddingType = VectorEmbeddingType.Text,
DestinationEmbeddingType = VectorEmbeddingType.Single,
NumberOfEdges = 20,
NumberOfCandidatesForIndexing = 20
}
}
}
},
Configuration = new IndexConfiguration()
{
["Indexing.Static.SearchEngineType"] = "Corax"
}
};
store.Maintenance.Send(new PutIndexesOperation(indexDefinition));
Execute a vector search using the index:
Results will include Product documents where the Name
field is similar to the search term "italian food"
.
var similarProducts = session
.Query<Products_ByVector_Text.IndexEntry, Products_ByVector_Text>()
// Perform a vector search
// Call the 'VectorSearch' method
.VectorSearch(
field => field
// Call 'WithField'
// Specify the index-field in which to search for similar values
.WithField(x => x.VectorFromText),
searchTerm => searchTerm
// Call 'ByText'
// Provide the term for the similarity comparison
.ByText("italian food"),
// Optionally, specify the minimum similarity value
minimumSimilarity: 0.82f,
// Optionally, specify the number candidates for querying
numberOfCandidates: 20,
// Optionally, specify whether the vector search should use the 'exact search method'
isExact: true)
// Waiting for not-stale results is not mandatory
// but will assure results are not stale
.Customize(x => x.WaitForNonStaleResults())
.OfType<Product>()
.ToList();
var similarProducts = await asyncSession
.Query<Products_ByVector_Text.IndexEntry, Products_ByVector_Text>()
.VectorSearch(
field => field
.WithField(x => x.VectorFromText),
searchTerm => searchTerm
.ByText("italian food"), 0.82f, 20, isExact: true)
.Customize(x => x.WaitForNonStaleResults())
.OfType<Product>()
.ToListAsync();
var similarProducts = session.Advanced
.DocumentQuery<Products_ByVector_Text.IndexEntry, Products_ByVector_Text>()
.VectorSearch(
field => field
.WithField(x => x.VectorFromText),
searchTerm => searchTerm
.ByText("italian food"), 0.82f, 20, isExact: true)
.WaitForNonStaleResults()
.OfType<Product>()
.ToList();
var similarProducts = await asyncSession.Advanced
.AsyncDocumentQuery<Products_ByVector_Text.IndexEntry, Products_ByVector_Text>()
.VectorSearch(
field => field
.WithField(x => x.VectorFromText),
searchTerm => searchTerm
.ByText("italian food"),
0.82f, 20, isExact: true)
.WaitForNonStaleResults()
.OfType<Product>()
.ToListAsync();
var similarProducts = session.Advanced
.RawQuery<Product>(@"
from index 'Products/ByVector/Text'
// Optionally, wrap the 'vector.search' query with 'exact()' to perform an exact search
where exact(vector.search(VectorFromText, $searchTerm, 0.82, 20))")
.AddParameter("searchTerm", "italian food")
.WaitForNonStaleResults()
.ToList();
var similarProducts = await asyncSession.Advanced
.AsyncRawQuery<Product>(@"
from index 'Products/ByVector/Text'
// Optionally, wrap the 'vector.search' query with 'exact()' to perform an exact search
where exact(vector.search(VectorFromText, $searchTerm, 0.82, 20))")
.AddParameter("searchTerm", "italian food")
.WaitForNonStaleResults()
.ToListAsync();
from index "Products/ByVector/Text"
// Optionally, wrap the 'vector.search' query with 'exact()' to perform an exact search
where exact(vector.search(VectorFromText, $searchTerm, 0.82, 20))
{ "searchTerm" : "italian food" }
Indexing pre-made text-embeddings
The index in this example defines a vector field named VectorfromTextEmbeddings
.
It indexes pre-made embeddings that were generated by this
embedding generation task.
public class Categories_ByPreMadeTextEmbeddings :
AbstractIndexCreationTask<Category, Categories_ByPreMadeTextEmbeddings.IndexEntry>
{
public class IndexEntry()
{
// This index-field will hold the text embeddings
// that were pre-made by the Embeddings Generation Task
public object VectorFromTextEmbeddings { get; set; }
}
public Categories_ByPreMadeTextEmbeddings()
{
Map = categories => from category in categories
select new IndexEntry
{
// Call 'LoadVector' to create a VECTOR FIELD. Pass:
// * The document field name to be indexed (as a string)
// * The identifier of the task that generated the embeddings
// for the 'Name' field
VectorFromTextEmbeddings = LoadVector("Name", "id-for-task-open-ai")
};
VectorIndexes.Add(x => x.VectorFromTextEmbeddings,
new VectorOptions()
{
// Vector options can be customized
// in the same way as the above index example.
});
// The index MUST use the Corax search engine
SearchEngineType = Raven.Client.Documents.Indexes.SearchEngineType.Corax;
}
}
public class Categories_ByPreMadeTextEmbeddings_JS : AbstractJavaScriptIndexCreationTask
{
public Categories_ByPreMadeTextEmbeddings_JS()
{
Maps = new HashSet<string>()
{
$@"map('Categories', function (category) {{
return {{
VectorFromTextEmbeddings:
loadVector('Name', 'id-for-task-open-ai')
}};
}})"
};
Fields = new();
Fields.Add("VectorFromTextEmbeddings", new IndexFieldOptions()
{
Vector = new VectorOptions()
{
// Vector options can be customized
// in the same way as the above index example.
}
});
SearchEngineType = Raven.Client.Documents.Indexes.SearchEngineType.Corax;
}
}
var indexDefinition = new IndexDefinition
{
Name = "Categories/ByPreMadeTextEmbeddings",
Maps = new HashSet<string>
{
@"
from category in docs.Categories
select new
{
VectorFromTextEmbeddings = LoadVector(""Name"", ""id-for-task-open-ai"")
}"
},
Fields = new Dictionary<string, IndexFieldOptions>()
{
{
"VectorFromTextEmbeddings",
new IndexFieldOptions()
{
Vector = new VectorOptions()
{
// Vector options can be customized
// in the same way as the above index example.
}
}
}
},
Configuration = new IndexConfiguration()
{
["Indexing.Static.SearchEngineType"] = "Corax"
}
};
store.Maintenance.Send(new PutIndexesOperation(indexDefinition));
Execute a vector search using the index:
Results will include Category documents where the Name
field is similar to the search term "candy"
.
var similarCategories = session
.Query<Categories_ByPreMadeTextEmbeddings.IndexEntry, Categories_ByPreMadeTextEmbeddings>()
// Perform a vector search
// Call the 'VectorSearch' method
.VectorSearch(
field => field
// Call 'WithField'
// Specify the index-field in which to search for similar values
.WithField(x => x.VectorFromTextEmbeddings),
searchTerm => searchTerm
// Call 'ByText'
// Provide the search term for the similarity comparison
.ByText("candy"),
// Optionally, specify the minimum similarity value
minimumSimilarity: 0.75f,
// Optionally, specify the number of candidates for querying
numberOfCandidates: 20,
// Optionally, specify whether the vector search should use the 'exact search method'
isExact: true)
// Waiting for not-stale results is not mandatory
// but will assure results are not stale
.Customize(x => x.WaitForNonStaleResults())
.OfType<Category>()
.ToList();
var similarCategories = await asyncSession
.Query<Categories_ByPreMadeTextEmbeddings.IndexEntry, Categories_ByPreMadeTextEmbeddings>()
.VectorSearch(
field => field
.WithField(x => x.VectorFromTextEmbeddings),
searchTerm => searchTerm
.ByText("candy"), 0.75f, 20, isExact: true)
.Customize(x => x.WaitForNonStaleResults())
.OfType<Category>()
.ToListAsync();
var similarCategories = session.Advanced
.DocumentQuery<Categories_ByPreMadeTextEmbeddings.IndexEntry, Categories_ByPreMadeTextEmbeddings>()
.VectorSearch(
field => field
.WithField(x => x.VectorFromTextEmbeddings),
searchTerm => searchTerm
.ByText("candy"), 0.75f, 20, isExact: true)
.WaitForNonStaleResults()
.OfType<Category>()
.ToList();
var similarCategories = await asyncSession.Advanced
.AsyncDocumentQuery<Categories_ByPreMadeTextEmbeddings.IndexEntry, Categories_ByPreMadeTextEmbeddings>()
.VectorSearch(
field => field
.WithField(x => x.VectorFromTextEmbeddings),
searchTerm => searchTerm
.ByText("candy"),
0.75f, 20, isExact: true)
.WaitForNonStaleResults()
.OfType<Category>()
.ToListAsync();
var similarCategories = session.Advanced
.RawQuery<Category>(@"
from index 'Categories/ByPreMadeTextEmbeddings'
// Optionally, wrap the 'vector.search' query with 'exact()' to perform an exact search
where exact(vector.search(VectorFromTextEmbeddings, $searchTerm, 0.75, 20))")
.AddParameter("searchTerm", "candy")
.WaitForNonStaleResults()
.ToList();
var similarCategories = await asyncSession.Advanced
.AsyncRawQuery<Category>(@"
from index 'Categories/ByPreMadeTextEmbeddings'
// Optionally, wrap the 'vector.search' query with 'exact()' to perform an exact search
where exact(vector.search(VectorFromTextEmbeddings, $searchTerm, 0.75, 20))")
.AddParameter("searchTerm", "candy")
.WaitForNonStaleResults()
.ToListAsync();
from index "Categories/ByPreMadeTextEmbeddings"
// Optionally, wrap the 'vector.search' query with 'exact()' to perform an exact search
where exact(vector.search(VectorFromTextEmbeddings, $p0, 0.75, 20))
{ "p0": "candy" }
Indexing vector data - NUMERICAL
The examples in this section use the sample data provided in the dynamic query article.
The following index defines a vector field named VectorFromSingle
.
It indexes embeddings generated from the numerical data in the TagsEmbeddedAsSingle
field of all Movie documents.
The raw numerical data in the source documents is in 32-bit floating-point format.
public class Movies_ByVector_Single :
AbstractIndexCreationTask<Movie, Movies_ByVector_Single.IndexEntry>
{
public class IndexEntry()
{
// This index-field will hold the embeddings that will be generated
// from the NUMERICAL content in the documents.
public object VectorFromSingle { get; set; }
}
public Movies_ByVector_Single()
{
Map = movies => from movie in movies
select new IndexEntry
{
// Call 'CreateVector' to create a VECTOR FIELD.
// Pass the document field containing the array (32-bit floating-point values)
// from which the embeddings will be generated.
VectorFromSingle = CreateVector(movie.TagsEmbeddedAsSingle)
};
// Customize the vector field options:
VectorIndexes.Add(x => x.VectorFromSingle,
new VectorOptions()
{
// Define the source embedding type
SourceEmbeddingType = VectorEmbeddingType.Single,
// Define the quantization for the destination embedding
DestinationEmbeddingType = VectorEmbeddingType.Single,
// It is recommended to configure the number of dimensions
// which is the size of the arrays that will be indexed.
Dimensions = 2,
// Optionally, set the number of edges and candidates
NumberOfEdges = 20,
NumberOfCandidatesForIndexing = 20
});
// The index MUST use the Corax search engine
SearchEngineType = Raven.Client.Documents.Indexes.SearchEngineType.Corax;
}
}
public class Movies_ByVector_Single_JS : AbstractJavaScriptIndexCreationTask
{
public Movies_ByVector_Single_JS()
{
Maps = new HashSet<string>()
{
$@"map('Movies', function (movie) {{
return {{
VectorFromSingle: createVector(movie.TagsEmbeddedAsSingle)
}};
}})"
};
Fields = new();
Fields.Add("VectorFromSingle", new IndexFieldOptions()
{
Vector = new VectorOptions()
{
SourceEmbeddingType = VectorEmbeddingType.Single,
DestinationEmbeddingType = VectorEmbeddingType.Single,
Dimensions = 2,
NumberOfEdges = 20,
NumberOfCandidatesForIndexing = 20
}
});
SearchEngineType = Raven.Client.Documents.Indexes.SearchEngineType.Corax;
}
}
var indexDefinition = new IndexDefinition
{
Name = "Movies/ByVector/Single",
Maps = new HashSet<string>
{
@"
from movie in docs.Movies
select new
{
VectorFromSingle = CreateVector(movie.TagsEmbeddedAsSingle)
}"
},
Fields = new Dictionary<string, IndexFieldOptions>()
{
{
"VectorFromSingle",
new IndexFieldOptions()
{
Vector = new VectorOptions()
{
SourceEmbeddingType = VectorEmbeddingType.Single,
DestinationEmbeddingType = VectorEmbeddingType.Single,
Dimensions = 2,
NumberOfEdges = 20,
NumberOfCandidatesForIndexing = 20
}
}
}
},
Configuration = new IndexConfiguration()
{
["Indexing.Static.SearchEngineType"] = "Corax"
}
};
store.Maintenance.Send(new PutIndexesOperation(indexDefinition));
Execute a vector search using the index:
var similarMovies = session
.Query<Movies_ByVector_Single.IndexEntry, Movies_ByVector_Single>()
// Perform a vector search
// Call the 'VectorSearch' method
.VectorSearch(
field => field
// Call 'WithField'
// Specify the index-field in which to search for similar values
.WithField(x => x.VectorFromSingle),
queryVector => queryVector
// Call 'ByEmbedding'
// Provide the vector for the similarity comparison
.ByEmbedding(
new RavenVector<float>(new float[] { 6.599999904632568f, 7.699999809265137f })))
.Customize(x => x.WaitForNonStaleResults())
.OfType<Movie>()
.ToList();
var similarMovies = await asyncSession
.Query<Movies_ByVector_Single.IndexEntry, Movies_ByVector_Single>()
.VectorSearch(
field => field
.WithField(x => x.VectorFromSingle),
queryVector => queryVector
.ByEmbedding(
new RavenVector<float>(new float[] { 6.599999904632568f, 7.699999809265137f })))
.Customize(x => x.WaitForNonStaleResults())
.OfType<Movie>()
.ToListAsync();
var similarMovies = session.Advanced
.DocumentQuery<Movies_ByVector_Single.IndexEntry, Movies_ByVector_Single>()
.VectorSearch(
field => field
.WithField(x => x.VectorFromSingle),
queryVector => queryVector
.ByEmbedding(
new RavenVector<float>(new float[] { 6.599999904632568f, 7.699999809265137f })))
.WaitForNonStaleResults()
.OfType<Movie>()
.ToList();
var similarMovies = await asyncSession.Advanced
.AsyncDocumentQuery<Movies_ByVector_Single.IndexEntry, Movies_ByVector_Single>()
.VectorSearch(
field => field
.WithField(x => x.VectorFromSingle),
queryVector => queryVector
.ByEmbedding(
new RavenVector<float>(new float[] { 6.599999904632568f, 7.699999809265137f })))
.WaitForNonStaleResults()
.OfType<Movie>()
.ToListAsync();
var similarMovies = session.Advanced
.RawQuery<Movie>(@"
from index 'Movies/ByVector/Single'
where vector.search(VectorFromSingle, $queryVector)")
.AddParameter("queryVector", new RavenVector<float>(new float[]
{
6.599999904632568f, 7.699999809265137f
}))
.WaitForNonStaleResults()
.ToList();
var similarMovies = await asyncSession.Advanced
.AsyncRawQuery<Movie>(@"
from index 'Movies/ByVector/Single'
where vector.search(VectorFromSingle, $queryVector)")
.AddParameter("queryVector", new RavenVector<float>(new float[]
{
6.599999904632568f, 7.699999809265137f
}))
.WaitForNonStaleResults()
.ToListAsync();
from index "Movies/ByVector/Single"
where vector.search(VectorFromSingle, $queryVector)
{ "queryVector" : { "@vector" : [6.599999904632568, 7.699999809265137] }}
The following index defines a vector field named VectorFromInt8Arrays
.
It indexes embeddings generated from the numerical arrays in the TagsEmbeddedAsInt8
field of all Movie documents.
The raw numerical data in the source documents is in Int8 (8-bit integers) format.
public class Movies_ByVector_Int8 :
AbstractIndexCreationTask<Movie, Movies_ByVector_Int8.IndexEntry>
{
public class IndexEntry()
{
// This index-field will hold the embeddings that will be generated
// from the NUMERICAL content in the documents.
public object VectorFromInt8Arrays { get; set; }
}
public Movies_ByVector_Int8()
{
Map = movies => from movie in movies
select new IndexEntry
{
// Call 'CreateVector' to create a VECTOR FIELD.
// Pass the document field containing the arrays (8-bit integer values)
// from which the embeddings will be generated.
VectorFromInt8Arrays = CreateVector(movie.TagsEmbeddedAsInt8)
};
// Customize the vector field options:
VectorIndexes.Add(x => x.VectorFromInt8Arrays,
new VectorOptions()
{
// Define the source embedding type
SourceEmbeddingType = VectorEmbeddingType.Int8,
// Define the quantization for the destination embedding
DestinationEmbeddingType = VectorEmbeddingType.Int8,
// It is recommended to configure the number of dimensions
// which is the size of the arrays that will be indexed.
Dimensions = 2,
// Optionally, set the number of edges and candidates
NumberOfEdges = 20,
NumberOfCandidatesForIndexing = 20
});
// The index MUST use the Corax search engine
SearchEngineType = Raven.Client.Documents.Indexes.SearchEngineType.Corax;
}
}
public class Movies_ByVector_Int8_JS : AbstractJavaScriptIndexCreationTask
{
public Movies_ByVector_Int8_JS()
{
Maps = new HashSet<string>()
{
$@"map('Movies', function (movie) {{
return {{
VectorFromInt8Arrays: createVector(movie.TagsEmbeddedAsInt8)
}};
}})"
};
Fields = new();
Fields.Add("VectorFromInt8Arrays", new IndexFieldOptions()
{
Vector = new VectorOptions()
{
SourceEmbeddingType = VectorEmbeddingType.Int8,
DestinationEmbeddingType = VectorEmbeddingType.Int8,
Dimensions = 2,
NumberOfEdges = 20,
NumberOfCandidatesForIndexing = 20
}
});
SearchEngineType = Raven.Client.Documents.Indexes.SearchEngineType.Corax;
}
}
var indexDefinition = new IndexDefinition
{
Name = "Movies/ByVector/Int8",
Maps = new HashSet<string>
{
@"
from movie in docs.Movies
select new
{
VectorFromInt8Arrays = CreateVector(movie.TagsEmbeddedAsInt8)
}"
},
Fields = new Dictionary<string, IndexFieldOptions>()
{
{
"VectorFromInt8Arrays",
new IndexFieldOptions()
{
Vector = new VectorOptions()
{
SourceEmbeddingType = VectorEmbeddingType.Int8,
DestinationEmbeddingType = VectorEmbeddingType.Int8,
Dimensions = 2,
NumberOfEdges = 20,
NumberOfCandidatesForIndexing = 20
}
}
}
},
Configuration = new IndexConfiguration()
{
["Indexing.Static.SearchEngineType"] = "Corax"
}
};
store.Maintenance.Send(new PutIndexesOperation(indexDefinition));
Execute a vector search using the index:
var similarMovies = session
.Query<Movies_ByVector_Int8.IndexEntry, Movies_ByVector_Int8>()
// Perform a vector search
// Call the 'VectorSearch' method
.VectorSearch(
field => field
// Call 'WithField'
// Specify the index-field in which to search for similar values
.WithField(x => x.VectorFromInt8Arrays),
queryVector => queryVector
// Call 'ByEmbedding'
// Provide the vector for the similarity comparison
// (Note: provide a single vector)
.ByEmbedding(
// The provided vector MUST be in the same format as was stored in your document
// Call 'VectorQuantizer.ToInt8' to transform the rawData to the Int8 format
VectorQuantizer.ToInt8(new float[] { 0.1f, 0.2f })))
.Customize(x => x.WaitForNonStaleResults())
.OfType<Movie>()
.ToList();
var similarMovies = await asyncSession
.Query<Movies_ByVector_Int8.IndexEntry, Movies_ByVector_Int8>()
.VectorSearch(
field => field
.WithField(x => x.VectorFromInt8Arrays),
queryVector => queryVector
.ByEmbedding(
VectorQuantizer.ToInt8(new float[] { 0.1f, 0.2f })))
.Customize(x => x.WaitForNonStaleResults())
.OfType<Movie>()
.ToListAsync();
var similarMovies = session.Advanced
.DocumentQuery<Movies_ByVector_Int8.IndexEntry, Movies_ByVector_Int8>()
.VectorSearch(
field => field
.WithField(x => x.VectorFromInt8Arrays),
queryVector => queryVector
.ByEmbedding(
VectorQuantizer.ToInt8(new float[] { 0.1f, 0.2f })))
.WaitForNonStaleResults()
.OfType<Movie>()
.ToList();
var similarMovies = await asyncSession.Advanced
.AsyncDocumentQuery<Movies_ByVector_Int8.IndexEntry, Movies_ByVector_Int8>()
.VectorSearch(
field => field
.WithField(x => x.VectorFromInt8Arrays),
queryVector => queryVector
.ByEmbedding(
VectorQuantizer.ToInt8(new float[] { 0.1f, 0.2f })))
.WaitForNonStaleResults()
.OfType<Movie>()
.ToListAsync();
var similarMovies = session.Advanced
.RawQuery<Movie>(@"
from index 'Movies/ByVector/Int8'
where vector.search(VectorFromInt8Arrays, $queryVector)")
.AddParameter("queryVector", VectorQuantizer.ToInt8(new float[] { 0.1f, 0.2f }))
.WaitForNonStaleResults()
.ToList();
var similarMovies = await asyncSession.Advanced
.AsyncRawQuery<Movie>(@"
from index 'Movies/ByVector/Int8'
where vector.search(VectorFromInt8Arrays, $queryVector)")
.AddParameter("queryVector", VectorQuantizer.ToInt8(new float[] { 0.1f, 0.2f }))
.WaitForNonStaleResults()
.ToListAsync();
from index "Movies/ByVector/Int8"
where vector.search(VectorFromInt8Arrays, $queryVector)
{ "queryVector" : [64, 127, -51, -52, 76, 62] }
Indexing multiple field types
An index can define multiple types of index-fields. In this example, the index includes:
A 'regular' field, a 'vector' field, and a field configured for full-text search.
This allows you to query across all fields using various predicates.
public class Products_ByMultipleFields :
AbstractIndexCreationTask<Product, Products_ByMultipleFields.IndexEntry>
{
public class IndexEntry()
{
// An index-field for 'regular' data
public decimal PricePerUnit { get; set; }
// An index-field for 'full-text' search
public string Name { get; set; }
// An index-field for 'vector' search
public object VectorFromText { get; set; }
}
public Products_ByMultipleFields()
{
Map = products => from product in products
select new IndexEntry
{
PricePerUnit = product.PricePerUnit,
Name = product.Name,
VectorFromText = CreateVector(product.Name)
};
// Configure the index-field 'Name' for FTS:
Index(x => x.Name, FieldIndexing.Search);
// Note:
// Default values will be used for the VECTOR FIELD if not customized here.
// The index MUST use the Corax search engine
SearchEngineType = Raven.Client.Documents.Indexes.SearchEngineType.Corax;
}
}
public class Products_ByMultipleFields_JS : AbstractJavaScriptIndexCreationTask
{
public Products_ByMultipleFields_JS()
{
Maps = new HashSet<string>()
{
$@"map('Products', function (product) {{
return {{
PricePerUnit: product.PricePerUnit,
Name: product.Name,
VectorFromText: createVector(product.Name)
}};
}})"
};
Fields = new();
Fields.Add("Name", new IndexFieldOptions()
{
Indexing = FieldIndexing.Search
});
SearchEngineType = Raven.Client.Documents.Indexes.SearchEngineType.Corax;
}
}
var indexDefinition = new IndexDefinition
{
Name = "Products/ByMultipleFields",
Maps = new HashSet<string>
{
@"
from product in docs.Products
select new
{
PricePerUnit = product.PricePerUnit,
Name = product.Name,
VectorFromText = CreateVector(product.Name)
}"
},
Fields = new Dictionary<string, IndexFieldOptions>()
{
{
"Name",
new IndexFieldOptions()
{
Indexing = FieldIndexing.Search
}
}
},
Configuration = new IndexConfiguration()
{
["Indexing.Static.SearchEngineType"] = "Corax"
}
};
store.Maintenance.Send(new PutIndexesOperation(indexDefinition));
Execute a query that combines predicates across all index-field types:
var results = session.Advanced
.DocumentQuery<Products_ByMultipleFields.IndexEntry, Products_ByMultipleFields>()
// Perform a regular search
.WhereGreaterThan(x => x.PricePerUnit, 200)
.OrElse()
// Perform a full-text search
.Search(x => x.Name, "Alice")
.OrElse()
// Perform a vector search
.VectorSearch(
field => field
.WithField(x => x.VectorFromText),
searchTerm => searchTerm
.ByText("italian food"),
minimumSimilarity: 0.8f)
.WaitForNonStaleResults()
.OfType<Product>()
.ToList();
var results = await asyncSession.Advanced
.AsyncDocumentQuery<Products_ByMultipleFields.IndexEntry, Products_ByMultipleFields>()
.WhereGreaterThan(x => x.PricePerUnit, 200)
.OrElse()
.Search(x => x.Name, "Alice")
.OrElse()
.VectorSearch(
field => field
.WithField(x => x.VectorFromText),
searchTerm => searchTerm
.ByText("italian food"),
minimumSimilarity: 0.8f)
.WaitForNonStaleResults()
.OfType<Product>()
.ToListAsync();
var results = session.Advanced
.RawQuery<Product>(@"
from index 'Products/ByMultipleFields'
where PricePerUnit > $minPrice
or search(Name, $searchTerm1)
or vector.search(VectorFromText, $searchTerm2, 0.8)")
.AddParameter("minPrice", 200)
.AddParameter("searchTerm1", "Alice")
.AddParameter("searchTerm2", "italian")
.WaitForNonStaleResults()
.ToList();
var results = await asyncSession.Advanced
.AsyncRawQuery<Product>(@"
from index 'Products/ByMultipleFields'
where PricePerUnit > $minPrice
or search(Name, $searchTerm1)
or vector.search(VectorFromText, $searchTerm2, 0.8)")
.AddParameter("minPrice", 200)
.AddParameter("searchTerm1", "Alice")
.AddParameter("searchTerm2", "italian")
.WaitForNonStaleResults()
.ToListAsync();
from index "Products/ByMultipleFields"
where PricePerUnit > $minPrice
or search(Name, $searchTerm1)
or vector.search(VectorFromText, $searchTerm2, 0.8)
{ "minPrice" : 200, "searchTerm1" : "Alice", "searchTerm2": "italian" }
Configure the vector field in the Studio

Add vector field

Customize vector field
- Vector field name
Enter the name of the vector field to customize. - Configure Vector Field
Click this button to customize the field. - Dimensions
For numerical input only - define the size of the array from your source document. - Edges
The number of edges that will be created for a vector during indexing. - Source embedding type
The format of the source embeddings (Text, Single, Int8, or Binary). - Candidates for indexing
The number of candidates (potential neighboring vectors) that RavenDB evaluates during vector indexing. - Destination embedding type
The quantization format for the embeddings that will be generated (Text, Single, Int8, or Binary).