将反向嵌套排序回父聚合

Sorting a reverse nested back to parent aggregation

我目前正在通过多级嵌套字段聚合一个集合,并从该集合中计算一些子聚合指标,这正在使用 elasticsearch 的反向嵌套功能进行工作,如 所述。

我目前的努力是找到一种方法来根据其中一个计算指标对聚合进行排序。例如,考虑以下文档和我当前的搜索调用,我想按点击总和对所有聚合进行排序。

我尝试在 back_to_parent 级别的内部 aggs 中使用 bucket_sort,但出现以下 java 异常。

class org.elasticsearch.search.aggregations.bucket.nested.InternalReverseNested cannot be cast to class org.elasticsearch.search.aggregations.InternalMultiBucketAggregation 
(org.elasticsearch.search.aggregations.bucket.nested.InternalReverseNested and org.elasticsearch.search.aggregations.InternalMultiBucketAggregation are in unnamed module of loader 'app')
{
  id: '32ead132eq13w21',
  statistics: {
    clicks: 123,
    views: 456
  },
  categories: [{ //nested type
    name: 'color',
    tags: [{ //nested type
      slug: 'blue'
    },{
      slug: 'red'
    }]
  }]
}
GET /acounts-123321/_search
{
  size: 0,
  aggs: {
    categories_parent: {
        nested: {
          path: 'categories.tags'
        },
        aggs: {
          filtered: {
            filter: {
              term: { 'categories.tags.category': 'color' }
            },
            aggs: {
              by_slug: {
                terms: {
                  field: 'categories.tags.slug',
                  size: perPage
                },
                aggs: {
                  back_to_parent: {
                    reverse_nested: {},
                    aggs: {
                      clicks: {
                        sum: {
                          field: 'statistics.clicks'
                        }
                      },
                      custom_metric: {
                        scripted_metric: {
                          init_script: 'state.accounts = []',
                          map_script: 'state.accounts.add(new HashMap(params["_source"]))',
                          combine_script: 'double result = 0;
                            for (acc in state.accounts) {
                              result += ( acc.statistics.clicks + acc.statistics.impressions);
                            }
                            return result;',
                          reduce_script: 'double sum = 0;
                            for (state in states) {
                              sum += state;
                            }                            
                            return sum;'
                        }
                      },
                      by_tag_sort: {
                        bucket_sort: {
                          sort: [{ 'clicks.value': { order: 'desc' } }]
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

更新:

了解如何通过无痛 scripted_metric 计算的自定义指标对存储桶进行排序也很好。我更新了上面的搜索调用,添加了一个示例 custom_metric,我希望允许对其进行排序。

我发现直接使用 bucket_sort 不适用于我们用于具体字段的标准排序数组。因此,以下内容似乎无法对事物进行排序。它也不适用于排序脚本,因为 [bucket_sort] only supports field based sorting.

by_tag_sort: {
  bucket_sort: {
    sort: [{ 'custom_metric.value': { order: 'desc' } }]
  }
}

bucket_sort 期望在 multi-bucket 上下文中是 运行 但你的 reverse_nested 聚合是 single-bucket(不管它是 [= multi-bucket terms 聚合的 27=]。

诀窍是使用 empty-ish filters aggregation 生成 multi-bucket 上下文,然后 运行 桶排序:

{
  "size": 0,
  "aggs": {
    "categories_parent": {
      "nested": {
        "path": "categories.tags"
      },
      "aggs": {
        "filtered": {
          "filter": {
            "term": {
              "categories.tags.category": "color"
            }
          },
          "aggs": {
            "by_slug": {
              "terms": {
                "field": "categories.tags.slug",
                "size": 10
              },
              "aggs": {
                "back_to_parent": {
                  "reverse_nested": {},
                  "aggs": {
                    "multi_bucket_emulator": {
                      "filters": {
                        "filters": {
                          "placeholder_match_all_query": {
                            "match_all": {}
                          }
                        }
                      },
                      "aggs": {
                        "clicks": {
                          "sum": {
                            "field": "statistics.clicks"
                          }
                        },
                        "by_tag_sort": {
                          "bucket_sort": {
                            "sort": [
                              {
                                "clicks.value": {
                                  "order": "desc"
                                }
                              }
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

更新:按自定义脚本指标值的结果排序

{
  "size": 0,
  "aggs": {
    "categories_parent": {
      "nested": {
        "path": "categories.tags"
      },
      "aggs": {
        "filtered": {
          "filter": {
            "term": {
              "categories.tags.category": "color"
            }
          },
          "aggs": {
            "by_slug": {
              "terms": {
                "field": "categories.tags.slug",
                "size": 10
              },
              "aggs": {
                "back_to_parent": {
                  "reverse_nested": {},
                  "aggs": {
                    "multi_bucket_emulator": {
                      "filters": {
                        "filters": {
                          "placeholder_match_all_query": {
                            "match_all": {}
                          }
                        }
                      },
                      "aggs": {
                        "clicks": {
                          "sum": {
                            "field": "statistics.clicks"
                          }
                        },
                        "custom_metric": {
                          "scripted_metric": {
                            "init_script": "state.accounts = []",
                            "map_script": """state.accounts.add(params["_source"])""",
                            "combine_script": """
                                double result = 0;
                                for (def acc : state.accounts) {
                                  result += ( acc.statistics.clicks + acc.statistics.impressions);
                                }
                                return result;
                            """,
                            "reduce_script": """
                              double sum = 0;
                              for (def state : states) {
                                sum += state;
                              }                            
                              return sum;
                            """
                          }
                        },
                        "by_tag_sort": {
                          "bucket_sort": {
                            "sort": [
                              {
                                "custom_metric.value": {
                                  "order": "desc"
                                }
                              }
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Joe - Elasticsearch 手册 - 我有一个与您的查询等效的查询(一个按自定义脚本指标的结果排序的查询),我希望对您的查询的响应看起来像像下面这样。

我注意到 bucket_sort 指定的排序不会应用于最上面的桶(即 by_slug.buckets),它们仍按默认 doc_count 顺序排序。这也可以通过将 custom_metric.value 顺序从 desc 更改为 asc 来验证,这对结果的顺序没有影响。

我对 bucket_sort 的理解表明,基于 custom_metric 的排序应用于向上一级的聚合,在本例中为 multi_bucket_emulator.buckets(但因为这是一个模拟器它没有实际的桶排序)。

是否可以根据 custom_metric 值对 by_slug.buckets 进行排序?

我正在使用 Elasticsearch v7.10。

非常感谢。

(很抱歉将这个问题作为答案发布;评论太长了。)

响应(近似值):

{
    "aggregations": {
        "categories_parent": {
            "filtered": {
                "by_slug": {
                    "buckets": [
                        {
                            "key": "xxxxxx",
                            "back_to_parent": {
                                "multi_bucket_emulator": {
                                    "buckets": {
                                        "placeholder_match_all_query": {
                                            "clicks": {
                                                "buckets": [
                                                    {
                                                        "key": 5.0,
                                                        "doc_count": 1
                                                    },
                                                    …
                                                ]
                                            },
                                            "custom_metric": {
                                                "value": 20.0
                                            }
                                        }
                                    }
                                }
                            }
                        },
                        …
                    ]
                }
            }
        }
    }
}