MongoDriver 分表分页查询

摘要:

  • 业务需求,分表也要兼容旧表。 技术有限,封装思路及代码如下,大佬们见笑。
  • 首先Mongdb的Collection及其内容字段都是可以动态创建的,所以这里需要的一个关键点是,分表时用什么字段。
  • 本文将使用数据的创建时间作为依据,按月分表(如果需要其它字段分表,也可以参考这个思路)
  • 首先本文使用泛型类约束为前提,类:MongoDriver<T>,这么做的目的是,每一个实例对应一个表操作类

上代码:

1. 封装根据时间获取Collection的方法,这里添加一个对事务的支持可选参数

 1         private IMongoCollection<T> GetCollection(DateTime? yearAndMonth, IClientSessionHandle session = null)
 2         {
 3             var tableName = typeof(T).Name;
 4             //如果没有传入时间范围就使用原表名
 5             var fixedTableName = yearAndMonth.HasValue ? $"{tableName}{yearAndMonth:yyyyMM}" : tableName;
 6 
 7             if (session == null)
 8             {
 9                 return _mongoDatabase.GetCollection<T>(fixedTableName);
10             }
11             else
12             {
13                 return session.Client.GetDatabase(_dBName).GetCollection<T>(fixedTableName);
14             }
15         }

 

2. 封装根据操作的数据的时间范围获取Collection

 1  private IEnumerable<IMongoCollection<T>> GetCollections(DateTime? startTime, DateTime? endTime, IClientSessionHandle session = null)
 2         {
 3             //没有时间范围最多查一年
 4             if (!startTime.HasValue && !endTime.HasValue)
 5             {
 6                 endTime = DateTime.Now;
 7                 startTime = endTime.Value.AddYears(-1);
 8             }
 9             //没有开始时间有结束时间,默认从开始时间往后查一年
10             else if (!startTime.HasValue && endTime.HasValue)
11             {
12                 startTime = endTime.Value.AddYears(-1);
13             }
14             else
15             {
16                 startTime ??= DateTime.Now;
17                 endTime ??= DateTime.Now;
18             }
19 
20             if (endTime.Value < startTime.Value)
21             {
22                 throw new InvalidOperationException("End time should greater than start time");
23             }
24             else if ((endTime.Value - startTime.Value).TotalDays > 365)
25             {
26                 throw new InvalidOperationException("时间跨度不能大于365天");
27             }
28             if (startTime.Value.Year == endTime.Value.Year && startTime.Value.Month == endTime.Value.Month)
29             {
30                 var collection = GetCollection( startTime.Value, session);
31                 if (collection != null)
32                 {
33                     yield return collection;
34                 }
35             }
36             else
37             {
38                 var cur = endTime.Value;
39                 while (cur.Month >= startTime.Value.Month)
40                 {
41                     var collection = GetCollection( new DateTime(cur.Year, cur.Month, 1), session);
42                     if (collection != null)
43                     {
44                         yield return collection;
45                     }
46                     cur = cur.AddMonths(-1);
47                 }
48             }
49 
50             yield return _mongoCollection;
51         }

 

3.在有了获取Collection的支撑后,剩下的事情就好办了,我们来实现分页查询的逻辑

 1     /// <summary>
 2         /// 按时间段分页查询
 3         /// </summary>
 4         /// <typeparam name="TKey"></typeparam>
 5         /// <param name="predicate">查询条件</param>
 6         /// <param name="pageIndex">第几页</param>
 7         /// <param name="pageSize">每页数量</param>
 8         /// <param name="keySelector">要排序的字段,不需要的话传null</param>
 9         /// <param name="totalCount">总数</param>
