【问题标题】:Elasticsearch: How to score query for range based on array with max/min valuesElasticsearch:如何根据具有最大值/最小值的数组对范围查询进行评分
【发布时间】:2016-07-24 16:33:25
【问题描述】:

我有许多包含 rate 属性的文档,该属性是一个包含可接受速率的最小/最大范围的数组。

{ "rate": [250, 700] }

我现在想执行提供另一个范围的查询,例如:

{
  "bool": {
     "must": [
       "range": {
         "rate": { "from": 100, "to": 500 }
       }
     ]
  }
}

这很好用,并且总是返回的值至少有一个在我想要的范围内提供的值。

但是,对于所有结果,分数都是相同的。值是否与文档上的值相同或者它只是达到几个数字的范围都没有关系。如下图:

{
  "_id": "one",
  "_score": 1",
  "_source": { "rate": [250,750] }
},
{
  "_id": "two",
  "_score": 1",
  "_source": { "rate": [200,350] }
},
{
  "_id": "three",
  "_score": 1",
  "_source": { "rate": [500,750] }
}

有什么方法可以改善范围搜索,提供另一个这样的范围?

【问题讨论】:

    标签: elasticsearch elasticsearch-query


    【解决方案1】:

    您要的是range,这隐含的是一个是或否的问题。甚至除了作为助推器之外的任何东西都对它进行评分实际上很奇怪(例如:如果它有,那么就提高分数,但如果它没有它,那没关系)。因此,range 查询往往最好在过滤器上下文中使用。

    "query": {
      "bool": {
        "filter": [
          {
            "range": {
              "rate": { "gte": 100, "lte": 500 }
            }
          }
        ]
      }
    }
    

    (语法假定为 ES 2.0)

    这并不能真正帮助您,但它是执行您正在执行的请求的更好方法。

    至于您的要求,您希望根据文档中的原始值进行加权。这less 直截了当,因为该值是一个数组,其值可能超出范围,它不是nested 对象,因此它始终被视为一个数组(这意味着您需要手动重新排除忽略的结果)。

    完全自定义评分需要脚本(本机或其他),这可以通过脚本评分轻松完成。

    无论该值是否与文档上的值相同,或者它只是达到几个数字的范围。

    我实际上不明白第一部分的含义:您希望单个匹配“称重”更少还是更多?与边缘的距离是否重要?只是匹配重要吗?

    我会假设匹配越多越好,无论它们在范围内的哪个位置:

    {
      "query": {
        "bool": {
          "must": {
            "function_score": {
              "functions": [
                {
                  "script_score": {
                    "script": {
                      "inline": "doc['rate'].values.findAll { it >= gte && it <= lte }.size()",
                      "lang": "groovy",
                      "params": {
                        "gte": 100,
                        "lte": 500
                      }
                    }
                  }
                }
              ],
              "boost_mode": "replace"
            }
          },
          "filter": [
            {
              "range": {
                "rate": {
                  "gte": 100,
                  "lte": 500
                }
              }
            }
          ]
        }
      }
    }
    

    你应该not be using inline Groovy scripts in production(改用基于文件的脚本),但上面的方法会起作用。

    【讨论】:

    • 谢谢@pickypg,当我问这个问题时,我实际上忘记了添加范围查询(现已修复)。不幸的是,我无法访问 groovy,因为我正在使用 AWS ES 服务。你会想到任何其他可以让它发挥作用的想法吗?如果有帮助,我可以对数据进行不同的建模,但我发现像 rate_fromrate_to 这样的单独值变得更加复杂。这个想法是,它与范围内的区域匹配得越多,得分越高(即,当提供 100,500 时,100,500 的输入将是 100% 匹配,而 400、700 则不是那么多。
    • 为什么不使用GroovyPainless 有没有等效的解决方案?
    • Groovy 在沙盒方面存在很多安全问题。通过允许内联脚本,您会为集群打开很多问题。此外,它们是即时编译(并缓存)的,如果从不重用脚本,这是一种浪费(使用脚本params 可以重用,就像 SQL 数据库中的存储过程一样)。 Painless 借鉴了 Groovy 的一些特性,但它主要是 Java 的一个子集。所以如果你能用 Java 做,你也可以用 Painless 做。这绝对可以使用 Java Streams 来完成。这假设您获得了流,但是:stream().filter(rate -&gt; rate &gt;= gte &amp;&amp; rate &lt;= lte).count();.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 2011-07-14
    • 2020-11-10
    • 1970-01-01
    • 1970-01-01
    • 2021-10-28
    • 1970-01-01
    相关资源
    最近更新 更多