嵌套聚合

编辑

一个特殊的单桶聚合,可以对嵌套文档进行聚合。

例如,假设我们有一个产品索引,每个产品都包含经销商列表 - 每个经销商都有该产品的自己的价格。映射可能如下所示

resp = client.indices.create(
    index="products",
    mappings={
        "properties": {
            "resellers": {
                "type": "nested",
                "properties": {
                    "reseller": {
                        "type": "keyword"
                    },
                    "price": {
                        "type": "double"
                    }
                }
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'products',
  body: {
    mappings: {
      properties: {
        resellers: {
          type: 'nested',
          properties: {
            reseller: {
              type: 'keyword'
            },
            price: {
              type: 'double'
            }
          }
        }
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "products",
  mappings: {
    properties: {
      resellers: {
        type: "nested",
        properties: {
          reseller: {
            type: "keyword",
          },
          price: {
            type: "double",
          },
        },
      },
    },
  },
});
console.log(response);
PUT /products
{
  "mappings": {
    "properties": {
      "resellers": { 
        "type": "nested",
        "properties": {
          "reseller": {
            "type": "keyword"
          },
          "price": {
            "type": "double"
          }
        }
      }
    }
  }
}

resellers 是一个包含嵌套文档的数组。

以下请求添加了一个带有两个经销商的产品

resp = client.index(
    index="products",
    id="0",
    refresh=True,
    document={
        "name": "LED TV",
        "resellers": [
            {
                "reseller": "companyA",
                "price": 350
            },
            {
                "reseller": "companyB",
                "price": 500
            }
        ]
    },
)
print(resp)
response = client.index(
  index: 'products',
  id: 0,
  refresh: true,
  body: {
    name: 'LED TV',
    resellers: [
      {
        reseller: 'companyA',
        price: 350
      },
      {
        reseller: 'companyB',
        price: 500
      }
    ]
  }
)
puts response
const response = await client.index({
  index: "products",
  id: 0,
  refresh: "true",
  document: {
    name: "LED TV",
    resellers: [
      {
        reseller: "companyA",
        price: 350,
      },
      {
        reseller: "companyB",
        price: 500,
      },
    ],
  },
});
console.log(response);
PUT /products/_doc/0?refresh
{
  "name": "LED TV", 
  "resellers": [
    {
      "reseller": "companyA",
      "price": 350
    },
    {
      "reseller": "companyB",
      "price": 500
    }
  ]
}

我们正在对 name 属性使用动态映射。

以下请求返回产品可以购买的最低价格

resp = client.search(
    index="products",
    size="0",
    query={
        "match": {
            "name": "led tv"
        }
    },
    aggs={
        "resellers": {
            "nested": {
                "path": "resellers"
            },
            "aggs": {
                "min_price": {
                    "min": {
                        "field": "resellers.price"
                    }
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'products',
  size: 0,
  body: {
    query: {
      match: {
        name: 'led tv'
      }
    },
    aggregations: {
      resellers: {
        nested: {
          path: 'resellers'
        },
        aggregations: {
          min_price: {
            min: {
              field: 'resellers.price'
            }
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "products",
  size: 0,
  query: {
    match: {
      name: "led tv",
    },
  },
  aggs: {
    resellers: {
      nested: {
        path: "resellers",
      },
      aggs: {
        min_price: {
          min: {
            field: "resellers.price",
          },
        },
      },
    },
  },
});
console.log(response);
GET /products/_search?size=0
{
  "query": {
    "match": {
      "name": "led tv"
    }
  },
  "aggs": {
    "resellers": {
      "nested": {
        "path": "resellers"
      },
      "aggs": {
        "min_price": {
          "min": {
            "field": "resellers.price"
          }
        }
      }
    }
  }
}

正如您在上面看到的,嵌套聚合需要在顶级文档中嵌套文档的 path。然后,可以对这些嵌套文档定义任何类型的聚合。

响应

{
  ...
  "aggregations": {
    "resellers": {
      "doc_count": 2,
      "min_price": {
        "value": 350.0
      }
    }
  }
}

您可以使用 filter 子聚合来返回特定经销商的结果。

resp = client.search(
    index="products",
    size="0",
    query={
        "match": {
            "name": "led tv"
        }
    },
    aggs={
        "resellers": {
            "nested": {
                "path": "resellers"
            },
            "aggs": {
                "filter_reseller": {
                    "filter": {
                        "bool": {
                            "filter": [
                                {
                                    "term": {
                                        "resellers.reseller": "companyB"
                                    }
                                }
                            ]
                        }
                    },
                    "aggs": {
                        "min_price": {
                            "min": {
                                "field": "resellers.price"
                            }
                        }
                    }
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'products',
  size: 0,
  body: {
    query: {
      match: {
        name: 'led tv'
      }
    },
    aggregations: {
      resellers: {
        nested: {
          path: 'resellers'
        },
        aggregations: {
          filter_reseller: {
            filter: {
              bool: {
                filter: [
                  {
                    term: {
                      'resellers.reseller' => 'companyB'
                    }
                  }
                ]
              }
            },
            aggregations: {
              min_price: {
                min: {
                  field: 'resellers.price'
                }
              }
            }
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "products",
  size: 0,
  query: {
    match: {
      name: "led tv",
    },
  },
  aggs: {
    resellers: {
      nested: {
        path: "resellers",
      },
      aggs: {
        filter_reseller: {
          filter: {
            bool: {
              filter: [
                {
                  term: {
                    "resellers.reseller": "companyB",
                  },
                },
              ],
            },
          },
          aggs: {
            min_price: {
              min: {
                field: "resellers.price",
              },
            },
          },
        },
      },
    },
  },
});
console.log(response);
GET /products/_search?size=0
{
  "query": {
    "match": {
      "name": "led tv"
    }
  },
  "aggs": {
    "resellers": {
      "nested": {
        "path": "resellers"
      },
      "aggs": {
        "filter_reseller": {
          "filter": {
            "bool": {
              "filter": [
                {
                  "term": {
                    "resellers.reseller": "companyB"
                  }
                }
              ]
            }
          },
          "aggs": {
            "min_price": {
              "min": {
                "field": "resellers.price"
              }
            }
          }
        }
      }
    }
  }
}

搜索返回

{
  ...
  "aggregations": {
    "resellers": {
      "doc_count": 2,
      "filter_reseller": {
        "doc_count": 1,
        "min_price": {
          "value": 500.0
        }
      }
    }
  }
}