10         /// <param name="desc">默认降序</param>
11         /// <param name="startTime">查询范围的开始时间,精确到年月日</param>
12         /// <param name="endTime">查询范围的结束时间,精确到年月日</param>
13         /// <returns></returns>
14         public List<T> QueryPagedByTimeRange<TKey>(Expression<Func<T, bool>> predicate, int pageIndex, int pageSize, Expression<Func<T, TKey>> keySelector, out int totalCount, bool desc = true, DateTime? startTime = null, DateTime? endTime = null)
15         {
16             if (pageSize <= 1)
17             {
18                 throw new ArgumentException("arg:pageSize should greater than 1");
19             }
20 
21             var collections = GetCollections(startTime, endTime);
22             var result = new List<T>();
23             totalCount = 0;
24             var pageLeft = pageSize;
25 
26             foreach (var item in collections)
27             {
28                 //当前表符合条件的数量
29                 var currentTableCount = QueryPageCount(predicate, item);
30 
31                 //查到的总数为0 ,没必要再去查一遍数据了
32                 if (currentTableCount == 0)
33                 {
34                     continue;
35                 }
36 
37                 //查询到的总数
38                 totalCount += currentTableCount;
39                 //从第几条开始
40                 var startIndexNumber = (pageIndex - 1) * pageSize;
41                 //当前表查询到的数据
42                 var rangeResult = new List<T>();
43                 //第pageIndex页,从startIndexNumber条开始取,如果查到的数累计不够到startIndexNumber 就不执行查询了,继续下一张表的数量计算
44                 if (startIndexNumber > totalCount)
45                 {
46                     continue;
47                 }
48 
49                 rangeResult = QueryPaged(predicate, pageIndex, pageLeft, keySelector, item, desc);
50 
51                 //下一个表再查pageLeft条
52                 pageLeft -= rangeResult.Count;
53 
54                 if (rangeResult?.Count > 0)
55                 {
56                     result.AddRange(rangeResult);
57                 }
58 
59                 //当前表查询完不够一页了 下一个表从第一页算
60                 if (pageLeft > 0)
61                 {
62                     pageIndex = 1;
63                 }
64 
65                 //够一页了  返回数据
66                 if (pageLeft <= 0)
67                 {
68                     break;
69                 }
70             }
71 
72             return result;
73         }

4.最后用到普通的分页查询逻辑

 1         private List<T> QueryPaged<TKey>(Expression<Func<T, bool>> predicate, int pageIndex, int pageSize, Expression<Func<T, TKey>> orderKeySelector, IMongoCollection<T> collection, bool desc = true)
 2         {
 3             IQueryable<T> queryList = default;
 4             if (predicate == null)
 5             {
 6                 if (orderKeySelector != null)
 7                 {
 8                     queryList = desc ?
 9                         collection.AsQueryable().OrderByDescending(orderKeySelector).Skip(pageSize * (pageIndex - 1)).Take(pageSize) :
10                         collection.AsQueryable().OrderBy(orderKeySelector).Skip(pageSize * (pageIndex - 1)).Take(pageSize);
11                 }
12                 else
13                 {
14                     queryList = collection.AsQueryable().Skip(pageSize * (pageIndex - 1)).Take(pageSize);
15                 }
16             }
17             else
18             {
19                 if (orderKeySelector != null)
20                 {
21                     queryList = desc ?
22                         collection.AsQueryable().Where(predicate).OrderByDescending(orderKeySelector).Skip(pageSize * (pageIndex - 1)).Take(pageSize) :
23                         collection.AsQueryable().Where(predicate).OrderBy(orderKeySelector).Skip(pageSize * (pageIndex - 1)).Take(pageSize);
24                 }
25                 else
26                 {
27                     queryList = collection.AsQueryable().Where(predicate).Skip(pageSize * (pageIndex - 1)).Take(pageSize);
28                 }
29             }
30 
31             return queryList?.ToList();
32         }

其它CURD操作这里不再赘述了,主要还是通过GetCollections方法来确定分表的范围,根据范围得到该操作哪些Collection,之后执行业务代码即可。

posted @ 2023-04-21 12:02  InDulGed  阅读(69)  评论(0编辑  收藏  举报