ElasticSearch分组聚合分页

 提示:转载请注明原文链接

 本文永久链接:https://360us.net/article/71.html

ElasticSearch分组聚合分页

类似SQL里面group by后分页输出列表。

这里需要分成两个步骤,一个是计算总数,第二个是输出列表。

数据场景:

展示一个车主列表,输入关键字,可以通过车主的姓名、车辆的车牌来搜索出对应的车主数据列表。

添加一个车辆索引:car,里面包括车主姓名:name,车主id:uid,车牌:car_no

因为车主和车辆是一对多的关系,所以我们在车辆索引里面搜索车主的时候需要对uid去重,这里就用到了聚合。

计算聚合桶的个数

方法就是用cardinality聚合和sum_bucket聚合结合起来计算。

{
	"size": 0,
	"query": {
		"bool": {
		    //这里用前缀搜索name和car_no
			"should": [
				{
					"prefix": {
						"name": "113566"
					}
				},
				{
					"prefix": {
						"car_no": "113566"
					}
				}
			],
			//should必须至少需要一个匹配,不设置这个,即使没有任何匹配也会返回数据
			"minimum_should_match": 1
		}
	},
	"aggs": {
		"uid": {
		    //这里是设置用uid去重,类似sql的group by
			"terms": {
				"field": "uid"
			},
			"aggs": {
			    //对当前桶(bucket)的uid计数,因为上面已经对id去重了,所以每个桶的uid数量都是1
				"uid_count": {
					"cardinality": {
						"field": "uid"
					}
				}
			}
		},
		//这里是Pipeline Aggregations的Sum Bucket Aggregation
		//对上面的uid_count求和
		"sun_uid": {
			"sum_bucket": {
				"buckets_path": "uid>uid_count"
			}
		}
	}
}

返回结果:

{
	"took": 2,
	"timed_out": false,
	"_shards": {
		"total": 1,
		"successful": 1,
		"skipped": 0,
		"failed": 0
	},
	"hits": {
		"total": {
			"value": 27,
			"relation": "eq"
		},
		"max_score": null,
		"hits": []
	},
	"aggregations": {
		"uid": {
			"doc_count_error_upper_bound": 0,
			"sum_other_doc_count": 0,
			"buckets": [
				{
					"key": 19,
					"doc_count": 7,
					"uid_count": {
						"value": 1
					}
				},
				{
					"key": 34,
					"doc_count": 7,
					"uid_count": {
						"value": 1
					}
				},
				{
					"key": 15,
					"doc_count": 5,
					"uid_count": {
						"value": 1
					}
				},
				{
					"key": 25,
					"doc_count": 3,
					"uid_count": {
						"value": 1
					}
				},
				{
					"key": 43,
					"doc_count": 2,
					"uid_count": {
						"value": 1
					}
				},
				{
					"key": 39,
					"doc_count": 1,
					"uid_count": {
						"value": 1
					}
				},
				{
					"key": 44,
					"doc_count": 1,
					"uid_count": {
						"value": 1
					}
				},
				{
					"key": 49,
					"doc_count": 1,
					"uid_count": {
						"value": 1
					}
				}
			]
		},
		"sun_uid": {
			"value": 8
		}
	}
}

上面的sun_uid.value就是桶的总数了。

获取分页后的数据列表

{
	"size": 0,
	"query": {
		"bool": {
			"should": [
				{
					"prefix": {
						"name": "11356"
					}
				},
				{
					"prefix": {
						"car_no": "11356"
					}
				}
			],
			"minimum_should_match": 1
		}
	},
	"aggs": {
		"uid": {
			"composite": {
				"size": 2,
				"sources": [
					{
						"uid": {
							"terms": {
								"field": "uid",
								"order": "desc"
							}
						}
					}
				],
				//"after": {
				//	"uid": 19
				//}
			}
		}
	}
}

关键字搜索那里和上面获取总数那里是没有区别的,区别是在聚合那里。

聚合是使用的composite

  • size:指定本次查询需要获取的数量
  • sources:里面指定用uid去重,根据uid排序。
  • after:指定获取数据的开始点,这里就是分页的关键了。不指定就是获取的是第一页的数据,如果指定上一页的最后一条数据,就可以获取下一页的数据。

假如sources里面指定了多字段,在after里面也是要相应的指定这些字段的值的,如下实例:

GET /_search
{
  "size": 0,
  "aggs": {
    "my_buckets": {
      "composite": {
        "size": 2,
        "sources": [
          { "date": { "date_histogram": { "field": "timestamp", "calendar_interval": "1d", "order": "desc" } } },
          { "product": { "terms": { "field": "product", "order": "asc" } } }
        ],
        "after": { "date": 1494288000000, "product": "mad max" } 
      }
    }
  }
}

数据查询结果会返回一个after_key,可以直接拿来放到到after参数里面获取下一页数据。

这里聚合拿到一页车主id数据之后,就可以到车主表里面去获取车主信息了

siezafter分页缺点就是只能上一页,下一页,不能跳页。

 评论
暂无评论