Elasticsearch按下一个/上一个数组项过滤/聚合

时间:2018-04-03 11:27:45

标签: elasticsearch filter aggregation-framework bucket

假设有三个是这三个文档,需要编写一个 elasticsearch 查询,该查询获取一个项目名称参数,并返回该事件的下一个项目(按使用顺序计算)。

itemArray被定义为nestedObject,但不必嵌套。我在文档中丢失了一点。任何帮助将不胜感激。

数据示例:

doc-1

{
  "id" : 0
  "itemArray": [
     {
        "name":"X",
        "order" : 0
     },
     {
        "name":"Y",
        "order" : 1
     },
     {
        "name":"Z",
        "order" : 2
     }
  ]
}

doc-2

{
  "id" : 1
  "itemArray": [
     {
        "name":"X",
        "order" : 0
     },
     {
        "name":"Y",
        "order" : 1
     },
     {
        "name":"T",
        "order" : 2
     }
  ]
}

DOC-3

{
  "id" : 2
  "itemArray": [
     {
        "name":"X",
        "order" : 0
     },
     {
        "name":"Y",
        "order" : 1
     },
     {
        "name":"Z",
        "order" : 2
     }
  ]
}
输入“ X ”的

响应示例三个文档包含Y ;根据顺序在数组中 X 之后:

{
    "Y": 3
}
输入“ Y ”的

响应示例两个文档包含Z 一个文档包含T ;根据顺序在数组中 Y 之后:

{
    "Z": 2,
    "T": 1
}

ElasticSearch版本: 6.2

1 个答案:

答案 0 :(得分:0)

如果你稍微考虑denormalizing your data,这是非常可行的。

"数组中的下一个元素"聚合实施?

请考虑您的映射如下所示:

PUT nextval
{
  "mappings": {
    "item": {
      "properties": {
        "id": {
          "type": "long"
        },
        "itemArray": {
          "type": "nested",
          "properties": {
            "name": {
              "type": "keyword"
            },
            "nextName": {
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}

这里我们明确地存储数组的nested下一个值。现在让我们插入数据:

POST nextval/item/0
{
  "id" : 0,
  "itemArray": [
     {
        "name":"X",
        "nextName":"Y"
     },
     {
        "name":"Y",
        "nextName":"Z"
     },
     {
        "name":"Z"
     }
  ]
}

POST nextval/item/1
{
  "id" : 1,
  "itemArray": [
     {
        "name":"X",
        "nextName":"Y"
     },
     {
        "name":"Y",
        "nextName":"T"
     },
     {
        "name":"T"
     }
  ]
}

POST nextval/item/2
{
  "id" : 2,
  "itemArray": [
     {
        "name":"X",
        "nextName":"Y"
     },
     {
        "name":"Y",
        "nextName":"Z"
     },
     {
        "name":"Z"
     }
  ]
}

并使用这样的查询来获取输入X的结果:

POST nextval/item/_search
{
  "query": {
    "nested": {
      "path": "itemArray",
      "query": {
        "term": {
          "itemArray.name": "X"
        }
      }
    }
  },
  "aggs": {
    "1. setup nested": {
      "nested": {
        "path": "itemArray"
      },
      "aggs": {
        "2. filter agg results": {
          "filter": {
            "term": {
              "itemArray.name": "X"
            }
          },
          "aggs": {
            "3. aggregate by nextName": {
              "terms": {
                "field": "itemArray.nextName"
              }
            }
          }
        }
      }
    }
  }
}

输出将如下所示:

{
  ...,
  "aggregations": {
    "1. setup nested": {
      "doc_count": 9,
      "2. filter agg results": {
        "doc_count": 3,
        "3. aggregate by nextName": {
          "doc_count_error_upper_bound": 0,
          "sum_other_doc_count": 0,
          "buckets": [
            {
              "key": "Y",
              "doc_count": 3
            }
          ]
        }
      }
    }
  }
}

如果我们对输入Y进行查询,则输出将为:

{
  ...,
  "aggregations": {
    "1. setup nested": {
      "doc_count": 9,
      "2. filter agg results": {
        "doc_count": 3,
        "3. aggregate by nextName": {
          "doc_count_error_upper_bound": 0,
          "sum_other_doc_count": 0,
          "buckets": [
            {
              "key": "Z",
              "doc_count": 2
            },
            {
              "key": "T",
              "doc_count": 1
            }
          ]
        }
      }
    }
  }
}

它是如何工作的?

了解嵌套对象的一个​​重要事项是:

  

每个嵌套对象都被索引为隐藏的单独文档

我建议阅读指南的this page,它们提供了很好的解释和示例。

由于这些对象是分开的,我们会丢失有关它们在数组中的位置的信息。这就是你把order置于首位的原因。

这就是为什么我们将nextName字段放在嵌套对象中:所以对象本身知道哪个是它的邻居。

好的,但为什么聚合太复杂了?

让我们回顾一下。在我们的查询中,基本上有4个要点:

  1. itemArray.name==X
  2. 查询
  3. 1级聚合,nested
  4. 2级聚合,filter
  5. 3级聚合,terms
  6. 1)非常明显:我们只想要符合我们要求的文件。 2)也很简单:由于itemArraynested,我们只能在nested上下文中进行聚合。

    3)一个很棘手。让我们回到查询的输出:

    {
      ...,
      "aggregations": {
        "1. setup nested": {
          "doc_count": 9,
          "2. filter agg results": {
            "doc_count": 3,
            "3. aggregate by nextName": {
              "doc_count_error_upper_bound": 0,
              "sum_other_doc_count": 0,
              "buckets": [
                {
                  "key": "Z",
                  "doc_count": 2
                },
                {
                  "key": "T",
                  "doc_count": 1
                }
              ]
            }
          }
        }
      }
    }
    

    第一个聚合的doc_count是9.为什么9?因为这是我们在匹配搜索查询的文档中的嵌套对象的数量。

    这就是为什么我们需要3)聚合:从所有项目中只选择那些itemArray.name==X

    而且一个又简单:只计算每个字段itemArray.nextName满足的次数。

    有更好的方法吗?

    可能,是的。这取决于您的数据和您的需求以及您有多自由地更改映射。例如,如果您只是在探索数据,scripted aggregations的潜力巨大。

    希望有所帮助!