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)
{
//拿出重复的记录id
var 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条流水里最终的结果可能才几条。
完。。。