过滤/排序嵌套数组中包含的两个值之间的差异(仅使用脚本过滤器和doc值)

时间:2017-03-15 18:12:36

标签: elasticsearch

使用用例更容易说明我的问题所以让我们从弹性搜索指南中获取example

这列出了一个产品。每个产品都有一个嵌套数组,其中包含销售该产品的经销商:

    {
        ...

        "product" : {
            "properties" : {
                "resellers" : { 
                    "type" : "nested",
                    "properties" : {
                        "name" : { "type" : "text" },
                        "price" : { "type" : "double" }
                    }
                }
            }
        }
    }

如果可能的话,我该如何做?

  • 过滤所有storeA比storeB便宜的产品。例如:product.resellers[name=storeA].price < product.resellers[name=storeB].price
  • 按商店A和商店B的价格差异订购产品

这可能需要分别使用脚本过滤器和订单过滤器,但不确定如何进行此操作。此外,经常使用这些类型的查询,因此性能很重要。因此,我可能需要坚持使用docValues而不是诉诸_source。这可能吗?

2 个答案:

答案 0 :(得分:1)

是的,这绝对是可能的,你可以这样做:

{
  "sort": {
    "_script": {
      "type": "number",
      "script": {
        "inline": "def store1 = _source.resellers.find{it.name == store1}; def store2 = _source.resellers.find{it.name == store2}; (store1 != null && store2 != null) ? store1.price - store2.price : 0",
        "lang": "groovy",
        "params": {
          "store1": "storeA",
          "store2": "storeB"
        }
      },
      "order": "asc"
    }
  },
  "query": {
    "bool": {
      "filter": [
        {
          "script": {
            "script": {
              "inline": "def store1 = _source.resellers.find{it.name == store1}; def store2 = _source.resellers.find{it.name == store2}; (store1 != null && store2 != null) ? store1.price < store2.price : false",
              "lang": "groovy",
              "params": {
                "store1": "storeA",
                "store2": "storeB"
              }
            }
          }
        }
      ]
    }
  }
}

排序脚本如下所示:

def store1 = _source.resellers.find{it.name == store1}; 
def store2 = _source.resellers.find{it.name == store2}; 
(store1 != null && store2 != null) ? store1.price - store2.price : 0

过滤器脚本有点类似,如下所示:

def store1 = _source.resellers.find{it.name == store1}; 
def store2 = _source.resellers.find{it.name == store2}; 
(store1 != null && store2 != null) ? store1.price < store2.price : false

两个脚本都在输入中使用两个参数,即您要比较的经销商商店的名称。

<强>更新

不知何故,我忘了解释为什么不能用doc值来做这件事。 Doc值实际上是倒排索引的倒数,即向每个文档映射存在于该文档内的令牌。这与嵌套文档作为独立(但隐藏)文档存储在索引中的事实相结合,文档的doc值如下所示

{
  "id": 1,
  "product": "Water",
  "resellers": [
    {
      "name": "storeA",
      "price": 20
    },
    {
      "name": "storeB",
      "price": 30
    }
  ]
}

看起来像这样:

Document        | Values
----------------+---------------------------
1  (top-level)  | water
1a (1st nested} | storea, 20
1b (2nd nested} | storeb, 30

查看上表,并且由于脚本是在每个文档的上下文中执行的(无论是顶层还是嵌套),因此当访问脚本中的doc值时,只会产生该文档的值,因此无法将它们与另一个文档中的值进行比较。

访问源代码时,我们会有效地迭代resellers数组,因此可以比较它们之间的值,并产生在您的上下文中有用的内容。

答案 1 :(得分:0)

这看起来像是一个市场问题。所以我会根据他们的主产品ID分离产品 - 所以产品可以有不同的描述,属性等 - 并添加优先级进行排序和过滤。

 {
        ...

        "product" : {
            "properties" : {
                "masterProduct" : "int",
                "priority" : "int",
                "resellers" : { 

                    "type" : "nested",
                    "properties" : {
                        "name" : { "type" : "text" },
                        "price" : { "type" : "double" }
                    }
                }
            }
        }
    }

让我解释一下; 首先,

product.resellers [name = storeA] .price&lt; roduct.resellers [名称= storeB]。价格

我想这个问题引发了,因为你想在搜索结果中显示最便宜的产品。因此,我认为在索引产品时,您应该拥有所有经销商的价格。

如果你知道最便宜的索引,那么它的优先级就像1那样的正数。并且将其他产品乘以-1,这样你就可以在产品细节中对它们进行分类,价格昂贵。 这解决了第二个问题(按商店A和商店B的价格之间的差异订购产品)。

毕竟你的指数中有正面优先级和负面优先级。所有你可以优先制作过滤器&gt; 0为您提供最便宜的产品。因此,如果任何经销商希望成为搜索结果的顶端或者宣传自己,那么优先级可以通过增加优先级来实现。