Mongo查询用户的操作记录,去重后,仅在重复数据中取最新的一条

分类: Mongodb

Mongo如何使用group by查询去重?去重后如何查询重复记录里的最新第一条?

有时候我们碰到的需求稀奇古怪,真正实现起来特别的绕。
比如维权中心的红黑榜赞踩的流水。
需求是这样的:

查询50条,某一期的所有平台下的赞踩流水记录。

需求看起来很简单,那么产品又补刀了。

查询50条,某一期的所有平台下的赞踩流水记录。且每个用户只显示一条最新的记录。

首先如果只是查询50条流水,那很简单不过了。绕就绕在每个用户只显示一条最新的记录。而且我们是使用的Mongo。

我们能确定的是无法一次性查询出来。

第一步:

我们需要使用group by查询所有的去重记录

  1. var stages = new List<BsonDocument>();
  2. if (type > 0)
  3. {
  4. stages.Add(
  5. new BsonDocument {
  6. { "$match", new BsonDocument {
  7. { "PeriodsId", periodsId },
  8. { "Type", type }
  9. }}
  10. }
  11. );
  12. }
  13. else
  14. {
  15. stages.Add(
  16. new BsonDocument {
  17. { "$match", new BsonDocument {
  18. { "PeriodsId", periodsId }
  19. }}
  20. }
  21. );
  22. }
  23. stages.AddRange(
  24. new List<BsonDocument> {
  25. new BsonDocument {
  26. { "$sort", new BsonDocument {
  27. { "ThumbTime", -1 }
  28. }}
  29. },
  30. new BsonDocument {
  31. { "$group", new BsonDocument {
  32. {
  33. "_id" , new BsonDocument{
  34. { "UserId", "$UserId" }
  35. }
  36. },
  37. {
  38. "ids" , new BsonDocument{
  39. { "$addToSet", "$_id" }
  40. }
  41. }
  42. }}
  43. },
  44. new BsonDocument {
  45. { "$sort", new BsonDocument {
  46. { "ids" , -1 }
  47. }}
  48. },
  49. new BsonDocument {
  50. { "$limit", limit }
  51. }
  52. }
  53. );
  54. var pipe = PipelineDefinition<PeriodsExposalThumbMongoModel, BsonDocument>.Create(stages);
  55. var objs = thumbExposalMongoClient.GetTable().Aggregate(pipe, new AggregateOptions { AllowDiskUse = true }).ToList();

这里要注意的是$group前后的$sort。sort作用于上一次条件排序。也就是说第一个sort,是对原始数据的排序。第二个sort,是对group后的结果进行排序。所以我们对自定义的ids进行排序。

第二步:

我们需要查询出结果,只取第一条最新的记录。

  1. var stages = new List<BsonDocument>();
  2. if (type > 0)
  3. {
  4. stages.Add(
  5. new BsonDocument {
  6. { "$match", new BsonDocument {
  7. { "PeriodsId", periodsId },
  8. { "Type", type }
  9. }}
  10. }
  11. );
  12. }
  13. else
  14. {
  15. stages.Add(
  16. new BsonDocument {
  17. { "$match", new BsonDocument {
  18. { "PeriodsId", periodsId }
  19. }}
  20. }
  21. );
  22. }
  23. stages.AddRange(
  24. new List<BsonDocument> {
  25. new BsonDocument {
  26. { "$sort", new BsonDocument {
  27. { "ThumbTime", -1 }
  28. }}
  29. },
  30. new BsonDocument {
  31. { "$group", new BsonDocument {
  32. {
  33. "_id" , new BsonDocument{
  34. { "UserId", "$UserId" }
  35. }
  36. },
  37. {
  38. "ids" , new BsonDocument{
  39. { "$addToSet", "$_id" }
  40. }
  41. }
  42. }}
  43. },
  44. new BsonDocument {
  45. { "$sort", new BsonDocument {
  46. { "ids" , -1 }
  47. }}
  48. },
  49. new BsonDocument {
  50. { "$limit", limit }
  51. }
  52. }
  53. );
  54. var pipe = PipelineDefinition<PeriodsExposalThumbMongoModel, BsonDocument>.Create(stages);
  55. var objs = thumbExposalMongoClient.GetTable().Aggregate(pipe, new AggregateOptions { AllowDiskUse = true }).ToList();
  56. //临时存储流水记录(最终的去重流水)
  57. var objIds = new List<string>();
  58. foreach (var obj in objs)
  59. {
  60. //拿出重复的记录id
  61. var array = obj["ids"].AsBsonArray;
  62. //将重复的记录进行倒序排序,索性mongo的objectid是有规律的。利用这一点我们可以直接倒序
  63. var _objIds = array.ToMap(c => c.AsObjectId.ToString()).OrderByDescending(c => c).ToList();
  64. //只拿第一条(我们进行了倒序,也就是id最大的一条为最新的一条)
  65. objIds.Add(_objIds[0]);
  66. }
  67. return objIds;

这样,我们就拿到了去重后的最新一条流水id集合了。

我们就可以直接查询出完整的流水记录了。

  1. var entities = await thumbExposalMongoClient.GetListAsync(c => objIds.Contains(c.MongoObjId), "ThumbTime desc", cancellationToken: cancellationToken);

刚开始第一版的解决方案是取150条记录,然后再内存里去重。但是这种方式性能太低。而且150条记录有不确定性,如果连续几十条都是同一个用户的话,150条流水里最终的结果可能才几条。

完。。。

标签: Mongodb NetCore

上一篇: 没有了

下一篇: Mongodb导出索引

by 2023-08-07 23:49:07
篇笔记

学习笔记