提问者:小点点

在ElasticSearch上索引聚合数据的最佳方式是什么


我有用户,我的用户有事件。每个事件作为每个事件发生的类型和日期。

举个例子

{
  id: 1,
  name: john,
  events: [{
    type: 'logged_in'
    date: "01/01/2016
  },{
    type: 'logged_in'
    date: "02/01/2016
  }{
    type: 'added_email'
    date: "02/05/2016
  }]
}

现在的问题是,我希望能够查询在特定时间范围内完成特定事件N次的用户

例如:哪些用户在1月1日16日至1月20日17日期间登录两次以上

我知道我可以使用聚合,但查询变得复杂,并且数百万个事件的性能下降。

我想知道是否有更好的方法来索引/查询这些数据?


共1个答案

匿名用户

表示此数据的明显方式是使用嵌套映射:

"id": {"type": "integer"},
"name": {"type": "keyword"},
"events": {
  "type": "nested",
  "properties": {
    "type": {"type": "keyword"},
    "date": {"type": "date"}
  }    

我想这就是你提到性能问题时所说的(嵌套查询和聚合很慢)。对于你所说的那种分析,我不认为你可以避免使用聚合,但我会“扁平化”数据以避免使用嵌套字段[1],改为每个记录一个文档,如下所示:

"id": {"type": "integer"},
"name": {"type": "keyword"},
"event_type": {"type": "keyword"},
"date": {"type": "date"}

然后做一个聚合,比如:

{
  "query": {"bool": {
    "filter": [
      {"match": {"event_type": "logged_in"}},
      {"range": {"date": {"gte": "2016-01-01", "lt": "2017-01-20"}}}
    }
  "aggs": {
    "terms": {
      "field": "name",
      "size": 50
    }
  }

您还可以在索引中聚合一些数据,以防您知道您永远不需要更细粒度的分析。例如:

"name": {"type": "keyword"},
"event_type": {"type": "keyword"},
"event_count": {"type": "integer"},
"date_bucket": {"type": "date"}

其中date_bucket表示日期存储桶的开始(比如如果你只关心完整的月份,那么1月份的每个事件都将进入“2017-01-01”的记录)。如果event_count已经存在,你可以使用upser更新的脚本来更新它,如果不存在,你可以创建一个新的文档。然后,你可以在术语聚合中使用event_count的求和聚合。只有在你事先知道你关心的颗粒度时,这才有意义。

[1]如果您还需要以不同的方式访问数据,您可能会考虑索引成两个索引,例如数据上的两个“视图”。基本上,除非您有无限的资源,或小数据集,或者您不太关心性能,否则您应该非常努力地避免嵌套字段。