脚本评分查询

编辑

使用脚本为返回的文档提供自定义评分。

例如,如果评分函数的计算成本很高,并且您只需要计算过滤后的文档集的评分,则 script_score 查询会很有用。

示例请求

编辑

以下 script_score 查询为每个返回的文档分配一个等于 my-int 字段值除以 10 的分数。

resp = client.search(
    query={
        "script_score": {
            "query": {
                "match": {
                    "message": "elasticsearch"
                }
            },
            "script": {
                "source": "doc['my-int'].value / 10 "
            }
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      script_score: {
        query: {
          match: {
            message: 'elasticsearch'
          }
        },
        script: {
          source: "doc['my-int'].value / 10 "
        }
      }
    }
  }
)
puts response
const response = await client.search({
  query: {
    script_score: {
      query: {
        match: {
          message: "elasticsearch",
        },
      },
      script: {
        source: "doc['my-int'].value / 10 ",
      },
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "script_score": {
      "query": {
        "match": { "message": "elasticsearch" }
      },
      "script": {
        "source": "doc['my-int'].value / 10 "
      }
    }
  }
}

script_score 的顶层参数

编辑
query
(必需,查询对象)用于返回文档的查询。
script

(必需,脚本对象)用于计算由 query 返回的文档的分数的脚本。

来自 script_score 查询的最终相关性分数不能为负数。为了支持某些搜索优化,Lucene 要求分数是正数或 0

min_score
(可选,浮点数)分数低于此浮点数的文档将从搜索结果中排除。
boost
(可选,浮点数)script 生成的文档分数乘以 boost 以生成最终的文档分数。默认为 1.0

注意

编辑

在脚本中使用相关性分数

编辑

在脚本中,您可以访问表示文档当前相关性分数的 _score 变量。

在脚本中使用词项统计信息

编辑

在脚本中,您可以访问 _termStats 变量,该变量提供有关 script_score 查询的子查询中使用的词项的统计信息。

预定义函数

编辑

您可以在 script 中使用任何可用的 painless 函数。您还可以使用以下预定义函数来自定义评分

我们建议使用这些预定义函数而不是编写自己的函数。这些函数利用了 Elasticsearch 内部机制的效率。

饱和度
编辑

saturation(value,k) = value/(k + value)

"script" : {
    "source" : "saturation(doc['my-int'].value, 1)"
}
Sigmoid
编辑

sigmoid(value, k, a) = value^a/ (k^a + value^a)

"script" : {
    "source" : "sigmoid(doc['my-int'].value, 2, 1)"
}
随机评分函数
编辑

random_score 函数生成均匀分布的分数,范围从 0 到但不包括 1。

randomScore 函数具有以下语法:randomScore(<seed>, <fieldName>)。它有一个必需的参数 - seed 作为整数值,以及一个可选的参数 - fieldName 作为字符串值。

"script" : {
    "source" : "randomScore(100, '_seq_no')"
}

如果省略 fieldName 参数,则内部 Lucene 文档 ID 将用作随机性的来源。这非常高效,但不幸的是不可重现,因为文档可能会因合并而重新编号。

"script" : {
    "source" : "randomScore(100)"
}

请注意,同一分片中且字段值相同的文档将获得相同的分数,因此通常需要使用一个对整个分片中的所有文档都具有唯一值的字段。一个好的默认选择可能是使用 _seq_no 字段,它的唯一缺点是,如果文档更新,分数也会更改,因为更新操作也会更新 _seq_no 字段的值。

数值字段的衰减函数
编辑

您可以在此处阅读有关衰减函数的更多信息。

  • double decayNumericLinear(double origin, double scale, double offset, double decay, double docValue)
  • double decayNumericExp(double origin, double scale, double offset, double decay, double docValue)
  • double decayNumericGauss(double origin, double scale, double offset, double decay, double docValue)
"script" : {
    "source" : "decayNumericLinear(params.origin, params.scale, params.offset, params.decay, doc['dval'].value)",
    "params": { 
        "origin": 20,
        "scale": 10,
        "decay" : 0.5,
        "offset" : 0
    }
}

使用 params 允许仅编译一次脚本,即使参数发生更改也是如此。

地理字段的衰减函数
编辑
  • double decayGeoLinear(String originStr, String scaleStr, String offsetStr, double decay, GeoPoint docValue)
  • double decayGeoExp(String originStr, String scaleStr, String offsetStr, double decay, GeoPoint docValue)
  • double decayGeoGauss(String originStr, String scaleStr, String offsetStr, double decay, GeoPoint docValue)
"script" : {
    "source" : "decayGeoExp(params.origin, params.scale, params.offset, params.decay, doc['location'].value)",
    "params": {
        "origin": "40, -70.12",
        "scale": "200km",
        "offset": "0km",
        "decay" : 0.2
    }
}
日期字段的衰减函数
编辑
  • double decayDateLinear(String originStr, String scaleStr, String offsetStr, double decay, JodaCompatibleZonedDateTime docValueDate)
  • double decayDateExp(String originStr, String scaleStr, String offsetStr, double decay, JodaCompatibleZonedDateTime docValueDate)
  • double decayDateGauss(String originStr, String scaleStr, String offsetStr, double decay, JodaCompatibleZonedDateTime docValueDate)
"script" : {
    "source" : "decayDateGauss(params.origin, params.scale, params.offset, params.decay, doc['date'].value)",
    "params": {
        "origin": "2008-01-01T01:00:00Z",
        "scale": "1h",
        "offset" : "0",
        "decay" : 0.5
    }
}

日期衰减函数仅限于默认格式和默认时区的日期。也不支持使用 now 进行计算。

向量字段的函数
编辑

可以通过 script_score 查询访问向量字段的函数

允许昂贵的查询

编辑

如果 search.allow_expensive_queries 设置为 false,则不会执行脚本评分查询。

更快的替代方案

编辑

script_score 查询会计算每个匹配文档或命中的分数。有一些更快的替代查询类型可以有效地跳过非竞争的命中

  • 如果要在某些静态字段上提升文档,请使用 rank_feature 查询。
  • 如果要在更接近日期或地理点的文档上提升文档,请使用 distance_feature 查询。

从函数评分查询过渡

编辑

我们建议使用 script_score 查询而不是 function_score 查询,因为 script_score 查询更简单。

您可以使用 script_score 查询实现 function_score 查询的以下函数

script_score
编辑

您在函数评分查询的 script_score 中使用的内容,您可以复制到脚本评分查询中。这里没有变化。

weight
编辑

weight 函数可以通过以下脚本在脚本评分查询中实现

"script" : {
    "source" : "params.weight * _score",
    "params": {
        "weight": 2
    }
}
random_score
编辑

使用随机评分函数中描述的 randomScore 函数。

field_value_factor
编辑

field_value_factor 函数可以通过脚本轻松实现

"script" : {
    "source" : "Math.log10(doc['field'].value * params.factor)",
    "params" : {
        "factor" : 5
    }
}

要检查文档是否缺少值,可以使用 doc['field'].size() == 0。例如,如果文档没有 field 字段,则此脚本将使用值 1

"script" : {
    "source" : "Math.log10((doc['field'].size() == 0 ? 1 : doc['field'].value()) * params.factor)",
    "params" : {
        "factor" : 5
    }
}

此表列出了如何通过脚本实现 field_value_factor 修饰符

修饰符 脚本评分中的实现

none

-

log

Math.log10(doc['f'].value)

log1p

Math.log10(doc['f'].value + 1)

log2p

Math.log10(doc['f'].value + 2)

ln

Math.log(doc['f'].value)

ln1p

Math.log(doc['f'].value + 1)

ln2p

Math.log(doc['f'].value + 2)

square

Math.pow(doc['f'].value, 2)

sqrt

Math.sqrt(doc['f'].value)

reciprocal

1.0 / doc['f'].value

decay 函数
编辑

script_score 查询具有可以在脚本中使用的等效衰减函数

向量字段的函数

编辑

在向量函数计算期间,所有匹配的文档都会被线性扫描。因此,预计查询时间会随着匹配文档的数量线性增长。因此,我们建议使用 query 参数限制匹配文档的数量。

这是可用的向量函数和向量访问方法的列表

  1. cosineSimilarity – 计算余弦相似度
  2. dotProduct – 计算点积
  3. l1norm – 计算 L1 距离
  4. hamming – 计算汉明距离
  5. l2norm - 计算 L2 距离
  6. doc[<field>].vectorValue – 返回向量的值,作为浮点数数组
  7. doc[<field>].magnitude – 返回向量的大小(模)

cosineSimilarity 函数不支持 bit 向量。

推荐的访问稠密向量的方式是通过 cosineSimilaritydotProductl1norml2norm 函数。请注意,您应该每个脚本只调用这些函数一次。例如,不要在循环中使用这些函数来计算文档向量和多个其他向量之间的相似度。如果需要该功能,请通过直接访问向量值来自己重新实现这些函数。

让我们创建一个带有 dense_vector 映射的索引,并将一些文档索引到其中。

resp = client.indices.create(
    index="my-index-000001",
    mappings={
        "properties": {
            "my_dense_vector": {
                "type": "dense_vector",
                "index": False,
                "dims": 3
            },
            "my_byte_dense_vector": {
                "type": "dense_vector",
                "index": False,
                "dims": 3,
                "element_type": "byte"
            },
            "status": {
                "type": "keyword"
            }
        }
    },
)
print(resp)

resp1 = client.index(
    index="my-index-000001",
    id="1",
    document={
        "my_dense_vector": [
            0.5,
            10,
            6
        ],
        "my_byte_dense_vector": [
            0,
            10,
            6
        ],
        "status": "published"
    },
)
print(resp1)

resp2 = client.index(
    index="my-index-000001",
    id="2",
    document={
        "my_dense_vector": [
            -0.5,
            10,
            10
        ],
        "my_byte_dense_vector": [
            0,
            10,
            10
        ],
        "status": "published"
    },
)
print(resp2)

resp3 = client.indices.refresh(
    index="my-index-000001",
)
print(resp3)
const response = await client.indices.create({
  index: "my-index-000001",
  mappings: {
    properties: {
      my_dense_vector: {
        type: "dense_vector",
        index: false,
        dims: 3,
      },
      my_byte_dense_vector: {
        type: "dense_vector",
        index: false,
        dims: 3,
        element_type: "byte",
      },
      status: {
        type: "keyword",
      },
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "my-index-000001",
  id: 1,
  document: {
    my_dense_vector: [0.5, 10, 6],
    my_byte_dense_vector: [0, 10, 6],
    status: "published",
  },
});
console.log(response1);

const response2 = await client.index({
  index: "my-index-000001",
  id: 2,
  document: {
    my_dense_vector: [-0.5, 10, 10],
    my_byte_dense_vector: [0, 10, 10],
    status: "published",
  },
});
console.log(response2);

const response3 = await client.indices.refresh({
  index: "my-index-000001",
});
console.log(response3);
PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_dense_vector": {
        "type": "dense_vector",
        "index": false,
        "dims": 3
      },
      "my_byte_dense_vector": {
        "type": "dense_vector",
        "index": false,
        "dims": 3,
        "element_type": "byte"
      },
      "status" : {
        "type" : "keyword"
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "my_dense_vector": [0.5, 10, 6],
  "my_byte_dense_vector": [0, 10, 6],
  "status" : "published"
}

PUT my-index-000001/_doc/2
{
  "my_dense_vector": [-0.5, 10, 10],
  "my_byte_dense_vector": [0, 10, 10],
  "status" : "published"
}

POST my-index-000001/_refresh
余弦相似度
编辑

cosineSimilarity 函数计算给定查询向量和文档向量之间的余弦相似度度量。

resp = client.search(
    index="my-index-000001",
    query={
        "script_score": {
            "query": {
                "bool": {
                    "filter": {
                        "term": {
                            "status": "published"
                        }
                    }
                }
            },
            "script": {
                "source": "cosineSimilarity(params.query_vector, 'my_dense_vector') + 1.0",
                "params": {
                    "query_vector": [
                        4,
                        3.4,
                        -0.2
                    ]
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    query: {
      script_score: {
        query: {
          bool: {
            filter: {
              term: {
                status: 'published'
              }
            }
          }
        },
        script: {
          source: "cosineSimilarity(params.query_vector, 'my_dense_vector') + 1.0",
          params: {
            query_vector: [
              4,
              3.4,
              -0.2
            ]
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  query: {
    script_score: {
      query: {
        bool: {
          filter: {
            term: {
              status: "published",
            },
          },
        },
      },
      script: {
        source:
          "cosineSimilarity(params.query_vector, 'my_dense_vector') + 1.0",
        params: {
          query_vector: [4, 3.4, -0.2],
        },
      },
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published" 
            }
          }
        }
      },
      "script": {
        "source": "cosineSimilarity(params.query_vector, 'my_dense_vector') + 1.0", 
        "params": {
          "query_vector": [4, 3.4, -0.2]  
        }
      }
    }
  }
}

要限制应用脚本评分计算的文档数量,请提供过滤器。

该脚本将 1.0 添加到余弦相似度,以防止分数变为负数。

要利用脚本优化,请将查询向量作为脚本参数提供。

如果文档的稠密向量字段的维度数与查询的向量不同,将抛出错误。

点积
编辑

dotProduct 函数计算给定查询向量和文档向量之间的点积度量。

resp = client.search(
    index="my-index-000001",
    query={
        "script_score": {
            "query": {
                "bool": {
                    "filter": {
                        "term": {
                            "status": "published"
                        }
                    }
                }
            },
            "script": {
                "source": "\n          double value = dotProduct(params.query_vector, 'my_dense_vector');\n          return sigmoid(1, Math.E, -value); \n        ",
                "params": {
                    "query_vector": [
                        4,
                        3.4,
                        -0.2
                    ]
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    query: {
      script_score: {
        query: {
          bool: {
            filter: {
              term: {
                status: 'published'
              }
            }
          }
        },
        script: {
          source: "\n          double value = dotProduct(params.query_vector, 'my_dense_vector');\n          return sigmoid(1, Math.E, -value); \n        ",
          params: {
            query_vector: [
              4,
              3.4,
              -0.2
            ]
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  query: {
    script_score: {
      query: {
        bool: {
          filter: {
            term: {
              status: "published",
            },
          },
        },
      },
      script: {
        source:
          "\n          double value = dotProduct(params.query_vector, 'my_dense_vector');\n          return sigmoid(1, Math.E, -value); \n        ",
        params: {
          query_vector: [4, 3.4, -0.2],
        },
      },
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published"
            }
          }
        }
      },
      "script": {
        "source": """
          double value = dotProduct(params.query_vector, 'my_dense_vector');
          return sigmoid(1, Math.E, -value); 
        """,
        "params": {
          "query_vector": [4, 3.4, -0.2]
        }
      }
    }
  }
}

使用标准 sigmoid 函数可以防止分数变为负数。

L1 距离(曼哈顿距离)
编辑

l1norm 函数计算给定查询向量和文档向量之间的 L1 距离(曼哈顿距离)。

resp = client.search(
    index="my-index-000001",
    query={
        "script_score": {
            "query": {
                "bool": {
                    "filter": {
                        "term": {
                            "status": "published"
                        }
                    }
                }
            },
            "script": {
                "source": "1 / (1 + l1norm(params.queryVector, 'my_dense_vector'))",
                "params": {
                    "queryVector": [
                        4,
                        3.4,
                        -0.2
                    ]
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    query: {
      script_score: {
        query: {
          bool: {
            filter: {
              term: {
                status: 'published'
              }
            }
          }
        },
        script: {
          source: "1 / (1 + l1norm(params.queryVector, 'my_dense_vector'))",
          params: {
            "queryVector": [
              4,
              3.4,
              -0.2
            ]
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  query: {
    script_score: {
      query: {
        bool: {
          filter: {
            term: {
              status: "published",
            },
          },
        },
      },
      script: {
        source: "1 / (1 + l1norm(params.queryVector, 'my_dense_vector'))",
        params: {
          queryVector: [4, 3.4, -0.2],
        },
      },
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published"
            }
          }
        }
      },
      "script": {
        "source": "1 / (1 + l1norm(params.queryVector, 'my_dense_vector'))", 
        "params": {
          "queryVector": [4, 3.4, -0.2]
        }
      }
    }
  }
}

与表示相似度的 cosineSimilarity 不同,下面显示的 l1norml2norm 表示距离或差异。这意味着,向量越相似,l1norml2norm 函数产生的分数就越低。因此,由于我们需要更相似的向量来获得更高的分数,我们反转了 l1norml2norm 的输出。此外,为了避免当文档向量与查询完全匹配时除以 0,我们在分母中添加了 1

汉明距离
编辑

hamming 函数计算给定查询向量和文档向量之间的 汉明距离。它仅适用于字节向量和位向量。

resp = client.search(
    index="my-index-000001",
    query={
        "script_score": {
            "query": {
                "bool": {
                    "filter": {
                        "term": {
                            "status": "published"
                        }
                    }
                }
            },
            "script": {
                "source": "(24 - hamming(params.queryVector, 'my_byte_dense_vector')) / 24",
                "params": {
                    "queryVector": [
                        4,
                        3,
                        0
                    ]
                }
            }
        }
    },
)
print(resp)
const response = await client.search({
  index: "my-index-000001",
  query: {
    script_score: {
      query: {
        bool: {
          filter: {
            term: {
              status: "published",
            },
          },
        },
      },
      script: {
        source:
          "(24 - hamming(params.queryVector, 'my_byte_dense_vector')) / 24",
        params: {
          queryVector: [4, 3, 0],
        },
      },
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published"
            }
          }
        }
      },
      "script": {
        "source": "(24 - hamming(params.queryVector, 'my_byte_dense_vector')) / 24", 
        "params": {
          "queryVector": [4, 3, 0]
        }
      }
    }
  }
}

计算汉明距离并将其按位归一化,以获得 0 到 1 之间的分数。

L2 距离(欧几里得距离)
编辑

l2norm 函数计算给定查询向量和文档向量之间的 L2 距离(欧几里得距离)。

resp = client.search(
    index="my-index-000001",
    query={
        "script_score": {
            "query": {
                "bool": {
                    "filter": {
                        "term": {
                            "status": "published"
                        }
                    }
                }
            },
            "script": {
                "source": "1 / (1 + l2norm(params.queryVector, 'my_dense_vector'))",
                "params": {
                    "queryVector": [
                        4,
                        3.4,
                        -0.2
                    ]
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    query: {
      script_score: {
        query: {
          bool: {
            filter: {
              term: {
                status: 'published'
              }
            }
          }
        },
        script: {
          source: "1 / (1 + l2norm(params.queryVector, 'my_dense_vector'))",
          params: {
            "queryVector": [
              4,
              3.4,
              -0.2
            ]
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  query: {
    script_score: {
      query: {
        bool: {
          filter: {
            term: {
              status: "published",
            },
          },
        },
      },
      script: {
        source: "1 / (1 + l2norm(params.queryVector, 'my_dense_vector'))",
        params: {
          queryVector: [4, 3.4, -0.2],
        },
      },
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published"
            }
          }
        }
      },
      "script": {
        "source": "1 / (1 + l2norm(params.queryVector, 'my_dense_vector'))",
        "params": {
          "queryVector": [4, 3.4, -0.2]
        }
      }
    }
  }
}
检查缺失值
编辑

如果文档没有执行向量函数的向量字段的值,将抛出错误。

您可以使用 doc['my_vector'].size() == 0 来检查文档是否具有字段 my_vector 的值。您的完整脚本可以如下所示

"source": "doc['my_vector'].size() == 0 ? 0 : cosineSimilarity(params.queryVector, 'my_vector')"
直接访问向量
编辑

您可以通过以下函数直接访问向量值

  • doc[<field>].vectorValue – 以浮点数组的形式返回向量的值

对于 bit 向量,它会返回一个 float[],其中每个元素表示 8 位。

  • doc[<field>].magnitude – 以浮点数形式返回向量的大小(模)(对于在 7.5 版本之前创建的向量,不存储大小。因此,此函数每次调用都会重新计算它)。

对于 bit 向量,这只是 1 位之和的平方根。

例如,以下脚本使用这两个函数实现余弦相似度

resp = client.search(
    index="my-index-000001",
    query={
        "script_score": {
            "query": {
                "bool": {
                    "filter": {
                        "term": {
                            "status": "published"
                        }
                    }
                }
            },
            "script": {
                "source": "\n          float[] v = doc['my_dense_vector'].vectorValue;\n          float vm = doc['my_dense_vector'].magnitude;\n          float dotProduct = 0;\n          for (int i = 0; i < v.length; i++) {\n            dotProduct += v[i] * params.queryVector[i];\n          }\n          return dotProduct / (vm * (float) params.queryVectorMag);\n        ",
                "params": {
                    "queryVector": [
                        4,
                        3.4,
                        -0.2
                    ],
                    "queryVectorMag": 5.25357
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    query: {
      script_score: {
        query: {
          bool: {
            filter: {
              term: {
                status: 'published'
              }
            }
          }
        },
        script: {
          source: "\n          float[] v = doc['my_dense_vector'].vectorValue;\n          float vm = doc['my_dense_vector'].magnitude;\n          float dotProduct = 0;\n          for (int i = 0; i < v.length; i++) {\n            dotProduct += v[i] * params.queryVector[i];\n          }\n          return dotProduct / (vm * (float) params.queryVectorMag);\n        ",
          params: {
            "queryVector": [
              4,
              3.4,
              -0.2
            ],
            "queryVectorMag": 5.25357
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  query: {
    script_score: {
      query: {
        bool: {
          filter: {
            term: {
              status: "published",
            },
          },
        },
      },
      script: {
        source:
          "\n          float[] v = doc['my_dense_vector'].vectorValue;\n          float vm = doc['my_dense_vector'].magnitude;\n          float dotProduct = 0;\n          for (int i = 0; i < v.length; i++) {\n            dotProduct += v[i] * params.queryVector[i];\n          }\n          return dotProduct / (vm * (float) params.queryVectorMag);\n        ",
        params: {
          queryVector: [4, 3.4, -0.2],
          queryVectorMag: 5.25357,
        },
      },
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published"
            }
          }
        }
      },
      "script": {
        "source": """
          float[] v = doc['my_dense_vector'].vectorValue;
          float vm = doc['my_dense_vector'].magnitude;
          float dotProduct = 0;
          for (int i = 0; i < v.length; i++) {
            dotProduct += v[i] * params.queryVector[i];
          }
          return dotProduct / (vm * (float) params.queryVectorMag);
        """,
        "params": {
          "queryVector": [4, 3.4, -0.2],
          "queryVectorMag": 5.25357
        }
      }
    }
  }
}
位向量和向量函数
编辑

当使用 bit 向量时,并非所有向量函数都可用。支持的函数有

  • hamming – 计算汉明距离,即两个向量的按位异或之和
  • l1norm – 计算 L1 距离,这只是 hamming 距离
  • l2norm - 计算 L2 距离,这是 hamming 距离的平方根
  • dotProduct – 计算点积。当比较两个 bit 向量时,这是两个向量的按位与之和。如果提供 float[]byte[],它具有 dims 个元素,作为查询向量,则 dotProduct 是使用存储的 bit 向量作为掩码的浮点值之和。

当比较 floatsbytesbit 向量时,bit 向量被视为大端顺序的掩码。例如,如果 bit 向量是 10100001 (例如,单字节值 161)并且将其与值数组 [1, 2, 3, 4, 5, 6, 7, 8] 进行比较,则 dotProduct 将为 1 + 3 + 8 = 16

这是一个使用位向量的点积的示例。

resp = client.indices.create(
    index="my-index-bit-vectors",
    mappings={
        "properties": {
            "my_dense_vector": {
                "type": "dense_vector",
                "index": False,
                "element_type": "bit",
                "dims": 40
            }
        }
    },
)
print(resp)

resp1 = client.index(
    index="my-index-bit-vectors",
    id="1",
    document={
        "my_dense_vector": [
            8,
            5,
            -15,
            1,
            -7
        ]
    },
)
print(resp1)

resp2 = client.index(
    index="my-index-bit-vectors",
    id="2",
    document={
        "my_dense_vector": [
            -1,
            115,
            -3,
            4,
            -128
        ]
    },
)
print(resp2)

resp3 = client.index(
    index="my-index-bit-vectors",
    id="3",
    document={
        "my_dense_vector": [
            2,
            18,
            -5,
            0,
            -124
        ]
    },
)
print(resp3)

resp4 = client.indices.refresh(
    index="my-index-bit-vectors",
)
print(resp4)
const response = await client.indices.create({
  index: "my-index-bit-vectors",
  mappings: {
    properties: {
      my_dense_vector: {
        type: "dense_vector",
        index: false,
        element_type: "bit",
        dims: 40,
      },
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "my-index-bit-vectors",
  id: 1,
  document: {
    my_dense_vector: [8, 5, -15, 1, -7],
  },
});
console.log(response1);

const response2 = await client.index({
  index: "my-index-bit-vectors",
  id: 2,
  document: {
    my_dense_vector: [-1, 115, -3, 4, -128],
  },
});
console.log(response2);

const response3 = await client.index({
  index: "my-index-bit-vectors",
  id: 3,
  document: {
    my_dense_vector: [2, 18, -5, 0, -124],
  },
});
console.log(response3);

const response4 = await client.indices.refresh({
  index: "my-index-bit-vectors",
});
console.log(response4);
PUT my-index-bit-vectors
{
  "mappings": {
    "properties": {
      "my_dense_vector": {
        "type": "dense_vector",
        "index": false,
        "element_type": "bit",
        "dims": 40 
      }
    }
  }
}

PUT my-index-bit-vectors/_doc/1
{
  "my_dense_vector": [8, 5, -15, 1, -7] 
}

PUT my-index-bit-vectors/_doc/2
{
  "my_dense_vector": [-1, 115, -3, 4, -128]
}

PUT my-index-bit-vectors/_doc/3
{
  "my_dense_vector": [2, 18, -5, 0, -124]
}

POST my-index-bit-vectors/_refresh

bit 向量的维度数或位数。

此向量表示 5 个字节,或 5 * 8 = 40 位,等于配置的维度数

resp = client.search(
    index="my-index-bit-vectors",
    query={
        "script_score": {
            "query": {
                "match_all": {}
            },
            "script": {
                "source": "dotProduct(params.query_vector, 'my_dense_vector')",
                "params": {
                    "query_vector": [
                        8,
                        5,
                        -15,
                        1,
                        -7
                    ]
                }
            }
        }
    },
)
print(resp)
const response = await client.search({
  index: "my-index-bit-vectors",
  query: {
    script_score: {
      query: {
        match_all: {},
      },
      script: {
        source: "dotProduct(params.query_vector, 'my_dense_vector')",
        params: {
          query_vector: [8, 5, -15, 1, -7],
        },
      },
    },
  },
});
console.log(response);
GET my-index-bit-vectors/_search
{
  "query": {
    "script_score": {
      "query" : {
        "match_all": {}
      },
      "script": {
        "source": "dotProduct(params.query_vector, 'my_dense_vector')",
        "params": {
          "query_vector": [8, 5, -15, 1, -7] 
        }
      }
    }
  }
}

此向量为 40 位,因此将使用存储的向量计算按位 & 运算。

resp = client.search(
    index="my-index-bit-vectors",
    query={
        "script_score": {
            "query": {
                "match_all": {}
            },
            "script": {
                "source": "dotProduct(params.query_vector, 'my_dense_vector')",
                "params": {
                    "query_vector": [
                        0.23,
                        1.45,
                        3.67,
                        4.89,
                        -0.56,
                        2.34,
                        3.21,
                        1.78,
                        -2.45,
                        0.98,
                        -0.12,
                        3.45,
                        4.56,
                        2.78,
                        1.23,
                        0.67,
                        3.89,
                        4.12,
                        -2.34,
                        1.56,
                        0.78,
                        3.21,
                        4.12,
                        2.45,
                        -1.67,
                        0.34,
                        -3.45,
                        4.56,
                        -2.78,
                        1.23,
                        -0.67,
                        3.89,
                        -4.34,
                        2.12,
                        -1.56,
                        0.78,
                        -3.21,
                        4.45,
                        2.12,
                        1.67
                    ]
                }
            }
        }
    },
)
print(resp)
const response = await client.search({
  index: "my-index-bit-vectors",
  query: {
    script_score: {
      query: {
        match_all: {},
      },
      script: {
        source: "dotProduct(params.query_vector, 'my_dense_vector')",
        params: {
          query_vector: [
            0.23, 1.45, 3.67, 4.89, -0.56, 2.34, 3.21, 1.78, -2.45, 0.98, -0.12,
            3.45, 4.56, 2.78, 1.23, 0.67, 3.89, 4.12, -2.34, 1.56, 0.78, 3.21,
            4.12, 2.45, -1.67, 0.34, -3.45, 4.56, -2.78, 1.23, -0.67, 3.89,
            -4.34, 2.12, -1.56, 0.78, -3.21, 4.45, 2.12, 1.67,
          ],
        },
      },
    },
  },
});
console.log(response);
GET my-index-bit-vectors/_search
{
  "query": {
    "script_score": {
      "query" : {
        "match_all": {}
      },
      "script": {
        "source": "dotProduct(params.query_vector, 'my_dense_vector')",
        "params": {
          "query_vector": [0.23, 1.45, 3.67, 4.89, -0.56, 2.34, 3.21, 1.78, -2.45, 0.98, -0.12, 3.45, 4.56, 2.78, 1.23, 0.67, 3.89, 4.12, -2.34, 1.56, 0.78, 3.21, 4.12, 2.45, -1.67, 0.34, -3.45, 4.56, -2.78, 1.23, -0.67, 3.89, -4.34, 2.12, -1.56, 0.78, -3.21, 4.45, 2.12, 1.67] 
        }
      }
    }
  }
}

此向量为 40 个单独的维度,因此将使用存储的 bit 向量作为掩码来求浮点值之和。

目前,cosineSimilarity 函数不支持 bit 向量。

解释请求

编辑

使用 解释请求可以解释分数的各个部分是如何计算的。script_score 查询可以通过设置 explanation 参数来添加自己的解释

resp = client.explain(
    index="my-index-000001",
    id="0",
    query={
        "script_score": {
            "query": {
                "match": {
                    "message": "elasticsearch"
                }
            },
            "script": {
                "source": "\n          long count = doc['count'].value;\n          double normalizedCount = count / 10;\n          if (explanation != null) {\n            explanation.set('normalized count = count / 10 = ' + count + ' / 10 = ' + normalizedCount);\n          }\n          return normalizedCount;\n        "
            }
        }
    },
)
print(resp)
response = client.explain(
  index: 'my-index-000001',
  id: 0,
  body: {
    query: {
      script_score: {
        query: {
          match: {
            message: 'elasticsearch'
          }
        },
        script: {
          source: "\n          long count = doc['count'].value;\n          double normalizedCount = count / 10;\n          if (explanation != nil) {\n            explanation.set('normalized count = count / 10 = ' + count + ' / 10 = ' + normalizedCount);\n          }\n          return normalizedCount;\n        "
        }
      }
    }
  }
)
puts response
const response = await client.explain({
  index: "my-index-000001",
  id: 0,
  query: {
    script_score: {
      query: {
        match: {
          message: "elasticsearch",
        },
      },
      script: {
        source:
          "\n          long count = doc['count'].value;\n          double normalizedCount = count / 10;\n          if (explanation != null) {\n            explanation.set('normalized count = count / 10 = ' + count + ' / 10 = ' + normalizedCount);\n          }\n          return normalizedCount;\n        ",
      },
    },
  },
});
console.log(response);
GET /my-index-000001/_explain/0
{
  "query": {
    "script_score": {
      "query": {
        "match": { "message": "elasticsearch" }
      },
      "script": {
        "source": """
          long count = doc['count'].value;
          double normalizedCount = count / 10;
          if (explanation != null) {
            explanation.set('normalized count = count / 10 = ' + count + ' / 10 = ' + normalizedCount);
          }
          return normalizedCount;
        """
      }
    }
  }
}

请注意,在正常的 _search 请求中使用时,explanation 将为 null,因此最好使用条件保护。