Elasticsearch:distance feature 查询 - 对靠近位置或时间点的文档提高相关性

提高文档的相关性分数,使其更接近提供的 orgin 日期或地理位置。 例如,你可以使用此查询为更接近某个日期或位置的文档赋予更大的权重。你可以使用 distance_feature 查询来找到某个位置最近的邻居。 你还可以在 bool query 的 “should” 过滤器中使用此查询,以将增强的相关性得分添加到 bool query 的得分中。

 

为什么使用地理距离或时间作为排名?

考虑搜索一家餐馆。通常使用一种标准,包括类型,价格范围,以及地理位置靠近度(proximity)。在所有其他条件保持不变的情况下,你最好选择最近的餐馆。这不是唯一的 - 在无数其他相关性排名情况下(例如,在寻找工作时,约会,水管工,遛狗,寻找保姆,旅游胜地。实际上,对地理位置所影响的任何事物(几乎是物理世界中存在的任何事物)的相关性排名都将从与地理距离有关的内容中受益。

类似的情况发生在与时间相关的搜索中,例如在搜索学术文章,报纸文章,博客文章或产品评论时,通常会选择最近的记录而不是时间比较长的历史记录(假设所有其他方面都相同)。就像在物理上接近在现实世界中具有物理位置的事物中搜索一样,新近度将是搜索信息中的典型偏好。换句话说,几乎所有相关性排名标准都将受益于日期邻近度或地理邻近度。

我们经常看到以下两种方式使用的日期和地理位置接近度:

  • 作为过滤器,即二分法标准,例如仅检索比 x 更近或最近 y 天之内的记录,并按其他搜索条件对这些记录进行排名(例如,对文本使用 BM25
  • 作为唯一的排名标准,例如按地理位置或日期接近程度对所有符合二分类标准的记录进行排序

换句话说,接近度或者被用作检索标准,而不是用于相关性等级,或者被用作相关性等级的单个标准。

问题在于,用户经常愿意走更长的距离来寻找排名更高的餐厅,或者等待更长的时间来寻找排名更高的保姆,或者即使不是最近阅读的文章,也经常阅读引用最多的文章。换句话说,我们作为搜索者的实际搜索标准将接近度(空间和时间)与其他标准(例如与词频相关的标准)组合到单个相关性评分中。

 

你无法使用 “Function Score” 或 “Painless script” 来做到这一点吗?

当人们在设计,调整和编程搜索算法时,选择了另一种方法,我们经常避免把接近度和词频(或其他数字)排名标准揉在一起,因为这在技术上具有挑战性。存在两个主要挑战:

  • 归一化(normalization)- 必须对接近度评分进行归一化,以便将其与其他相关度得分(例如 BM25)相加时具有正确的影响。 Lucene 的默认设置是将各种分数简单地求和为一个数,并且在大多数情况下,只要先将不同分数标准化即可。但是,归一化本身并不简单。
  • 性能(Performance)- 邻近程度重新排名会造成重大的性能损失。为了解决这个问题,更老练的 Elastic 用户经常形成一个不包括接近度的条件,以选择一小部分最相关的记录,然后在考虑到接近度后仅对这些记录进行排名。这种折衷意味着算法在考虑邻近度后将不会考虑可能成为最相关结果的结果。

Elastic 希望为用户提供非凡的算法挑战,因此在 Elasticsearch 发布的版本中,Elasticsearch 将使用户能够结合 “正常” 排名得分和归一化的接近度(时间或空间)得分对所有记录进行排名。

那么,你可以使用 “Function Score” 或 “Painless script” 为排名算法增加接近度吗?这取决于。当前可以通过 Function score 和 Painless 与接近度进行关联并对其进行归一化,但只能在结果集的一小部分上进行,并且会带来巨大的性能损失。有时,这些数量上的差异会导致质量上的差异:对于 distance_feature 查询,按接近度进行排序并与其他相关性排名算法结合使用,在许多情况下成为合理的原则排名机制。

下面,我们将使用一个具体的例子来进行展示。

 

准备数据

在今天的练习中,我来准备一些数据。尽管在之前的文章 “Elasticsearch:运用 distance feature 查询来增强相关性”,我已经做过一个例子。我觉得展示的时候,不一定要很多的数据。在某些的情况下使用较少的数据反而能够使得我们更容易看清是咋回事。我们首先定义如下的 mapping:

PUT items
{
  "mappings": {
    "properties": {
      "name": {
        "type": "keyword"
      },
      "production_date": {
        "type": "date"
      },
      "location": {
        "type": "geo_point"
      }
    }
  }
}

我们接着导入如下的数据:

POST items/_bulk
{"index":{"_id":"1"}}
{"name":"chocolate","production_date":"2021-02-01","location":[-73.88929,41.98284]}
{"index":{"_id":"2"}}
{"name":"chocolate","production_date":"2021-01-01","location":[-73.41654,41.78798]}
{"index":{"_id":"3"}}
{"name":"chocolate","production_date":"2020-12-01","location":[-73.13655,42.41272]}

在上面,我们导入三个数据。它们的地理位置分布如下:

在上面,我们导入的三个数据的位置相对关系显示如上。同时,我们可以看到一个 origin 的位置。

 

根据位置提升文档

以下 bool query 将返回名称为 Chocolate 的文档。 该搜索还使用 distance_feature 查询来增加位置值接近 [-73.49803, 41.94219] (在上面的 origin 中显示的位置)的文档的相关性得分。

GET items/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "name": "chocolate"
        }
      },
      "should": {
        "distance_feature": {
          "field": "location",
          "pivot": "1000m",
          "origin": [-73.49803, 41.94219]
        }
      }
    }
  }
}

