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,之后执行业务代码即可。