我有用户,我的用户有事件。每个事件作为每个事件发生的类型和日期。
举个例子
{
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日期间登录两次以上
我知道我可以使用聚合,但查询变得复杂,并且数百万个事件的性能下降。
我想知道是否有更好的方法来索引/查询这些数据?
表示此数据的明显方式是使用嵌套映射:
"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]如果您还需要以不同的方式访问数据,您可能会考虑索引成两个索引,例如数据上的两个“视图”。基本上,除非您有无限的资源,或小数据集,或者您不太关心性能,否则您应该非常努力地避免嵌套字段。