标量属性下推:加速向量+标量查询混合检索

更新时间:
复制为 MD 格式

本文介绍了标量属性下推的方法,将标量过滤集成到向量检索的算法内部,以解决传统混合查询“先过滤、后搜索”的低效问题,从而提升查询速度。

前提条件

  • 已开通向量引擎。如何开通,请参见开通向量引擎

  • 已开通搜索引擎。如何开通,请参见开通指南

  • 向量引擎与搜索引擎为3.10.11及以上版本。如何查看或升级当前版本,请参见升级小版本

    重要

    如果您的控制台显示已是最新版本,无法升级版本,请联系Lindorm技术支持(钉钉号:s0s3eg3)。

  • 已将客户端IP地址添加至Lindorm白名单,具体操作请参见设置白名单

准备工作

在使用高级特性前,请先通过curl命令连接搜索引擎。具体操作及连接参数说明,请参见连接搜索引擎

创建索引

目前属性下推支持 ivfpqivfbq(推荐)、hnswq(推荐) 三个算法。下方以hnswq + filter为例说明整体使用流程。

{
 "settings" : {
    "index": {
      "number_of_shards": 2,
      "knn": true
    }
  },
  "mappings": {
    "_source": {
      "excludes": ["vector1"]
    },
    "properties": {
      "vector1": {
        "type": "knn_vector",
        "dimension": 3,
        "meta": {"offline.construction": "true"},
        "method": {
          "engine": "lvector",
          "name": "hnswq", 
          "space_type": "l2",
          "parameters": {
            "m": 24,
            "ef_construction": 200
         }
       }
      },
      "field1": {
        "type": "integer",
          "meta": {
          "_vector_filter": "true"
        }
      }
    }
  }
}
说明

"meta": { "_vector_filter": "true" }代表启动属性下推功能,目前支持的下推属性有 integer、long、keyword、double、float、boolean。

数据写入

包含向量列的索引与普通索引的数据写入方式一致,以向量数组(Float数组)格式写入,例如[1.2, 1.3, 1.4]

单条写入

curl -u <username>:<password> -H 'Content-Type: application/json' -XPOST "http://<URL>/<索引名称>/_doc/1?pretty"  -d '
{"field1": 1, "vector1": [1.2, 1.3, 1.4]}'

以索引vector_test为例,返回结果如下:

{"_index":"vector_test","_type":"_doc","_id":"1","_version":1,"result":"created","_shards":{"total":1,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}

批量写入

curl -u <username>:<password> -H 'Content-Type: application/json' -XPUT "http://<URL>/_bulk?pretty"  -d '
{ "index" : { "_index" : "vector_test", "_id" : "2" } }
{ "field1" : 1, "vector1": [2.2, 2.3, 2.4]}
{ "index" : { "_index" : "vector_test", "_id" : "3" } }
{ "field1" : 2, "vector1": [1.2, 1.3, 4.4]}
{ "delete" : { "_index" : "vector_test", "_id" : "2" } }
{ "update" : {"_id" : "1", "_index" : "vector_test"} }
{ "doc" : {"field1" : 3, "vector1": [2.2, 3.3, 4.4]} }
'

插入写

若目标数据已存在,则不允许写入。

curl -u <username>:<password> -H 'Content-Type: application/json' -XPOST "http://<URL>/_bulk?pretty"  -d '
{ "create" : { "_index" : "vector_test", "_id" : "1" } }
{ "field1" : 1, "vector1": [2.2, 2.3, 2.4]}
{ "create" : { "_index" : "vector_test", "_id" : "2" } }
{ "field1" : 2, "vector1": [1.2, 1.3, 4.4]}
'

覆盖写

若目标数据不存在,写入数据;若目标数据已存在,则覆盖整行,之前的数据将被删除。如果首次写入仅写入向量字段,后续写入仅写入标量字段,则向量字段会被删除。

curl -u <username>:<password> -H 'Content-Type: application/json' -XPOST "http://<URL>/_bulk?pretty"  -d '
{ "index" : { "_index" : "vector_test", "_id" : "3" } }
{ "field1" : 1, "vector1": [2.2, 2.3, 2.4]}
{ "index" : { "_index" : "vector_test", "_id" : "4" } }
{ "field1" : 2, "vector1": [1.2, 1.3, 4.4]}
'

稀疏向量写入

写入方式与上述方式相同,但需要修改vector1的格式。共支持两种格式:JSON STRINGJSON Object。前者性能更优,后者格式更友好。

JSON STRING
curl -u <username>:<password> -H 'Content-Type: application/json' -XPUT "http://<URL>/vector_sparse_test/_doc/1?pretty"  -d '{
  "field1": 2, 
  "vector1": "{\"indices\": [10, 14, 16], \"values\": [0.5, 0.5, 0.2]}"
}'
JSON Object
curl -u <username>:<password> -H 'Content-Type: application/json' -XPUT "http://<URL>/vector_sparse_test/_doc/1?pretty"  -d '{
  "field1": 1, 
  "vector1": {"indices": [10, 12, 16], "values": [0.5, 0.5, 0.2]}
}'

索引构建

