KNN 查询

编辑

根据相似度度量找到与查询向量最接近的 *k* 个向量。knn 查询通过对索引的稠密向量进行近似搜索来查找最近的向量。进行近似 kNN 搜索的首选方法是通过搜索请求的顶级 knn 部分knn 查询保留用于专家案例,在这些案例中需要将此查询与其他查询结合使用。

示例请求

编辑
resp = client.indices.create(
    index="my-image-index",
    mappings={
        "properties": {
            "image-vector": {
                "type": "dense_vector",
                "dims": 3,
                "index": True,
                "similarity": "l2_norm"
            },
            "file-type": {
                "type": "keyword"
            },
            "title": {
                "type": "text"
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'my-image-index',
  body: {
    mappings: {
      properties: {
        "image-vector": {
          type: 'dense_vector',
          dims: 3,
          index: true,
          similarity: 'l2_norm'
        },
        "file-type": {
          type: 'keyword'
        },
        title: {
          type: 'text'
        }
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "my-image-index",
  mappings: {
    properties: {
      "image-vector": {
        type: "dense_vector",
        dims: 3,
        index: true,
        similarity: "l2_norm",
      },
      "file-type": {
        type: "keyword",
      },
      title: {
        type: "text",
      },
    },
  },
});
console.log(response);
PUT my-image-index
{
  "mappings": {
    "properties": {
       "image-vector": {
        "type": "dense_vector",
        "dims": 3,
        "index": true,
        "similarity": "l2_norm"
      },
      "file-type": {
        "type": "keyword"
      },
      "title": {
        "type": "text"
      }
    }
  }
}
  1. 索引您的数据。

    resp = client.bulk(
        index="my-image-index",
        refresh=True,
        operations=[
            {
                "index": {
                    "_id": "1"
                }
            },
            {
                "image-vector": [
                    1,
                    5,
                    -20
                ],
                "file-type": "jpg",
                "title": "mountain lake"
            },
            {
                "index": {
                    "_id": "2"
                }
            },
            {
                "image-vector": [
                    42,
                    8,
                    -15
                ],
                "file-type": "png",
                "title": "frozen lake"
            },
            {
                "index": {
                    "_id": "3"
                }
            },
            {
                "image-vector": [
                    15,
                    11,
                    23
                ],
                "file-type": "jpg",
                "title": "mountain lake lodge"
            }
        ],
    )
    print(resp)
    response = client.bulk(
      index: 'my-image-index',
      refresh: true,
      body: [
        {
          index: {
            _id: '1'
          }
        },
        {
          "image-vector": [
            1,
            5,
            -20
          ],
          "file-type": 'jpg',
          title: 'mountain lake'
        },
        {
          index: {
            _id: '2'
          }
        },
        {
          "image-vector": [
            42,
            8,
            -15
          ],
          "file-type": 'png',
          title: 'frozen lake'
        },
        {
          index: {
            _id: '3'
          }
        },
        {
          "image-vector": [
            15,
            11,
            23
          ],
          "file-type": 'jpg',
          title: 'mountain lake lodge'
        }
      ]
    )
    puts response
    const response = await client.bulk({
      index: "my-image-index",
      refresh: "true",
      operations: [
        {
          index: {
            _id: "1",
          },
        },
        {
          "image-vector": [1, 5, -20],
          "file-type": "jpg",
          title: "mountain lake",
        },
        {
          index: {
            _id: "2",
          },
        },
        {
          "image-vector": [42, 8, -15],
          "file-type": "png",
          title: "frozen lake",
        },
        {
          index: {
            _id: "3",
          },
        },
        {
          "image-vector": [15, 11, 23],
          "file-type": "jpg",
          title: "mountain lake lodge",
        },
      ],
    });
    console.log(response);
    POST my-image-index/_bulk?refresh=true
    { "index": { "_id": "1" } }
    { "image-vector": [1, 5, -20], "file-type": "jpg", "title": "mountain lake" }
    { "index": { "_id": "2" } }
    { "image-vector": [42, 8, -15], "file-type": "png", "title": "frozen lake"}
    { "index": { "_id": "3" } }
    { "image-vector": [15, 11, 23], "file-type": "jpg", "title": "mountain lake lodge" }
  2. 使用 knn 查询运行搜索,请求从每个分片获取前 10 个最近的向量,然后合并分片结果以获得前 3 个全局结果。

    resp = client.search(
        index="my-image-index",
        size=3,
        query={
            "knn": {
                "field": "image-vector",
                "query_vector": [
                    -5,
                    9,
                    -12
                ],
                "k": 10
            }
        },
    )
    print(resp)
    const response = await client.search({
      index: "my-image-index",
      size: 3,
      query: {
        knn: {
          field: "image-vector",
          query_vector: [-5, 9, -12],
          k: 10,
        },
      },
    });
    console.log(response);
    POST my-image-index/_search
    {
      "size" : 3,
      "query" : {
        "knn": {
          "field": "image-vector",
          "query_vector": [-5, 9, -12],
          "k": 10
        }
      }
    }

knn 的顶级参数

编辑
field

(必填,字符串) 要搜索的向量字段的名称。必须是已启用索引的 dense_vector 字段

query_vector

(可选,浮点数数组或字符串) 查询向量。必须与您要搜索的向量字段具有相同的维度数。必须是浮点数数组或十六进制编码的字节向量。必须提供此参数或 query_vector_builder 参数,但不能同时提供两者。

query_vector_builder

(可选,对象) 查询向量构建器。一个配置对象,指示如何在执行请求之前构建 query_vector。您必须提供 query_vector_builderquery_vector,但不能同时提供两者。请参阅执行语义搜索了解更多信息。

k

(可选,整数) 从每个分片返回的最近邻的数量。Elasticsearch 从每个分片收集 k 个结果,然后合并它们以找到全局顶级结果。此值必须小于或等于 num_candidates。默认为 num_candidates

num_candidates

(可选,整数) 在执行 knn 搜索时,每个分片要考虑的最近邻候选数。不能超过 10,000。增加 num_candidates 往往会提高最终结果的准确性。如果设置了 k,则默认为 1.5 * k;如果未设置 k,则默认为 1.5 * size

filter

(可选,查询对象) 用于过滤可以匹配的文档的查询。kNN 搜索将返回也匹配此过滤器的顶级文档。该值可以是单个查询或查询列表。如果没有提供 filter,则允许所有文档匹配。

过滤器是一个预过滤器,这意味着它在近似 kNN 搜索期间应用,以确保返回 num_candidates 个匹配的文档。

similarity

(可选,浮点数) 文档被认为匹配所需的最小相似度。计算出的相似度值与使用的原始similarity相关。不是文档分数。然后根据similarity对匹配的文档进行评分,并应用提供的 boost

boost

(可选,浮点数) 用于将匹配文档的分数相乘的浮点数。此值不能为负。默认为 1.0

_name

(可选,字符串) 用于标识查询的名称字段

knn 查询中的预过滤器和后过滤器

编辑

有两种方法可以过滤与 kNN 查询匹配的文档

  1. 预过滤 – 在近似 kNN 搜索期间应用过滤器,以确保返回 k 个匹配的文档。
  2. 后过滤 – 在近似 kNN 搜索完成后应用过滤器,即使有足够的匹配文档,也会导致结果少于 k 个。

预过滤通过 knn 查询的 filter 参数支持。来自别名的过滤器也作为预过滤器应用。

在查询 DSL 树中找到的所有其他过滤器都将作为后过滤器应用。例如,knn 查询查找具有最近向量的顶级 3 个文档 (k=3),这些文档与 term 过滤器组合,该过滤器是后过滤的。最终的文档集将仅包含通过后过滤器的单个文档。

resp = client.search(
    index="my-image-index",
    size=10,
    query={
        "bool": {
            "must": {
                "knn": {
                    "field": "image-vector",
                    "query_vector": [
                        -5,
                        9,
                        -12
                    ],
                    "k": 3
                }
            },
            "filter": {
                "term": {
                    "file-type": "png"
                }
            }
        }
    },
)
print(resp)
const response = await client.search({
  index: "my-image-index",
  size: 10,
  query: {
    bool: {
      must: {
        knn: {
          field: "image-vector",
          query_vector: [-5, 9, -12],
          k: 3,
        },
      },
      filter: {
        term: {
          "file-type": "png",
        },
      },
    },
  },
});
console.log(response);
POST my-image-index/_search
{
  "size" : 10,
  "query" : {
    "bool" : {
      "must" : {
        "knn": {
          "field": "image-vector",
          "query_vector": [-5, 9, -12],
          "k": 3
        }
      },
      "filter" : {
        "term" : { "file-type" : "png" }
      }
    }
  }
}

使用 knn 查询进行混合搜索

编辑

Knn 查询可用作混合搜索的一部分,其中 knn 查询与其他词汇查询相结合。例如,下面的查询查找 title 匹配 mountain lake 的文档,并将它们与与 query_vector 最接近的图像向量的顶级 10 个文档组合。然后对组合的文档进行评分,并返回前 3 个得分最高的文档。

+

resp = client.search(
    index="my-image-index",
    size=3,
    query={
        "bool": {
            "should": [
                {
                    "match": {
                        "title": {
                            "query": "mountain lake",
                            "boost": 1
                        }
                    }
                },
                {
                    "knn": {
                        "field": "image-vector",
                        "query_vector": [
                            -5,
                            9,
                            -12
                        ],
                        "k": 10,
                        "boost": 2
                    }
                }
            ]
        }
    },
)
print(resp)
const response = await client.search({
  index: "my-image-index",
  size: 3,
  query: {
    bool: {
      should: [
        {
          match: {
            title: {
              query: "mountain lake",
              boost: 1,
            },
          },
        },
        {
          knn: {
            field: "image-vector",
            query_vector: [-5, 9, -12],
            k: 10,
            boost: 2,
          },
        },
      ],
    },
  },
});
console.log(response);
POST my-image-index/_search
{
  "size" : 3,
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": {
              "query": "mountain lake",
              "boost": 1
            }
          }
        },
        {
          "knn": {
            "field": "image-vector",
            "query_vector": [-5, 9, -12],
            "k": 10,
            "boost": 2
          }
        }
      ]
    }
  }
}

嵌套查询中的 Knn 查询

编辑

knn 查询可用于嵌套查询中。此处的行为类似于顶级嵌套 kNN 搜索

  • 嵌套稠密向量上的 kNN 搜索使顶级文档的顶级结果多样化
  • 支持对顶级文档元数据的filter,并充当预过滤器
  • 不支持对nested字段元数据的filter

示例查询如下所示

{
  "query" : {
    "nested" : {
      "path" : "paragraph",
        "query" : {
          "knn": {
            "query_vector": [
                0.45,
                45
            ],
            "field": "paragraph.vector",
            "num_candidates": 2
        }
      }
    }
  }
}

带有聚合的 Knn 查询

编辑

knn 查询计算每个分片的前 k 个文档的聚合。因此,聚合的最终结果包含 k * number_of_shards 个文档。这与顶级 knn 部分不同,在顶级 knn 部分中,聚合是在全局顶级 k 个最近的文档上计算的。