Mongo如何使用group by查询去重?去重后如何查询重复记录里的最新第一条?
有时候我们碰到的需求稀奇古怪,真正实现起来特别的绕。
比如维权中心的红黑榜赞踩的流水。
需求是这样的:
查询50条,某一期的所有平台下的赞踩流水记录。
需求看起来很简单,那么产品又补刀了。
查询50条,某一期的所有平台下的赞踩流水记录。且每个用户只显示一条最新的记录。
首先如果只是查询50条流水,那很简单不过了。绕就绕在每个用户只显示一条最新的记录。而且我们是使用的Mongo。
我们能确定的是无法一次性查询出来。
第一步:
我们需要使用group by查询所有的去重记录
var stages = new List<BsonDocument>();if (type > 0){stages.Add(new BsonDocument {{ "$match", new BsonDocument {{ "PeriodsId", periodsId },{ "Type", type }}}});}else{stages.Add(new BsonDocument {{ "$match", new BsonDocument {{ "PeriodsId", periodsId }}}});}stages.AddRange(new List<BsonDocument> {new BsonDocument {{ "$sort", new BsonDocument {{ "ThumbTime", -1 }}}},new BsonDocument {{ "$group", new BsonDocument {{"_id" , new BsonDocument{{ "UserId", "$UserId" }}},{"ids" , new BsonDocument{{ "$addToSet", "$_id" }}}}}},new BsonDocument {{ "$sort", new BsonDocument {{ "ids" , -1 }}}},new BsonDocument {{ "$limit", limit }}});var pipe = PipelineDefinition<PeriodsExposalThumbMongoModel, BsonDocument>.Create(stages);var objs = thumbExposalMongoClient.GetTable().Aggregate(pipe, new AggregateOptions { AllowDiskUse = true }).ToList();
这里要注意的是$group前后的$sort。sort作用于上一次条件排序。也就是说第一个sort,是对原始数据的排序。第二个sort,是对group后的结果进行排序。所以我们对自定义的ids进行排序。
第二步:
我们需要查询出结果,只取第一条最新的记录。
var stages = new List<BsonDocument>();if (type > 0){stages.Add(new BsonDocument {{ "$match", new BsonDocument {{ "PeriodsId", periodsId },{ "Type", type }}}});}else{stages.Add(new BsonDocument {{ "$match", new BsonDocument {{ "PeriodsId", periodsId }}}});}stages.AddRange(new List<BsonDocument> {new BsonDocument {{ "$sort", new BsonDocument {{ "ThumbTime", -1 }}}},new BsonDocument {{ "$group", new BsonDocument {{"_id" , new BsonDocument{{ "UserId", "$UserId" }}},{"ids" , new BsonDocument{{ "$addToSet", "$_id" }}}}}},new BsonDocument {{ "$sort", new BsonDocument {{ "ids" , -1 }}}},new BsonDocument {{ "$limit", limit }}});var pipe = PipelineDefinition<PeriodsExposalThumbMongoModel, BsonDocument>.Create(stages);var objs = thumbExposalMongoClient.GetTable().Aggregate(pipe, new AggregateOptions { AllowDiskUse = true }).ToList();//临时存储流水记录(最终的去重流水)var objIds = new List<string>();foreach (var obj in objs){//拿出重复的记录idvar array = obj["ids"].AsBsonArray;//将重复的记录进行倒序排序,索性mongo的objectid是有规律的。利用这一点我们可以直接倒序var _objIds = array.ToMap(c => c.AsObjectId.ToString()).OrderByDescending(c => c).ToList();//只拿第一条(我们进行了倒序,也就是id最大的一条为最新的一条)objIds.Add(_objIds[0]);}return objIds;
这样,我们就拿到了去重后的最新一条流水id集合了。
我们就可以直接查询出完整的流水记录了。
var entities = await thumbExposalMongoClient.GetListAsync(c => objIds.Contains(c.MongoObjId), "ThumbTime desc", cancellationToken: cancellationToken);
刚开始第一版的解决方案是取150条记录,然后再内存里去重。但是这种方式性能太低。而且150条记录有不确定性,如果连续几十条都是同一个用户的话,150条流水里最终的结果可能才几条。
完。。。