地图上的弹性搜索聚合 - 在每个键上

时间:2017-09-11 22:18:17

标签: elasticsearch

我有以下类型的文件。

文件1

myAvatarHandler = sendFile typePng "/home/yesod/static/avatars/day/userid.png"

文件2

{
  "doc": {
    "id": 1,
    "errors": {
      "e1":5,
      "e2":20,
      "e3":30
    },
    "warnings": {
      "w1":1,
      "w2":2
    }
  }
}

我想在一个或多个电话中获得以下总和统计数据。可能吗?我尝试了各种解决方案,但是当 key 已知时,一切正常。在我的情况下,地图(e1,e2等)是未知的。

{
  "doc": {
    "id": 2,
    "errors": {
      "e1":10
    },
    "warnings": {
      "w1":1,
      "w2":2,
      "w3":33,
    }
  }
}

1 个答案:

答案 0 :(得分:3)

有两种解决方案,它们都不漂亮。我必须指出,选项2应该是首选方式,因为选项1使用实验性功能。

1。动态映射,[实验]脚本聚合

this answer和ES文档的Scripted Metric Aggregation页面的启发,我开始只将文档插入到不存在的索引(默认情况下会创建dynamic mapping)。

NB :我在ES 5.4上对此进行了测试,但文档显示该功能至少可以从2.0开始。

生成的聚合查询如下:

POST /my_index/my_type/_search
{
    "size": 0,
    "query" : {
        "match_all" : {}
    },
    "aggs": {
        "errors": {
            "scripted_metric": {
                "init_script" : "params._agg.errors = [:]",
                "map_script" : "for (t in params['_source']['doc']['errors'].entrySet()) { params._agg.errors[t.key] = t.value } ",
                "combine_script" : "return params._agg.errors",
                "reduce_script": "Map res = [:] ; for (a in params._aggs) { for (t in a.entrySet()) { res[t.key] = res.containsKey(t.key) ? res[t.key] + t.value : t.value } }  return res"

            }
        },
        "warnings": {
            "scripted_metric": {
                "init_script" : "params._agg.errors = [:]",
                "map_script" : "for (t in params['_source']['doc']['warnings'].entrySet()) { params._agg.errors[t.key] = t.value } ",
                "combine_script" : "return params._agg.errors",
                "reduce_script": "Map res = [:] ; for (a in params._aggs) { for (t in a.entrySet()) { res[t.key] = res.containsKey(t.key) ? res[t.key] + t.value : t.value } }  return res"

            }
        }
    }
}

产生此输出:

{
   ...
   "aggregations": {
      "warnings": {
         "value": {
            "w1": 2,
            "w2": 4,
            "w3": 33
         }
      },
      "errors": {
         "value": {
            "e1": 15,
            "e2": 20,
            "e3": 30
         }
      }
   }
}

如果您遵循此路径,您可能会对params['_source']下面的JavaDoc感兴趣。

警告:我认为脚本聚合效率不高,为了获得更好的性能,您应该查看选项2或其他数据处理引擎。

experimental意味着什么:

  

此功能属于实验性功能,可能会更改或删除   完全在未来的版本中。弹性将尽最大努力   解决任何问题的方法,但实验性功能不是主题   支持官方GA功能的SLA。

考虑到这一点,我们继续选择2。

2。静态嵌套映射,嵌套聚合

这里的想法是以不同方式存储您的数据,并且基本上能够以不同方式查询和聚合它。首先,我们需要使用nested data type创建映射。

PUT /my_index_nested/
{
    "mappings": {
        "my_type": {
            "properties": {
                "errors": { 
                    "type": "nested",
                    "properties": {
                        "name": {"type": "keyword"},
                        "val": {"type": "integer"}
                    }
                },
                "warnings": { 
                    "type": "nested",
                    "properties": {
                        "name": {"type": "keyword"},
                        "val": {"type": "integer"}
                    }
                }
            }
        }
    }
}

此类索引中的文档如下所示:

     {
        "_index": "my_index_nested",
        "_type": "my_type",
        "_id": "1",
        "_score": 1,
        "_source": {
           "errors": [
              {
                 "name": "e1",
                 "val": 5
              },
              {
                 "name": "e2",
                 "val": 20
              },
              {
                 "name": "e3",
                 "val": 30
              }
           ],
           "warnings": [
              {
                 "name": "w1",
                 "val": 1
              },
              {
                 "name": "w2",
                 "val": 2
              }
           ]
        }
     }

接下来我们需要编写聚合查询。首先,我们需要使用nested aggregation,这将允许我们查询此特殊nested数据类型。但由于我们实际上想要按name进行汇总,并将val的值相加,我们需要进行sub-aggregation

结果查询如下(为了清楚起见,我在查询旁添加注释):

POST /my_index_nested/my_type/_search
{
    "size": 0,
    "aggs": {
        "errors_top": {
            "nested": {
                // declare which nested objects we want to work with
                "path": "errors"  
            },
            "aggs": {
                "errors": {
                    // what we are aggregating - different values of name
                    "terms": {"field": "errors.name"}, 
                    // sub aggregation
                    "aggs": { 
                        "error_sum": {
                            // sum all val for same name
                            "sum": {"field": "errors.val"} 
                        }

                    }
                }
            }
        },
        "warnings_top": {
            // analogous to errors
        }
    }
}

此查询的输出将如下:

{
   ...
   "aggregations": {
      "errors_top": {
         "doc_count": 4,
         "errors": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
               {
                  "key": "e1",
                  "doc_count": 2,
                  "error_sum": {
                     "value": 15
                  }
               },
               {
                  "key": "e2",
                  "doc_count": 1,
                  "error_sum": {
                     "value": 20
                  }
               },
               {
                  "key": "e3",
                  "doc_count": 1,
                  "error_sum": {
                     "value": 30
                  }
               }
            ]
         }
      },
      "warnings_top": {
         ...
      }
   }
}

希望有所帮助。