Elasticsearch filter/aggregation by next/previous 数组项

Elasticsearch filter/aggregation by next/previous array item

假设三个是这三个文档,需要编写一个 elasticsearch 查询,该查询获取项目名称参数和 returns 下一个项目(使用顺序计算)它的出现。

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

稍微考虑一下denormalizing your data还是比较可行的

如何实现"next element in array"聚合?

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

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
            }
          ]
        }
      }
    }
  }
}

它是如何工作的?

关于嵌套对象需要了解的一件重要事情是:

each nested object is indexed as a hidden separate document

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

由于这些对象是分开的,我们失去了关于它们在数组中的位置的信息。这就是您首先将 order 放在那里的原因。

这就是我们将 nextName 字段放在嵌套对象中的原因:因此对象本身知道哪个是它的邻居。

好的,但为什么聚合如此复杂?

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

  1. itemArray.name==X
  2. 查询
  3. 1级聚合,nested
  4. 2级聚合,filter
  5. 3级聚合,terms

1) 很明显:我们只需要符合我们要求的文件。 2) 也很简单:因为 itemArraynested,我们只能在 nested 上下文中进行聚合。

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

{
  ...,
  "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) 聚合的原因:从所有项目 select 中,只有那些具有 itemArray.name==X.

的项目

而 4) 也很简单:只需计算每个字段 itemArray.nextName 被满足的次数。

有没有更好的方法?

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

希望对您有所帮助!