上面返回的结果为:

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 0.18500501,
    "hits" : [
      {
        "_index" : "items",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.18500501,
        "_source" : {
          "name" : "chocolate",
          "production_date" : "2021-01-01",
          "location" : [
            -73.41654,
            41.78798
          ]
        }
      },
      {
        "_index" : "items",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.16323614,
        "_source" : {
          "name" : "chocolate",
          "production_date" : "2021-02-01",
          "location" : [
            -73.88929,
            41.98284
          ]
        }
      },
      {
        "_index" : "items",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 0.1498698,
        "_source" : {
          "name" : "chocolate",
          "production_date" : "2020-12-01",
          "location" : [
            -73.13655,
            42.41272
          ]
        }
      }
    ]
  }
}

从上面可以看出来,文档的排序为 2,1,3。也就是说 _id 为 2 的文档的排名明显高于其它的两个文档。我们甚至可以添加 boost 参数:

GET items/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "name": "chocolate"
        }
      },
      "should": {
        "distance_feature": {
          "field": "location",
          "pivot": "1000m",
          "origin": [-73.49803, 41.94219],
          "boost": 2.0
        }
      }
    }
  }
}

 

根据日期提升文档

以下 bool query 将返回名称为 Chocolate 的文档。 该搜索还使用 distance_feature 查询来增加具有接近于 production_date 值的文档的相关性得分。

GET items/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "name": "chocolate"
        }
      },
      "should": {
        "distance_feature": {
          "field": "production_date",
          "pivot": "100d",
          "origin": "now"
        }
      }
    }
  }
}

上面的返回结果:

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 0.6946931,
    "hits" : [
      {
        "_index" : "items",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.6946931,
        "_source" : {
          "name" : "chocolate",
          "production_date" : "2021-02-01",
          "location" : [
            -73.88929,
            41.98284
          ]
        }
      },
      {
        "_index" : "items",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.6115388,
        "_source" : {
          "name" : "chocolate",
          "production_date" : "2021-01-01",
          "location" : [
            -73.41654,
            41.78798
          ]
        }
      },
      {
        "_index" : "items",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 0.5498481,
        "_source" : {
          "name" : "chocolate",
          "production_date" : "2020-12-01",
          "location" : [
            -73.13655,
            42.41272
          ]
        }
      }
    ]
  }
}

从上面的结果可以看出来,2021-02-01 是离现在最近的文档。得分最高,2020年的文档是离今天 2021-04-20 最远的时间。排名最后。 

如果你想了解分数是如何计算的,你可以添加 explained: true:

GET items/_search
{
  "explain": true, 
  "query": {
    "bool": {
      "must": {
        "match": {
          "name": "chocolate"
        }
      },
      "should": {
        "distance_feature": {
          "field": "production_date",
          "pivot": "100d",
          "origin": "now"
        }
      }
    }
  }
}

参考:

【1】 https://gist.github.com/spinscale/c3508e19bf4874581f70f44f84d28542

已标记关键词 清除标记
相关推荐