使用评分脚本进行精确 k-NN 搜索
您可以使用精确 k-NN 搜索结合评分脚本,来查找给定查询点的精确 k 个最近邻。通过使用 k-NN 评分脚本,您可以在执行最近邻搜索之前对索引应用过滤器。这对于动态搜索场景非常有用,因为索引内容可能因其他条件而异。
由于评分脚本方法执行的是暴力搜索,其扩展效率不如近似方法。在某些情况下,更好的做法是考虑重构您的工作流程或索引结构,以使用近似方法而非评分脚本方法。
向量评分脚本入门
与近似最近邻搜索类似,为了对一组向量使用评分脚本,您必须先创建一个包含一个或多个 knn_vector 字段的索引。
如果您打算仅使用评分脚本方法(而不使用近似方法),可以将 index.knn 设置为 false,并且不设置 index.knn.space_type。您可以在搜索时选择空间类型。关于 k-NN 评分脚本支持的空间类型,请参阅 空间。
此示例创建一个包含两个 knn_vector 字段的索引:
PUT my-knn-index-1
{
"mappings": {
"properties": {
"my_vector1": {
"type": "knn_vector",
"dimension": 2
},
"my_vector2": {
"type": "knn_vector",
"dimension": 4
}
}
}
}
提示:如果您想仅使用评分脚本,可以省略 "index.knn": true。这种方法能带来更快的索引速度和更低的内存使用量,但您将失去在该索引上运行标准 k-NN 查询的能力。
创建索引后,您可以向其中添加一些数据:
POST _bulk
{ "index": { "_index": "my-knn-index-1", "_id": "1" } }
{ "my_vector1": [1.5, 2.5], "price": 12.2 }
{ "index": { "_index": "my-knn-index-1", "_id": "2" } }
{ "my_vector1": [2.5, 3.5], "price": 7.1 }
{ "index": { "_index": "my-knn-index-1", "_id": "3" } }
{ "my_vector1": [3.5, 4.5], "price": 12.9 }
{ "index": { "_index": "my-knn-index-1", "_id": "4" } }
{ "my_vector1": [5.5, 6.5], "price": 1.2 }
{ "index": { "_index": "my-knn-index-1", "_id": "5" } }
{ "my_vector1": [4.5, 5.5], "price": 3.7 }
{ "index": { "_index": "my-knn-index-1", "_id": "6" } }
{ "my_vector2": [1.5, 5.5, 4.5, 6.4], "price": 10.3 }
{ "index": { "_index": "my-knn-index-1", "_id": "7" } }
{ "my_vector2": [2.5, 3.5, 5.6, 6.7], "price": 5.5 }
{ "index": { "_index": "my-knn-index-1", "_id": "8" } }
{ "my_vector2": [4.5, 5.5, 6.7, 3.7], "price": 4.4 }
{ "index": { "_index": "my-knn-index-1", "_id": "9" } }
{ "my_vector2": [1.5, 5.5, 4.5, 6.4], "price": 8.9 }
最后,您可以使用 knn 脚本对数据运行精确最近邻搜索:
GET my-knn-index-1/_search
{
"size": 4,
"query": {
"script_score": {
"query": {
"match_all": {}
},
"script": {
"source": "knn_score",
"lang": "knn",
"params": {
"field": "my_vector2",
"query_value": [2.0, 3.0, 5.0, 6.0],
"space_type": "cosinesimil"
}
}
}
}
}
所有参数都是必需的。
lang是脚本类型。此值通常是painless,但此处必须指定knn。source是脚本的名称,即knn_score。此脚本是 k-NN 插件的一部分,在标准的
_scripts路径下不可用。向_cluster/state/metadata发送 GET 请求也不会返回它。field是包含向量数据的字段。query_value是您希望查找其最近邻的点。对于欧几里得和余弦相似性空间,该值必须是与字段映射中设定的维度匹配的浮点数数组。对于汉明位距离,此值可以是带符号的长整型或 base64 编码的字符串(分别对应长整型和二进制字段类型)。space_type对应于距离函数。更多信息,请参阅 空间。
近似方法中的后过滤器示例展示了一个返回少于 k 个结果的搜索。如果您想避免这种情况,评分脚本方法允许您从根本上改变事件的顺序。换句话说,您可以先过滤要执行 k-NN 搜索的文档集。
此示例展示了使用评分脚本方法的 k-NN 搜索的前置过滤方法。首先,创建索引:
PUT my-knn-index-2
{
"mappings": {
"properties": {
"my_vector": {
"type": "knn_vector",
"dimension": 2
},
"color": {
"type": "keyword"
}
}
}
}
然后添加一些文档:
POST _bulk
{ "index": { "_index": "my-knn-index-2", "_id": "1" } }
{ "my_vector": [1, 1], "color" : "RED" }
{ "index": { "_index": "my-knn-index-2", "_id": "2" } }
{ "my_vector": [2, 2], "color" : "RED" }
{ "index": { "_index": "my-knn-index-2", "_id": "3" } }
{ "my_vector": [3, 3], "color" : "RED" }
{ "index": { "_index": "my-knn-index-2", "_id": "4" } }
{ "my_vector": [10, 10], "color" : "BLUE" }
{ "index": { "_index": "my-knn-index-2", "_id": "5" } }
{ "my_vector": [20, 20], "color" : "BLUE" }
{ "index": { "_index": "my-knn-index-2", "_id": "6" } }
{ "my_vector": [30, 30], "color" : "BLUE" }
最后,使用 script_score 查询在识别最近邻之前对文档进行前置过滤:
GET my-knn-index-2/_search
{
"size": 2,
"query": {
"script_score": {
"query": {
"bool": {
"filter": {
"term": {
"color": "BLUE"
}
}
}
},
"script": {
"lang": "knn",
"source": "knn_score",
"params": {
"field": "my_vector",
"query_value": [9.9, 9.9],
"space_type": "l2"
}
}
}
}
}
二进制数据评分脚本入门
k-NN 评分脚本还允许您使用汉明距离空间对二进制数据运行 k-NN 搜索。
为了使用汉明距离,目标字段必须具有 binary 或 long 字段类型。如果使用 binary 类型,数据必须是 base64 编码的字符串。
此示例展示了如何将汉明距离空间与 binary 字段类型结合使用:
PUT my-index
{
"mappings": {
"properties": {
"my_binary": {
"type": "binary",
"doc_values": true
},
"color": {
"type": "keyword"
}
}
}
}
然后添加一些文档:
POST _bulk
{ "index": { "_index": "my-index", "_id": "1" } }
{ "my_binary": "SGVsbG8gV29ybGQh", "color" : "RED" }
{ "index": { "_index": "my-index", "_id": "2" } }
{ "my_binary": "ay1OTiBjdXN0b20gc2NvcmluZyE=", "color" : "RED" }
{ "index": { "_index": "my-index", "_id": "3" } }
{ "my_binary": "V2VsY29tZSB0byBrLU5O", "color" : "RED" }
{ "index": { "_index": "my-index", "_id": "4" } }
{ "my_binary": "SSBob3BlIHRoaXMgaXMgaGVscGZ1bA==", "color" : "BLUE" }
{ "index": { "_index": "my-index", "_id": "5" } }
{ "my_binary": "QSBjb3VwbGUgbW9yZSBkb2NzLi4u", "color" : "BLUE" }
{ "index": { "_index": "my-index", "_id": "6" } }
{ "my_binary": "TGFzdCBvbmUh", "color" : "BLUE" }
最后,使用 script_score 查询在识别最近邻之前对文档进行前置过滤:
GET my-index/_search
{
"size": 2,
"query": {
"script_score": {
"query": {
"bool": {
"filter": {
"term": {
"color": "BLUE"
}
}
}
},
"script": {
"lang": "knn",
"source": "knn_score",
"params": {
"field": "my_binary",
"query_value": "U29tZXRoaW5nIEltIGxvb2tpbmcgZm9y",
"space_type": "hammingbit"
}
}
}
}
}
同样地,您可以使用 long 字段编码数据并运行搜索:
GET my-long-index/_search
{
"size": 2,
"query": {
"script_score": {
"query": {
"bool": {
"filter": {
"term": {
"color": "BLUE"
}
}
}
},
"script": {
"lang": "knn",
"source": "knn_score",
"params": {
"field": "my_long",
"query_value": 23,
"space_type": "hammingbit"
}
}
}
}
}