Knn 查询

编辑

根据相似性度量,查找与查询向量最接近的 k 个向量。knn 查询通过对索引的 dense_vectors 进行近似搜索来查找最近的向量。执行近似 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

(可选,浮点数) 文档被视为匹配项所需的最小相似度。计算的相似度值与使用的原始相似度有关,而不是文档分数。然后,根据相似度对匹配的文档进行评分,并应用提供的 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 查询与其他词法查询结合使用。例如,以下查询查找 titlemountain 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 搜索

  • 在嵌套的 dense_vectors 上进行 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 部分不同,后者在全局前 k 个最近文档上计算聚合。