Skip to main content

kNN Search Request

The kNN Search request performs a k-nearest neighbor (kNN) search: given a query vector, it finds the k closest vectors and returns those documents as search hits.

Note that this request uses the _knn_search API, which was deprecated in Elasticsearch 8.4 in favour of the knn option of the _search API. It is still available in 8.x, but may be removed in a future Elasticsearch version.

In order to use the kNN Search request, the searched field must be mapped as a dense_vector field with indexing enabled, for example:

import zio.elasticsearch.ElasticRequest.{createIndex, CreateIndexRequest}
import zio.elasticsearch._

val createIndexRequest: CreateIndexRequest =
createIndex(
index = IndexName("index"),
definition = """{ "mappings": { "properties": { "vectorField": { "type": "dense_vector", "dims": 3, "similarity": "l2_norm", "index": true } } } }"""
)

To create a kNN Search request do the following:

import zio.Chunk
import zio.elasticsearch.ElasticRequest.{knnSearch, KNNRequest}
// this import is required for using `IndexName`, `IndexPattern` and `MultiIndex`
import zio.elasticsearch._
import zio.elasticsearch.ElasticQuery._

val request: KNNRequest =
knnSearch(selectors = IndexName("index"), query = kNN(field = Document.vectorField, k = 2, numCandidates = 5, queryVector = Chunk(1.0, 2.0, 3.0)))

The query parameter of the knnSearch method is a kNN query.

If you want to filter the documents that can match, you can use the filter method with any query:

val requestWithFilter: KNNRequest =
knnSearch(selectors = IndexName("index"), query = kNN(field = Document.vectorField, k = 2, numCandidates = 5, queryVector = Chunk(1.0, 2.0, 3.0)))
.filter(range(Document.intField).gt(10))

If you want to change the routing, you can use the routing method:

// this import is required for using `Routing` also
import zio.elasticsearch._

val requestWithRouting: KNNRequest =
knnSearch(selectors = IndexName("index"), query = kNN(field = Document.vectorField, k = 2, numCandidates = 5, queryVector = Chunk(1.0, 2.0, 3.0)))
.routing(Routing("routing"))

If you want to create kNN Search request with IndexPattern, do the following:

val requestWithIndexPattern: KNNRequest =
knnSearch(selectors = IndexPattern("index*"), query = kNN(field = Document.vectorField, k = 2, numCandidates = 5, queryVector = Chunk(1.0, 2.0, 3.0)))

If you want to create kNN Search request with MultiIndex, do the following:

val requestWithMultiIndex: KNNRequest =
knnSearch(
selectors = MultiIndex.names(IndexName("index1"), IndexName("index2")),
query = kNN(field = Document.vectorField, k = 2, numCandidates = 5, queryVector = Chunk(1.0, 2.0, 3.0))
)

The result of executing a kNN Search request is KNNSearchResult, from which you can get the documents (or items) of the nearest neighbors:

import zio._

val result: RIO[Elasticsearch, Chunk[Document]] =
Elasticsearch.execute(request).documentAs[Document]

You can find more information about kNN Search request here.