docs-site/content/0.25.0/api/vector-search.md
Typesense has the ability to index embeddings generated by any machine learning model, and then do a nearest-neighbor (KNN) search on this data.
[[toc]]
Here are some example use-cases you can build, using vector search as the foundation:
You can also combine any of the above with features like filtering, faceting, sorting, grouping, etc to build a user-friendly search experience.
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:
You can import embeddings generated by these models 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.
You can also have Typesense generate these embeddings for you, using OpenAI, PaLM API or one of the built-in ML models listed here.
Here is one (of many possible) practical applications of vector search - a "Find Similar" feature in an ecommerce store: ecommerce-store.typesense.org. (Click on Find Similar below each product).
Here are two articles that talk about embeddings in more detail:
Let's now discuss how to do index and search embeddings in Typesense.
If you have already generated embeddings using your own models outside Typesense, you can import them into Typesense.
:::tip Here's a quick example of how to use the Sentence-BERT model to generate embeddings outside Typesense. :::
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're creating a vector with 4 dimensions in the examples 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. :::
<Tabs :tabs="['JavaScript','PHP','Python','Ruby', 'Dart','Java','Shell']"> <template v-slot:JavaScript>let schema = {
'name': 'docs',
'fields': [
{
'name': 'title',
'type': 'string'
},
{
'name': 'points',
'type': 'int32'
},
{
'name': 'vec',
'type': 'float[]',
'num_dim': 4
}
],
'default_sorting_field': 'points'
}
client.collections().create(schema)
$schema = [
'name' => 'docs',
'fields' => [
[
'name' => 'title',
'type' => 'string'
],
[
'name' => 'points',
'type' => 'int32'
],
[
'name' => 'vec',
'type' => 'float[]',
'num_dim' => 4
]
],
'default_sorting_field' => 'points'
];
$client->collections->create($schema);
schema = {
'name': 'docs',
'fields': [
{
'name' : 'title',
'type' : 'string'
},
{
'name' : 'points',
'type' : 'int32'
},
{
'name' : 'vec',
'type' : 'float[]',
'num_dim' : 4
}
],
'default_sorting_field': 'points'
}
client.collections.create(schema)
schema = {
'name' => 'places',
'fields' => [
{
'name' => 'title',
'type' => 'string'
},
{
'name' => 'points',
'type' => 'int32'
},
{
'name' => 'vec',
'type' => 'float[]',
'num_dim' => 4
}
],
'default_sorting_field' => 'points'
}
client.collections.create(schema)
final schema = Schema(
'docs',
{
Field('title', type: Type.string),
Field('points', type: Type.int32),
Field('vec', type: Type.float, isMultivalued: true, dimensions: 4),
},
defaultSortingField: Field('points', type: Type.int32),
);
await client.collections.create(schema);
CollectionSchema collectionSchema = new CollectionSchema();
collectionschema.name("docs")
.addFieldsItem(new Field().name("title").type("string"))
.addFieldsItem(new Field().name("points").type("int32"))
.addFieldsItem(new Field().name("vec").type("float[]").num_dim(4))
.defaultSortingField("points");
CollectionResponse collectionResponse = client.collections().create(collectionSchema);
curl "http://localhost:8108/collections" \
-X POST \
-H "Content-Type: application/json" \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"name": "docs",
"fields": [
{"name": "title", "type": "string" },
{"name": "points", "type": "int32" },
{"name": "vec", "type": "float[]", "num_dim": 4}
],
"default_sorting_field": "points"
}'
Let's now index a document with a vector.
<Tabs :tabs="['JavaScript','PHP','Python','Ruby', 'Dart','Java','Shell']"> <template v-slot:JavaScript>let document = {
'title': 'Louvre Museuem',
'points': 1,
'vec': [0.04, 0.234, 0.113, 0.001]
}
client.collections('docs').documents().create(document)
$document = [
'title' => 'Louvre Museuem',
'points' => 1,
'vec' => array(0.04, 0.234, 0.113, 0.001)
];
$client->collections['docs']->documents->create($document);
document = {
'title': 'Louvre Museuem',
'points': 1,
'vec': [0.04, 0.234, 0.113, 0.001]
}
client.collections['docs'].documents.create(document)
document = {
'title' => 'Louvre Museuem',
'points' => 1,
'vec' => [0.04, 0.234, 0.113, 0.001]
}
client.collections['docs'].documents.create(document)
final document = {
'title': 'Louvre Museuem',
'points': 1,
'vec': [0.04, 0.234, 0.113, 0.001]
};
await client.collection('docs').documents.create(document);
HaashMap<String, Object> document = new HashMap<>();
float[] vec = {0.04, 0.234, 0.113, 0.001}
document.add("title", "Louvre Museuem");
document.add("points", 1);
document.add("vec", vec);
client.collection("docs").documents.create(document);
curl "http://localhost:8108/collections/docs/documents" -X POST \
-H "Content-Type: application/json" \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{"points":1,"title":"Louvre Museuem", "vec": [0.04, 0.234, 0.113, 0.001]}'
To simplify the process of embedding generation, Typesense can automatically use your JSON data and either OpenAI API, PaLM API or any of the built-in embedding models listed here to generate & store embeddings.
When you do a search query on this automatically-generated vector field, your search query will be vectorized using the same model used for the field, which then allows you to do semantic search or combine keyword and semantic search to do hybrid search.
To create a field that automatically embeds other string or string array fields, you need to set the embed property of the field.
Here's an example:
<Tabs :tabs="['JavaScript','PHP','Python', 'Ruby', 'Java','Shell']"> <template v-slot:JavaScript>let schema = {
"name": "products",
"fields": [
{
"name": "product_name",
"type": "string"
},
{
"name": "categories",
"type": "string[]"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": [
"product_name",
"categories"
],
"model_config": {
"model_name": "ts/e5-small"
}
}
}
]
};
client.collections('products').create(schema);
$schema = [
"name" => "products",
"fields" => [
[
"name" => "product_name",
"type" => "string"
],
[
"name" => "categories",
"type" => "string[]"
],
[
"name" => "embedding",
"type" => "float[]",
"embed" => [
"from" => [
"product_name",
"categories"
],
"model_config" => [
"model_name" => "ts/e5-small"
]
]
]
]
];
$client->collections->create($schema);
schema = {
"name": "products",
"fields": [
{
"name" : "product_name",
"type" : "string"
},
{
"name" : "categories",
"type" : "string[]"
},
{
"name" : "embedding",
"type" : "float[]",
"embed": {
"from": [
"product_name",
"categories"
],
"model_config": {
"model_name": "ts/e5-small"
}
}
}
]
}
client.collections.create(schema)
schema = {
"name" => "products",
"fields" => [
{
"name" => "product_name",
"type" => "string"
},
{
"name" => "categories",
"type" => "string[]"
},
{
"name" => "embedding",
"type" => "float[]",
"embed" => {
"from" => [
"product_name",
"categories"
],
"model_config" => {
"model_name" => "ts/e5-small"
}
}
}
]
}
client.collections.create(schema)
CollectionSchema collectionSchema = new CollectionSchema();
ArrayList<String> embedFrom = new ArrayList<>();
embedFrom.add("product_name");
embedFrom.add("categories");
collectionschema.name("products")
.addFieldsItem(new Field().name("product_name").type(FieldTypes.STRING))
.addFieldsItem(new Field().name("categories").type(FieldTypes.STRING_ARRAY))
.addFieldsItem(new Field().name("embedding").type(FieldTypes.FLOAT_ARRAY).embed(
new FieldEmbed().from(embedFrom).modelConfig(new FieldEmbedModelConfig().modelName("ts/e5-small"))
));
CollectionResponse collectionResponse = client.collections().create(collectionSchema);
curl 'http://localhost:8108/collections' \
-X POST \
-H 'Content-Type: application/json' \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"name": "products",
"fields": [
{
"name": "product_name",
"type": "string"
},
{
"name": "categories",
"type": "string[]"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": [
"product_name",
"categories"
],
"model_config": {
"model_name": "ts/e5-small"
}
}
}
]
}'
In this example the embedding vector field will be generated automatically while indexing a document, using the concatenated values of the product_name and categories fields (separated by spaces).
These models are officially supported by Typesense and stored in the Typesense Hugging Face repository here.
You can specify them by adding the ts namespace before the model name. Typesense will automatically download these models and make them available for use when you index documents after creating the collection.
let schema = {
"name": "products",
"fields": [
{
"name": "brand",
"type": "string"
},
{
"name": "categories",
"type": "string[]"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": [
"brand",
"categories"
],
"model_config": {
"model_name": "ts/all-MiniLM-L12-v2"
}
}
}
]
};
client.collections('products').create(schema);
$schema = [
"name" => "products",
"fields" => [
[
"name" => "brand",
"type" => "string"
],
[
"name" => "categories",
"type" => "string[]"
],
[
"name" => "embedding",
"type" => "float[]",
"embed" => [
"from" => [
"brand",
"categories"
],
"model_config" => [
"model_name" => "ts/all-MiniLM-L12-v2"
]
]
]
]
];
$client->collections->create($schema);
schema = {
"name": "products",
"fields": [
{
"name" : "brand",
"type" : "string"
},
{
"name" : "categories",
"type" : "string[]"
},
{
"name" : "embedding",
"type" : "float[]",
"embed": {
"from": [
"brand",
"categories"
],
"model_config": {
"model_name": "ts/all-MiniLM-L12-v2"
}
}
}
]
}
client.collections.create(schema)
schema = {
"name" => "products",
"fields" => [
{
"name" => "brand",
"type" => "string"
},
{
"name" => "categories",
"type" => "string[]"
},
{
"name" => "embedding",
"type" => "float[]",
"embed" => {
"from" => [
"brand",
"categories"
],
"model_config" => {
"model_name" => "ts/all-MiniLM-L12-v2"
}
}
}
]
}
client.collections.create(schema)
CollectionSchema collectionSchema = new CollectionSchema();
ArrayList<String> embedFrom = new ArrayList<>();
embedFrom.add("product_name");
embedFrom.add("categories");
collectionschema.name("products")
.addFieldsItem(new Field().name("product_name").type(FieldTypes.STRING))
.addFieldsItem(new Field().name("categories").type(FieldTypes.STRING_ARRAY))
.addFieldsItem(new Field().name("embedding").type(FieldTypes.FLOAT_ARRAY).embed(
new FieldEmbed().from(embedFrom).modelConfig(new FieldEmbedModelConfig().modelName("ts/all-MiniLM-L12-v2"))
));
CollectionResponse collectionResponse = client.collections().create(collectionSchema);
curl 'http://localhost:8108/collections' \
-X POST \
-H 'Content-Type: application/json' \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"name": "products",
"fields": [
{
"name": "brand",
"type": "string"
},
{
"name": "categories",
"type": "string[]"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": [
"brand",
"categories"
],
"model_config": {
"model_name": "ts/all-MiniLM-L12-v2"
}
}
}
]
}'
When you create a collection with the schema above, the all-MiniLM-L12-v2 model will be downloaded and your documents will be automatically embedded by this model and will be stored in the embedding field.
See our Hugging Face repo for all officially supported models. If you need support for additional publicly-available models, feel free to convert the model to ONNX format and send a PR to our Hugging Face models repo.
Embedding models are computationally intensive to run. So when using one of the built-in models, you might want to consider running Typesense on a server with a GPU to improve the performance of embedding generation, especially for large datasets.
For select RAM / CPU configurations, you'll find the option to turn on "GPU Acceleration" when provisioning a new cluster or under Cluster Configuration > Modify for Typesense versions 0.25.0 and above.
You would have to install the following additional dependencies, after which Typesense will automatically make use of any available Nvidia GPUs:
Install CUDA following the instructions on Nvidia's site here.
You want to specifically install the following versions of these packages: cuda=11.8.0-1, libcudnn8=8.9.2.26-1+cuda11.8 and libcudnn8-dev=8.9.2.26-1+cuda11.8 and their dependencies.
Install cuDNN following the instructions here.
You want to specifically install the libcudnn8 and libcudnn8-dev packages.
Add the following to /etc/profile.d/cuda-path.sh:
export PATH=/usr/local/cuda/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/cuda/lib64
export CUDA_HOME=/usr/local/cuda
Install the Typesense GPU dependencies package:
curl -O https://dl.typesense.org/releases/{{ $site.themeConfig.typesenseLatestVersion }}/typesense-gpu-deps-{{ $site.themeConfig.typesenseLatestVersion }}-arm64.deb sudo apt install ./typesense-gpu-deps-{{ $site.themeConfig.typesenseLatestVersion }}-arm64.deb </code></pre> </div> </template> </Tabs>
You can also have Typesense send specific fields in your JSON data to OpenAI's API to generate text embeddings.
You can use any of OpenAI models listed here.
<Tabs :tabs="['JavaScript','PHP','Python','Ruby','Java','Shell']"> <template v-slot:JavaScript>let schema = {
"name": "products",
"fields": [
{
"name": "product_name",
"type": "string"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": [
"product_name"
],
"model_config": {
"model_name": "openai/text-embedding-ada-002",
"api_key": "your_openai_api_key"
}
}
}
]
};
client.collections('products').create(schema);
$schema = [
"name" => "products",
"fields" => [
[
"name" => "product_name",
"type" => "string"
],
[
"name" => "embedding",
"type" => "float[]",
"embed" => [
"from" => [
"product_name"
],
"model_config" => [
"model_name" => "openai/text-embedding-ada-002",
"api_key" => "your_openai_api_key"
]
]
]
]
];
$client->collections->create($schema);
schema = {
"name": "products",
"fields": [
{
"name" : "product_name",
"type" : "string"
},
{
"name" : "embedding",
"type" : "float[]",
"embed": {
"from": [
"product_name"
],
"model_config": {
"model_name": "openai/text-embedding-ada-002",
"api_key": "your_openai_api_key"
}
}
}
]
}
client.collections.create(schema)
schema = {
"name" => "products",
"fields" => [
{
"name" => "product_name",
"type" => "string"
},
{
"name" => "embedding",
"type" => "float[]",
"embed" => {
"from" => [
"product_name"
],
"model_config" => {
"model_name" => "openai/text-embedding-ada-002",
"api_key" => "your_openai_api_key"
}
}
}
]
}
client.collections.create(schema)
CollectionSchema collectionSchema = new CollectionSchema();
ArrayList<String> embedFrom = new ArrayList<>();
embedFrom.add("product_name");
embedFrom.add("categories");
collectionschema.name("products")
.addFieldsItem(new Field().name("product_name").type(FieldTypes.STRING))
.addFieldsItem(new Field().name("categories").type(FieldTypes.STRING_ARRAY))
.addFieldsItem(new Field().name("embedding").type(FieldTypes.FLOAT_ARRAY).embed(
new FieldEmbed().from(embedFrom).modelConfig(new FieldEmbedModelConfig().modelName("openai/text-embedding-ada-002").apiKey("your_openai_api_key")
));
CollectionResponse collectionResponse = client.collections().create(collectionSchema);
curl 'http://localhost:8108/collections' \
-X POST \
-H 'Content-Type: application/json' \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"name": "products",
"fields": [
{
"name": "product_name",
"type": "string"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": [
"product_name"
],
"model_config": {
"model_name": "openai/text-embedding-ada-002",
"api_key": "your_openai_api_key"
}
}
}
]
}'
When you create the collection above, we will call the OpenAI API to create embeddings from the product_name field and store them in the embedding field every time you index a document.
You have to provide a valid OpenAI API key in model_config to use this feature.
This API provided by Google MakerSuite to generate embeddings.
<Tabs :tabs="['JavaScript','PHP','Python', 'Ruby', 'Java','Shell']"> <template v-slot:JavaScript>let schema = {
"name": "products",
"fields": [
{
"name": "product_name",
"type": "string"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": [
"product_name"
],
"model_config": {
"model_name": "google/embedding-gecko-001",
"api_key": "your_google_api_key"
}
}
}
]
};
client.collections('products').create(schema);
$schema = [
"name" => "products",
"fields" => [
[
"name" => "product_name",
"type" => "string"
],
[
"name" => "embedding",
"type" => "float[]",
"embed" => [
"from" => [
"product_name"
],
"model_config" => [
"model_name" => "google/embedding-gecko-001",
"api_key" => "your_google_api_key"
]
]
]
]
];
$client->collections->create($schema);
schema = {
"name": "products",
"fields": [
{
"name" : "product_name",
"type" : "string"
},
{
"name" : "embedding",
"type" : "float[]",
"embed": {
"from": [
"product_name"
],
"model_config": {
"model_name": "google/embedding-gecko-001",
"api_key": "your_google_api_key"
}
}
}
]
}
client.collections.create(schema)
schema = {
"name" => "products",
"fields" => [
{
"name" => "product_name",
"type" => "string"
},
{
"name" => "embedding",
"type" => "float[]",
"embed" => {
"from" => [
"product_name"
],
"model_config" => {
"model_name" => "google/embedding-gecko-001",
"api_key" => "your_google_api_key"
}
}
}
]
}
client.collections.create(schema)
CollectionSchema collectionSchema = new CollectionSchema();
ArrayList<String> embedFrom = new ArrayList<>();
embedFrom.add("product_name");
embedFrom.add("categories");
collectionschema.name("products")
.addFieldsItem(new Field().name("product_name").type(FieldTypes.STRING))
.addFieldsItem(new Field().name("categories").type(FieldTypes.STRING_ARRAY))
.addFieldsItem(new Field().name("embedding").type(FieldTypes.FLOAT_ARRAY).embed(
new FieldEmbed().from(embedFrom).modelConfig(new FieldEmbedModelConfig().modelName("google/embedding-gecko-001").apiKey("your_google_api_key")
));
CollectionResponse collectionResponse = client.collections().create(collectionSchema);
curl 'http://localhost:8108/collections' \
-X POST \
-H 'Content-Type: application/json' \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"name": "products",
"fields": [
{
"name": "product_name",
"type": "string"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": [
"product_name"
],
"model_config": {
"model_name": "google/embedding-gecko-001",
"api_key": "your_google_api_key"
}
}
}
]
}'
Note: The only supported model is embedding-gecko-001 for now.
This API also provided by Google under the Google Cloud Platform (GCP) umbrella.
You would need the following authentication information to use this method:
Please refer to the Vertex AI docs for more information on how to fetch these values.
<Tabs :tabs="['JavaScript','PHP','Python', 'Ruby', 'Java','Shell']"> <template v-slot:JavaScript>schema = {
"name": "products",
"fields": [
{
"name": "product_name",
"type": "string"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": [
"product_name"
],
"model_config": {
"model_name": "gcp/embedding-gecko-001",
"access_token": "your_gcp_access_token",
"refresh_token": "your_gcp_refresh_token",
"client_id": "your_gcp_app_client_id",
"client_secret": "your_gcp_client_secret",
"project_id": "your_gcp_project_id"
}
}
}
]
};
client.collections('products').create(schema);
$schema = [
"name" => "products",
"fields" => [
[
"name" => "product_name",
"type" => "string"
],
[
"name" => "embedding",
"type" => "float[]",
"embed" => [
"from" => [
"product_name"
],
"model_config" => [
"model_name" => "gcp/embedding-gecko-001",
"access_token" => "your_gcp_access_token",
"refresh_token" => "your_gcp_refresh_token",
"client_id" => "your_gcp_app_client_id",
"client_secret" => "your_gcp_client_secret",
"project_id" => "your_gcp_project_id"
]
}
]
]
];
$client->collections->create($schema);
schema = {
"name": "products",
"fields": [
{
"name" : "product_name",
"type" : "string"
},
{
"name" : "embedding",
"type" : "float[]",
"embed": {
"from": [
"product_name"
],
"model_config": {
"model_name": "gcp/embedding-gecko-001",
"access_token": "your_gcp_access_token",
"refresh_token": "your_gcp_refresh_token",
"client_id": "your_gcp_app_client_id",
"client_secret": "your_gcp_client_secret",
"project_id": "your_gcp_project_id"
}
}
}
]
}
client.collections.create(schema)
schema = {
"name" => "products",
"fields" => [
{
"name" => "product_name",
"type" => "string"
},
{
"name" => "embedding",
"type" => "float[]",
"embed" => {
"from" => [
"product_name"
],
"model_config" => {
"model_name" => "gcp/embedding-gecko-001",
"access_token" => "your_gcp_access_token",
"refresh_token" => "your_gcp_refresh_token",
"client_id" => "your_gcp_app_client_id",
"client_secret" => "your_gcp_client_secret",
"project_id" => "your_gcp_project_id"
}
}
}
]
}
client.collections.create(schema)
CollectionSchema collectionSchema = new CollectionSchema();
ArrayList<String> embedFrom = new ArrayList<>();
embedFrom.add("product_name");
embedFrom.add("categories");
collectionschema.name("products")
.addFieldsItem(new Field().name("product_name").type(FieldTypes.STRING))
.addFieldsItem(new Field().name("categories").type(FieldTypes.STRING_ARRAY))
.addFieldsItem(new Field().name("embedding").type(FieldTypes.FLOAT_ARRAY).embed(
new FieldEmbed().from(embedFrom).modelConfig(new FieldEmbedModelConfig().modelName("gcp/embedding-gecko-001")
.accessToken("your_gcp_access_token")
.refreshToken("your_gcp_refresh_token")
.clientId("your_gcp_app_client_id")
.clientSecret("your_gcp_client_secret").projectId("your_gcp_project_id")))
));
CollectionResponse collectionResponse = client.collections().create(collectionSchema);
curl 'http://localhost:8108/collections' \
-X POST \
-H 'Content-Type: application/json' \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"name": "products",
"fields": [
{
"name": "product_name",
"type": "string"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": [
"product_name"
],
"model_config": {
"model_name": "gcp/embedding-gecko-001",
"access_token": "your_gcp_access_token",
"refresh_token": "your_gcp_refresh_token",
"client_id": "your_gcp_app_client_id",
"client_secret": "your_gcp_client_secret",
"project_id": "your_gcp_project_id"
}
}
}
]
}'
You can use any of the following parameters to fine-tune how API calls are made to remote embedding services:
| Parameter | Description | Default |
|---|---|---|
remote_embedding_timeout_ms | How long to wait until an API call to a remote embedding service is considered a timeout, during a search | 30s |
remote_embedding_num_tries | The number of times to retry an API call to a remote embedding service on failure, during a search | 2 |
let search_parameters = {
'q' : 'chair',
'query_by' : 'embedding',
'prefix' : false,
'remote_embedding_timeout_ms': 5000,
'remote_embedding_num_tries' : 3
}
client.collections('products').documents().search(search_parameters)
$search_parameters = [
'q' => 'chair',
'query_by' => 'embedding',
'prefix' => false,
'remote_embedding_timeout_ms'=> 5000,
'remote_embedding_num_try' => 3
];
$client->collections['products']->documents->search($search_parameters);
search_parameters = {
'q' : 'chair',
'query_by' : 'embedding',
'prefix' : false,
'remote_embedding_timeout_ms': 5000,
'remote_embedding_num_try' : 3
}
client.collections['products'].documents.search(search_parameters)
search_parameters = {
'q' => 'chair',
'query_by' => 'embedding',
'prefix' => false,
'remote_embedding_timeout_ms'=> 5000,
'remote_embedding_num_try' => 3
}
client.collections['products'].documents.search(search_parameters)
SearchParameters searchParameters = new SearchParameters();
searchParameters.q("chair")
.queryBy("embedding")
.prefix(false)
.remoteEmbeddingTimeoutMs(5000)
.remoteEmbeddingNumTries(3);
SearchResult searchResult = client.collections("products").documents().search(searchParameters);
curl 'http://localhost:8108/multi_search' \
-X POST \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"searches": [
{
"collection": "products",
"q": "chair",
"query_by": "embedding",
"prefix": false,
"remote_embedding_timeout_ms": 5000,
"remote_embedding_num_try": 3
}
]
}'
| Parameter | Description | Default |
|---|---|---|
remote_embedding_batch_size | Max size of each batch that will be sent to remote APIs while importing multiple documents at once. Using lower amount will lower timeout risk, but increase number of requests made. | 200 |
let import_parameters = {
'remote_embedding_batch_size': 200
}
client.collections('products').documents().import(documents, import_parameters)
$import_parameters = [
'remote_embedding_batch_size' => 200
];
$client->collections['products']->documents->import($documents, $import_parameters);
import_parameters = {
'remote_embedding_batch_size': 200
}
client.collections['products'].documents.import(documents, import_parameters)
import_parameters = {
'remote_embedding_batch_size' => 200
}
client.collections['products'].documents.import(documents, import_parameters)
ImportDocumentParameters importDocumentParameters = new ImportDocumentParameters();
importDocumentParameters.remoteEmbeddingBatchSize(200);
client.collections("products").documents().import(documents, importDocumentParameters);
curl 'http://localhost:8108/collections/products/documents/import?remote_embedding_batch_size=200' \
-X POST \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{"product_name": "chair"}
{"product_name": "table"}'
You can also use your own models to generate embeddings from within Typesense. They must be in the ONNX file format.
Create a directory under <data_dir>/models and store your ONNX model file, vocab file, and a JSON for model config there.
Note: Your model file MUST be named as model.onnx and the config file MUST be named as config.json.
This file will contain information about the type of model you want to use.
The JSON file must contain model_type (type of the model; we support bert and xlm_roberta at the moment) and vocab_file_name keys.
Directory Structure:
<data_dir>/models/test_model/model.onnx
<data_dir>/models/test_model/vocab.txt
<data_dir>/models/test_model/config.json
Contents of config.json:
{
"model_type": "bert",
"vocab_file_name": "vocab.txt"
}
Create an embedding field using the directory name as model_name in model_config.
let schema = {
"name": "products",
"fields": [
{
"name": "product_name",
"type": "string"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": ["product_name"],
"model_config": {
"model_name": "test_model"
}
}
}
]
};
client.collections('products').create(schema);
$schema = [
"name" => "products",
"fields" => [
[
"name" => "product_name",
"type" => "string"
],
[
"name" => "embedding",
"type" => "float[]",
"embed" => [
"from" => ["product_name"],
"model_config" => [
"model_name" => "test_model"
]
]
]
]
];
$client->collections->create($schema);
schema = {
"name": "products",
"fields": [
{
"name" : "product_name",
"type" : "string"
},
{
"name" : "embedding",
"type" : "float[]",
"embed": {
"from": ["product_name"],
"model_config": {
"model_name": "test_model"
}
}
}
]
}
client.collections.create(schema)
schema = {
"name" => "products",
"fields" => [
{
"name" => "product_name",
"type" => "string"
},
{
"name" => "embedding",
"type" => "float[]",
"embed" => [
"from" => ["product_name"],
"model_config" => [
"model_name" => "test_model"
]
}
}
]
}
client.collections.create(schema)
CollectionSchema collectionSchema = new CollectionSchema();
ArrayList<String> embedFrom = new ArrayList<>();
embedFrom.add("product_name");
embedFrom.add("categories");
collectionschema.name("products")
.addFieldsItem(new Field().name("product_name").type(FieldTypes.STRING))
.addFieldsItem(new Field().name("categories").type(FieldTypes.STRING_ARRAY))
.addFieldsItem(new Field().name("embedding").type(FieldTypes.FLOAT_ARRAY).embed(
new FieldEmbed().from(embedFrom).modelConfig(new FieldEmbedModelConfig().modelName("test_model")
));
CollectionResponse collectionResponse = client.collections().create(collectionSchema);
curl 'http://localhost:8108/collections' \
-X POST \
-H 'Content-Type: application/json' \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"name": "products",
"fields": [
{
"name": "product_name",
"type": "string"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": ["product_name"],
"model_config": {
"model_name": "test_model"
}
}
}
]
}'
These are optional model parameters, which may be required to use with your custom models.
Some models may require a prefix to know if texts are queries or they are actual texts to query on (you can check intfloat/e5-small, for example).
If you set this property in model_config, the given indexing prefix will be added to the text that will be used to create embeddings when you index a document and query_prefix to the actual query before creating embeddings of it.Example:
let schema = {
"name": "products",
"fields": [
{
"name": "product_name",
"type": "string"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": ["product_name"],
"model_config": {
"model_name": "e5-base",
"indexing_prefix": "passage:",
"query_prefix": "query:"
}
}
}
]
};
client.collections('products').create(schema);
$schema = [
"name" => "products",
"fields" => [
[
"name" => "product_name",
"type" => "string"
],
[
"name" => "embedding",
"type" => "float[]",
"embed" => [
"from" => ["product_name"],
"model_config" => [
"model_name" => "e5-base",
"indexing_prefix" => "passage:",
"query_prefix" => "query:"
]
]
]
]
];
$client->collections->create($schema);
schema = {
"name": "products",
"fields": [
{
"name" : "product_name",
"type" : "string"
},
{
"name" : "embedding",
"type" : "float[]",
"embed": {
"from": ["product_name"],
"model_config": {
"model_name": "e5-base",
"indexing_prefix": "passage:",
"query_prefix": "query:"
}
}
}
]
}
client.collections.create(schema)
schema = {
"name" => "products",
"fields" => [
{
"name" => "product_name",
"type" => "string"
},
{
"name" => "embedding",
"type" => "float[]",
"embed" => [
"from" => ["product_name"],
"model_config" => [
"model_name" => "e5-base",
"indexing_prefix" => "passage:",
"query_prefix" => "query:"
]
}
]
}
client.collections.create(schema)
CollectionSchema collectionSchema = new CollectionSchema();
ArrayList<String> embedFrom = new ArrayList<>();
embedFrom.add("product_name");
embedFrom.add("categories");
collectionschema.name("products")
.addFieldsItem(new Field().name("product_name").type(FieldTypes.STRING))
.addFieldsItem(new Field().name("categories").type(FieldTypes.STRING_ARRAY))
.addFieldsItem(new Field().name("embedding").type(FieldTypes.FLOAT_ARRAY).embed(
new FieldEmbed().from(embedFrom).modelConfig(new FieldEmbedModelConfig().modelName("e5-base").indexingPrefix("passage:").queryPrefix("query:")
));
CollectionResponse collectionResponse = client.collections().create(collectionSchema);
curl 'http://localhost:8108/collections' \
-X POST \
-H 'Content-Type: application/json' \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"name": "products",
"fields": [
{
"name": "product_name",
"type": "string"
},
{
"name": "embedding",
"type": "float[]",
"embed": {
"from": ["product_name"],
"model_config": {
"model_name": "e5-base",
"indexing_prefix": "passage:",
"query_prefix": "query:"
}
}
}
]
}'
For this example, when you index a document:
{
"product_name": "ABCD"
}
The text used to generate embeddings for the embedding field will be passage: ABCD instead of ABCD. And when you query, if your query is EFGH, it will be embedded as query: EFGH instead of EFGH.
Once you've indexed your embeddings in a vector field, you can now search for documents that are "closest" to a given query vector.
To control the number of documents that are returned, you can either use the per_page pagination parameter or the k parameter within the vector query.
let searchRequests = {
'searches': [
{
'collection': 'docs',
'q': '*',
'vector_query' : 'vec:([0.96826, 0.94, 0.39557, 0.306488], k:100)'
}
]
}
let commonSearchParams = {}
client.multiSearch.perform(searchRequests, commonSearchParams)
$searchRequests = [
'searches' => [
[
'collection' => 'docs',
'q' => '*',
'vector_query' => 'vec:([0.96826, 0.94, 0.39557, 0.306488], k:100)'
]
]
];
// Search parameters that are common to all searches go here
$commonSearchParams = [];
$client->multiSearch->perform($searchRequests, $commonSearchParams);
search_requests = {
'searches': [
{
'collection': 'docs',
'q' : '*',
'vector_query': 'vec:([0.96826, 0.94, 0.39557, 0.306488], k:100)'
}
]
}
# Search parameters that are common to all searches go here
common_search_params = {}
client.multi_search.perform(search_requests, common_search_params)
search_requests = {
'searches': [
{
'collection' => 'docs',
'q' => '*',
'vector_query' => 'vec:([0.96826, 0.94, 0.39557, 0.306488], k:100)'
}
]
}
# Search parameters that are common to all searches go here
common_search_params = {}
client.multi_search.perform(search_requests, common_search_params)
final searchRequests = {
'searches': [
{
'collection': 'docs',
'q': '*',
'vector_query': 'vec:([0.96826, 0.94, 0.39557, 0.306488], k:100)',
}
]
};
// Search parameters that are common to all searches go here
final commonSearchParams = {};
await client.multiSearch.perform(searchRequests, queryParams: commonSearchParams);
HashMap<String,String > search1 = new HashMap<>();
search1.put("collection","docs");
search1.put("q","*");
search1.put("vector_query", "vec:([0.96826, 0.94, 0.39557, 0.306488], k:100)");
List<HashMap<String, String>> searches = new ArrayList<>();
searches.add(search1);
HashMap<String, List<HashMap<String ,String>>> searchRequests = new HashMap<>();
searchRequests.put("searches", searches);
HashMap<String,String> commonSearchParams = new HashMap<>();
commonSearchParams.put("query_by","name");
client.multiSearch.perform(searchRequests, commonSearchParams);
curl 'http://localhost:8108/multi_search' \
-X POST \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"searches": [
{
"collection": "docs",
"q": "*",
"vector_query": "vec:([0.96826,0.94,0.39557,0.306488], k:100)"
}
]
}'
NOTE: If both per_page and k parameters are provided, the larger value is used.
:::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.
02.The hits are automatically sorted in ascending order of the vector_distance, i.e. best matching documents appear first.
Sample Response
<Tabs :tabs="['JSON']"> <template v-slot:JSON>{
"facet_counts": [],
"found": 1,
"hits": [
{
"document": {
"id": "0",
"vec": [
0.04, 0.234, 0.113, 0.001
]
},
"highlight": {
"full": {},
"snippet": {}
},
"highlights": [],
"vector_distance": 0.19744956493377686
}
],
"out_of": 1,
"page": 1,
"request_params": {
"collection_name": "docs",
"per_page": 10,
"q": "*"
},
"search_cutoff": false,
"search_time_ms": 0
}
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' \
-X POST \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"searches": [
{
"collection": "docs",
"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.
:::
When using auto-embedding, you can directly set query_by to the auto-embedding field to do a semantic search on this field.
Typesense will use the same embedding model that was used to generate the auto-embedding field to generate vectors for the q parameter and then do a nearest neighbor search internally.
let search_parameters = {
'q' : 'chair',
'query_by' : 'embedding',
}
client.collections('products').documents().search(search_parameters)
$search_parameters = [
'q' => 'chair',
'query_by' => 'embedding',
];
$client->collections['products']->documents->search($search_parameters);
search_parameters = {
'q' : 'chair',
'query_by' : 'embedding',
}
client.collections['products'].documents.search(search_parameters)
search_parameters = {
'q' => 'chair',
'query_by' => 'embedding',
}
client.collections['products'].documents.search(search_parameters)
SearchParameters searchParameters = new SearchParameters();
searchParameters.q("chair")
.queryBy("embedding");
SearchResult searchResult = client.collections("products").documents().search(searchParameters);
curl 'http://localhost:8108/multi_search' \
-X POST \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"searches": [
{
"collection": "products",
"q": "chair",
"query_by": "embedding"
}
]
}'
This will automatically embed the chair query with the same model used for the embedding field and will perform a nearest neighbor vector search.
When using auto-embedding, you can set query_by to a list of both regular fields and auto-embedding fields, to do a hybrid search on multiple fields.
Typesense will do a keyword search on all the regular fields, and a semantic search on the auto-embedding field and combine the results into a ranked set of results using Rank Fusion:
K = rank of document in keyword search
S = rank of document in semantic search
rank_fusion_score = 0.7 * K + 0.3 * S
let search_parameters = {
'q' : 'chair',
'query_by' : 'embedding,product_name',
}
client.collections('products').documents().search(search_parameters)
$search_parameters = [
'q' => 'chair',
'query_by' => 'embedding,product_name',
];
$client->collections['products']->documents->search($search_parameters);
search_parameters = {
'q' : 'chair',
'query_by' : 'embedding,product_name',
}
client.collections['products'].documents.search(search_parameters)
search_parameters = {
'q' => 'chair',
'query_by' => 'embedding,product_name',
}
client.collections['products'].documents.search(search_parameters)
SearchParameters searchParameters = new SearchParameters();
searchParameters.q("chair")
.queryBy("embedding,product_name");
SearchResult searchResult = client.collections("products").documents().search(searchParameters);
curl 'http://localhost:8108/multi_search' \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-X POST \
-d '{
"searches": [
{
"collection": "products",
"query_by": "embedding,product_name",
"q": "chair",
"exclude_fields": "embedding"
}
]
}'
You can also do a hybrid search when using your own embedding field, by combining the q parameter with the vector_query parameter.
Typesense will do a keyword search using the q parameter, and a nearest neighbor search using the vector_query field and combine the results into a ranked set of results using Rank Fusion (see above).
You can also set a maximum vector distance threshold for results of semantic search and hybrid search. You should set distance_threshold in vector_query parameter for this.
let search_parameters = {
'q' : 'chair',
'query_by' : 'embedding,product_name',
'vector_query' : 'embedding:([], distance_threshold:0.30)'
}
client.collections('products').documents().search(search_parameters)
$search_parameters = [
'q' => 'chair',
'query_by' => 'embedding,product_name',
'vector_query' => 'embedding:([], distance_threshold:0.30)'
];
$client->collections['products']->documents->search($search_parameters);
search_parameters = {
'q' : 'chair',
'query_by' : 'embedding,product_name',
'vector_query' : 'embedding:([], distance_threshold:0.30)'
}
client.collections['products'].documents.search(search_parameters)
search_parameters = {
'q' => 'chair',
'query_by' => 'embedding,product_name',
'vector_query' => 'embedding:([], distance_threshold:0.30)'
}
client.collections['products'].documents.search(search_parameters)
curl 'http://localhost:8108/multi_search' \
-X POST \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"searches": [
{
"collection": "products",
"q": "chair",
"query_by": "embedding,product_name",
"vector_query": "embedding:([], distance_threshold:0.30)"
}
]
}'
When doing a hybrid search (especially with a q parameter and an explicit vector_query parameter), you can sort by a combination of vector distance and also other numeric parameters using the special sort keyword _vector_distance in sort_by.
Here's an example:
{
...
"sort_by": "popularity_score:desc,_vector_distance:asc"
...
}
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' \
-X POST \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"searches": [
{
"collection": "docs",
"q": "*",
"filter_by": "category:shoes",
"vector_query": "vec:([0.96826, 0.94, 0.39557, 0.306488], k:100, flat_search_cutoff: 20)"
}
]
}'
Here's a demo that shows you how to implement Hybrid Search (Semantic Search + Keyword Search + Filtering + Faceting) using Typesense's built-in embedding generation mechanism.
Here's a demo that shows you how to implement a "Find Similar" feature using Vector Search in an ecommerce store.
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 external embeddings API and Vector Search.