分组后如何处理大型集合聚合?

Posted

技术标签:

【中文标题】分组后如何处理大型集合聚合?【英文标题】:How to handle large collection aggregations after grouping? 【发布时间】:2020-04-23 10:03:01 【问题描述】:

我的集合存储了我们系统中的每次点击,点击文档看起来像(简化)-

        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public string _id  get; set; 
        public string VisitorGuid  get; set; 
        public int AccountId  get; set; 

所以_id 是每次点击自动生成的唯一标识符。 VisitorGuid 是为每个唯一客户(基于 Cookie)生成的 guid,因此如果客户单击链接两次,它将创建两个具有不同 _id 但相同 VisitorGuid 的文档。

我的实际问题 - 在这个集合上,我正在做统计,所以使用上面的例子,我生成了一个关于每个帐户有多少访问者和多少点击的报告。 为了实现我的查询有 2 个组阶段,第一个由 VisitorGuid 分组,并投影一个新文档,其中包含特定 VisitorGuid 的点击量,这样最后我知道我总共有多少点击(总结访问者的点击次数)以及我有多少访问者。 (IRL 我按多个字段分组,不仅是 accountId)

我的问题是,在第一次分组后 Mongo 无法使用索引,并且该查询可能需要几秒钟,具体取决于我过滤的日期范围。该集合目前拥有大约 500 万个文档,最让我害怕的是它需要能够容纳更多,我不确定它将如何处理这样的查询。

查询示例:

            // Use the dates to create ObjectId type for comparison
            var startId = new ObjectId(request.FromDate.UtcDateTime, 0, 0, 0).ToString();
            var endId = new ObjectId(request.ToDate.UtcDateTime, 0, 0, 0).ToString();

            //filter
            var builder = Builders<Click>.Filter;
            var clickFilters = builder.Gte(c => c._id, startId)
                & builder.Lte(c => c._id, endId);

            var aggregateOptions = new AggregateOptions  AllowDiskUse = true ;
            //aggregate clicks -
            var clicksAggregationTask = Task.Run(() => collection.Aggregate(aggregateOptions)
                .Match(clickFilters)
                //group into visitors
                .Group(c => c.VisitorGuid, g => new Result()
                
                    AccountId = g.First().AccountId,
                    ClickCount = g.Count()
                )
                //statistics
                .Group(
                c => new GroupByResult()
                
                    AccountId = c.AccountId,
                ,
                g => new Res()
                
                    AccountId = g.First().AccountId,
                    ClickCount = g.Sum(group => group.ClickCount),
                    VisitorCount = g.Count()
                )
                .ToListAsync());

【问题讨论】:

【参考方案1】:

当数据在查询时间之前已知时应用索引(索引需要从数据中构建)。当在聚合查询中生成数据时,它不会使用“集合索引”这个词的索引。也就是说,某些内存中的操作原则上可以利用像hash joins 这样的算法(我不知道这具体如何应用于 MongoDB),这可以被认为是“索引”的不同含义。

对于聚合管道,我通常所说的想法是尽可能地将数据从一个阶段流式传输到下一个阶段。因此,在某些情况下,您可能需要调整查询以进行这种流式传输。

您可以通过创建这样一个更大的数据集来测试性能或更大的数据集。

【讨论】:

以上是关于分组后如何处理大型集合聚合?的主要内容,如果未能解决你的问题,请参考以下文章

使用mongodb在utc中存储日期时如何处理时区问题?

表单关闭后如何处理非托管资源? [复制]

从 QIODevice.read() 读取后如何处理数据?

Spring Security登录成功后如何处理用户信息

reCAPTCHA V3:空闲后如何处理过期令牌?

用户选择允许后如何处理启用位置服务?