最近在开发过程中需要用Java将从ES(ElasticSearch)查询中的聚合结果进行解析,考虑到聚合结果的JSONObject虽然会根据DSL的不同有所区别,但还是具有一定的特殊规则,因此写了一个较为公用的解析方法。
在这里遇到的实际业务场景是对一些客户信息进行查询,然后按照CUST_ID进行分桶,分桶后又对每个桶进一步的过滤和计算度量值,ES的返回结果最后封装成JSONObject。
注:ES的分桶和计算度量值类似于MySQL的group by后求sum,count等。
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"failed": 0
},
"hits": {
"total": 6,
"max_score": 2.9459102,
"hits": [
{
"_index": "gler-20190227190920",
"_type": "main",
"_id": "csdn_gler0001",
"_score": 2.9459102,
"_source": {
"OUT_SUM": "10000",
"ONWAY_STATE": "N",
"ALL_SUM": "10000",
"DATE": "2019-02-19",
"BUSI_CODE": "GLTEST",
"CUST_NAME": "XXX",
"IS_VALD ": "Y",
"C_SUM": "20000",
"G_ID": "XXXX ",
"ID": "test_gler0001",
"CUST_ID": "glertest"
}
},
{
……
}
]
},
"aggregations": {
"cust_data": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "glertest",
"doc_count": 4,
"all_count": {
"value": 4
},
"vald_data": {
"doc_count": 3,
"out_data ": {
"doc_count": 1,
"out_count": {
"value": 1
}
},
"vald_count": {
"value": 3
},
"out_sum": {
"value": 60000
},
"all_sum": {
"value": 70000
}
}
},
{
……
},
]
}
}
}
对于查询hit的记录是比较好处理的,只需要对于”hits”对象的”hits”数据依次进行解析,逐个获取”_source”的值即可。对于聚合结果“aggregations”对象,会根据DSL的写法返回不同格式的结果,但重要的聚合对象是固定的,如需要解析获得桶“buckets”下的信息,再获取桶下过滤后的度量值“value”,同时还需要获取分桶元素值CUST_ID,即每个桶的“key”。
/* 聚合结果解析 */
public static void parseESAggResult(getJSONObject qryResult) {
if (qryResult.containsKey("aggregations")) {
JSONObject aggs = qryResult.getJSONObject("aggregations");
/* 按CUST_ID分桶的数据 */
List esResults = esResults = getAggsBucketData("cust_data", aggs);
/* 由于可能有多层桶,需要根据实际需求转为Map再获取想要的字段或直接获取字段 */
for (JSONObject innerJo : esResults) {
innerJo.get("cust_data-key");
innerJo.get("all_count ");
......
}
}
}
// 公用解析代码
/**
* 遍历Buckets
*
* @param bucketName 需要解析的聚合名
* @param jo
* @return
*/
public static List getAggsBucketData(String bucketName, JSONObject jo) {
List results = new ArrayList();
if (jo.containsKey(bucketName)) {
JSONObject bucketAgg = jo.getJSONObject(bucketName);
/* 获取桶中的bucket数组 */
JSONArray buckets = bucketAgg.getJSONArray("buckets");
/* 遍历每个数组对象 */
for (Object object : buckets) {
JSONObject innerJo = (JSONObject) object;
JSONObject result = new JSONObject();
parseJSON(innerJo, bucketName, result);
results.add(result);
}
}
return results;
}
/**
* 解析JSONObject,返回桶bucket的value和度量值
*
* @param jo
* @param fieldName
* @param result
*/
public static void parseJSON(JSONObject jo, String fieldName, JSONObject result) {
for (String key : jo.keySet()) {
/* JSONObject,继续往底层解析 */
if (jo.get(key) instanceof JSONObject) {
/* 有桶buckets */
if (jo.getJSONObject(key).get("buckets") != null) {
JSONObject bucketJo = new JSONObject();
bucketJo.put(key, jo.getJSONObject(key));
result.put(key, getAggsBucketData(key, bucketJo));
} else {
parseJSON(jo.getJSONObject(key), key, result);
}
} else {
/* 度量值 */
if ("value".equals(key)) {
result.put(fieldName, jo.get(key));
} else if ("key".equals(key)) {
/* 桶的key值 */
result.put(fieldName + "-" + key, jo.get(key));
}
}
}
}