重要
  • ivfpq索引,其他类型索引创建时index.knn.offline.construction默认为false,即在线索引,无需手动构建。

  • 在触发ivfpq索引构建前需注意:在创建ivfpq索引时,需将index.knn.offline.construction显式指定为true,且在发起构建时务必确保已写入足够的数据量,必须大于256条且超过nlist30倍。

  • 手动触发索引构建完成后,后续可正常写入和查询,无需再次构建索引

触发构建

curl -u <username>:<password> -H 'Content-Type: application/json' -XPOST "http://<URL>/_plugins/_vector/index/build"  -d '
{
  "indexName": "vector_ivfpq_test",
  "fieldName": "vector1",
  "removeOldIndex": "true"
}'

参数说明

参数

是否必填

说明

indexName

表名称,例如vector_ivfpq_test

fieldName

针对哪个字段构建索引,例如vector1

removeOldIndex

构建索引时,是否删除旧的索引。取值如下:

  • true:在触发构建时,会删除旧的索引数据,在构建完成后才能进行knn查询。

    重要

    实际业务使用,建议设置为true

  • false(默认值):会保留旧的索引,但会影响检索性能。

返回结果如下:

{
  "payload": ["default_vector_ivfpq_test_vector1"]
}

返回结果为索引构建生成的taskId

查看索引状态

您可以通过以下方式查看索引构建状态。

curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/_plugins/_vector/index/tasks"  -d '{
  "indexName": "vector_ivfpq_test",
  "fieldName": "vector1",
  "taskIds": "[\"default_vector_ivfpq_test_vector1\"]"
}'

其中,taskIds为触发构建时生成的taskId,可以填写空的数组,例如"taskIds": "[]",效果与上述已填写taskIds的效果一致。

返回结果如下:

{
  "payload": ["task: default_vector_ivfpq_test_vector1, stage: FINISH, innerTasks: xxx, info: finish building"]
}

其中,stage表示构建状态,共包含以下几种状态:START(开始构建)、TRAIN(训练阶段)、BUILDING(构建中)、ABORT(终止构建)、FINISH(构建完成)和FAIL(构建失败)。

说明

通常调用/index/abort接口来终止索引构建,索引构建终止后状态为ABORT

终止构建

终止索引的构建流程。状态为FINISH的索引不支持终止构建操作。

curl -u <username>:<password> -H 'Content-Type: application/json' -XPOST "http://<URL>/_plugins/_vector/index/tasks/abort"  -d '{
  "indexName": "vector_ivfpq_test",
  "fieldName": "vector1",
  "taskIds": "[\"default_vector_ivfpq_test_vector1\"]"
}'

查询

通过在knn查询中内嵌filter条件,并结合ext参数中的"filter_type": "efficient_filter",实现了在向量检索过程中进行高效的实时过滤。这种方式避免了传统的“先过滤、再搜索”带来的性能瓶颈,能够大幅提升在海量数据中进行复杂条件筛选下的向量查询速度和准确性。

curl -u <username>:<password> -H 'Content-Type: application/json' -XGET "http://<URL>/<索引名称>/_search?pretty"  -d '
{
  "size": 10,
  "query": {
    "knn": {
      "vector1": {
        "vector": [2.2, 2.3, 2.4],
        "filter": {
          "range": {
            "field1": {
              "gte": 0
            }
          }
        },
        "k": 10
      }
    }
  },
  "ext": {"lvector": {"filter_type": "efficient_filter", "ef_search":"
100"}}
}'

参数说明

参数结构

参数

是否必填

说明

knn

vector

查询时使用的向量。

k

返回最相似的K个数据。

重要

在纯向量检索场景中,建议将sizek设置为相同的值。

ext

lvector.min_score

相似度阈值,要求返回的向量得分大于该值。返回的向量得分范围为[0,1]。

取值范围:[0,+inf]。默认值为0

lvector.filter_type

融合查询使用的模式。取值如下:

  • pre_filter:先过滤结构化数据,再查询向量数据。

  • post_filter:先查询向量数据,再过滤结构化数据。

  • efficient_filter:系统根据内部代价估算,自动选择使用pre_filterpost-filter

    重要

    要求搜索引擎为3.10.11及以上版本。

默认值为空。

lvector.ef_search

HNSW算法中,索引查询时动态列表的长度。只能用于HNSW算法。

取值范围:[1,1000]。默认值为100

lvector.nprobe

要查询的聚类单元(cluster units)的数量。请根据您的召回率要求,对该参数的值进行调整已达到理想效果。值越大,召回率越高,搜索性能越低。

取值范围:[1,method.parameters.nlist]。无默认值。

重要

仅适用于ivfpq算法。

lvector.reorder_factor

使用原始向量创建重排序(reorder)。ivfpq算法计算的距离为量化后的距离,会有一定的精度损失,需要使用原始向量进行重排序。比例为k * reorder_factor ,通常用于提升召回精度,但会增加性能开销。

取值范围:[1,200]。默认值为10

重要
  • 仅适用于ivfpq算法。

  • k值较小时可以设置为5,如果k大于100,直接设置为1即可。

lvector.client_refactor

是否不在每个分片(Shard)内进行重排序,而是在系统上层进行重排序,进而提升系统性能。取值如下:

  • true:是。

  • false(默认值):否。

lvector. k_expand_scope

Efficient_Filter近似查询场景下,服务端如果选择了post_filter模式,将使用该值替换k值,用来实现放大k值的效果。默认值为1000