聚合查询结构体系

​ 我们都知道Mongo中聚合是由$match$project等聚合项组成,所以在C# Driver中具有两种类型:聚合管道(PipelineDefinition)和聚合管道项(IPipelineStageDefinition) ,下面先来看一下聚合管道项的结构体系

IPipelineStageDefinition

​ IPipelineStageDefinition接口是聚合管道项的顶级接口,这个接口中只定义了一些获取输入类型和输出类型的简单的属性


  1. public interface IPipelineStageDefinition
  2. {
  3. // 输入类型
  4. Type InputType { get; }
  5. // 获取管道操作名称
  6. string OperatorName { get; }
  7. // 输出类型
  8. Type OutputType { get; }
  9. // 获取一个IRenderedIRenderedPipelineStageDefinition
  10. // IRenderedPipelineStageDefinition是一个Stage提供对象,下面会介绍
  11. IRenderedPipelineStageDefinition Render(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry);
  12. // 获取当前管道项的字符串格式,例:{ \"$match\" : { \"_id\" : \"402066782845407232\" } }
  13. string ToString(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry);
  14. }

​ 这个接口具有一个PipelineStageDefinition派生类,这个类是一个抽象类,在这个抽象类中只多了两个隐式转换,

  1. public abstract class PipelineStageDefinition<TInput, TOutput> : IPipelineStageDefinition
  2. {
  3. // 将一个BsonDocument对象转换为管道项
  4. public static implicit operator PipelineStageDefinition<TInput, TOutput>(BsonDocument document);
  5. // 将一个json字符串转换为管道项
  6. public static implicit operator PipelineStageDefinition<TInput, TOutput>(string json);
  7. }

​ 用过C# Driver的朋友都应该知道我们使用Driver时经常使用这种隐式转换,例如经常使用FilterDefinition便可使用json字符串直接赋值,这也是Driver强大的地方。

  1. FilterDefinition<BsonDocument> filter = "{_id:123}";

​ 其实这两个隐式转换如果翻源码就会看到直接创建了这个抽象类的实现类对象

  1. public static implicit operator PipelineStageDefinition<TInput, TOutput>(BsonDocument document)
  2. {
  3. if (document == null)
  4. return null;
  5. return new BsonDocumentPipelineStageDefinition<TInput, TOutput>(document);
  6. }
  7. public static implicit operator PipelineStageDefinition<TInput, TOutput>(string json)
  8. {
  9. if (json == null)
  10. return null;
  11. return new JsonPipelineStageDefinition<TInput, TOutput>(json);
  12. }

​ 也就是说这个抽象类具有这么两个派生类BsonDocumentPipelineStageDefinition,JsonPipelineStageDefinition 这两个类型就是使用Bsondocument对象和json字符串进行实例化聚合管道项

PipelineStageDefinition其它派生类

​ 如果仅仅使用,只使用上面那两个派生类即可,但实际上IPipelineStageDefinition的派生类还有两个:

DelegatedPipelineStageDefinition:由一个Func<IBsonSerializer, IBsonSerializerRegistry, RenderedPipelineStageDefinition委托创建的实例对象

SortPipelineStageDefinition:排序项的实例对象

​ 其实这两个派生类在使用上根本不需要知道,它们的访问级别是internal,也就是说在使用时根本无法创建这两个派生类的实例对象,其实这两个类都是PipelineStageDefinition实例在调用Match() ,Project() ,Sort() 方法时进行内部创建的,这个下面再说

​ 对于SortPipelineStageDefinition和DelegatedPipelineStageDefinition这两个派生类其实内部特别简单,但是却又扯到了另外一个类型

  1. internal class SortPipelineStageDefinition<TInput> : PipelineStageDefinition<TInput, TInput>
  2. {
  3. public SortPipelineStageDefinition(SortDefinition<TInput> sort)
  4. {
  5. Sort = sort;
  6. }
  7. // 排序条件对象
  8. public SortDefinition<TInput> Sort { get; private set; }
  9. // 操作项名称
  10. public override string OperatorName => "$sort";
  11. public override RenderedPipelineStageDefinition<TInput> Render(IBsonSerializer<TInput> inputSerializer, IBsonSerializerRegistry serializerRegistry)
  12. {
  13. var renderedSort = Sort.Render(inputSerializer, serializerRegistry);
  14. var document = new BsonDocument(OperatorName, renderedSort);
  15. return new RenderedPipelineStageDefinition<TInput>(OperatorName, document, inputSerializer);
  16. }
  17. }
  18. internal sealed class DelegatedPipelineStageDefinition<TInput, TOutput> : PipelineStageDefinition<TInput, TOutput>
  19. {
  20. // 委托缓存
  21. private readonly Func<IBsonSerializer<TInput>, IBsonSerializerRegistry, RenderedPipelineStageDefinition<TOutput>> _renderer;
  22. public DelegatedPipelineStageDefinition(string operatorName, Func<IBsonSerializer<TInput>, IBsonSerializerRegistry, RenderedPipelineStageDefinition<TOutput>> renderer)
  23. {
  24. _renderer = renderer;
  25. }
  26. // 获取RenderedPipelineStageDefinition 直接返回委托调用
  27. public override RenderedPipelineStageDefinition<TOutput> Render(IBsonSerializer<TInput> inputSerializer, IBsonSerializerRegistry serializerRegistry)
  28. {
  29. return _renderer(inputSerializer, serializerRegistry);
  30. }
  31. }

​ 通过上面代码可以看到这两个派生类型特别简单,感觉就是一个简单的代理,一切都指向于RenderedPipelineStageDefinition这个类型,也就是在真正执行聚合操作时可能使用的就是这个类型,这个在这先留一下悬念,因为RenderedPipelineStageDefinition这个类型还涉及到了整个聚合管道对象和执行操作,等到下面再讲解。

PipelineStageDefinitionBuilder

​ 下面来说一下PipelineStageDefinitionBuilder这个类型,顾名思义,这是一个创建PipelineStageDefinition的类型,它是一个静态类,内部具有创建各种的使用方法,这个类型中方法特别多,也不一一细讲,只讲三个方法,也就是上面提到的Match() ,Project() ,Sort()

  1. public static class PipelineStageDefinitionBuilder
  2. {
  3. // match
  4. public static PipelineStageDefinition<TInput, TInput> Match<TInput>(
  5. FilterDefinition<TInput> filter)
  6. {
  7. const string operatorName = "$match";
  8. var stage = new DelegatedPipelineStageDefinition<TInput, TInput>(
  9. operatorName,
  10. (s, sr) => new RenderedPipelineStageDefinition<TInput>(operatorName, new BsonDocument(operatorName, filter.Render(s, sr)), s));
  11. return stage;
  12. }
  13. // project
  14. public static PipelineStageDefinition<TInput, TOutput> Project<TInput, TOutput>(ProjectionDefinition<TInput, TOutput> projection)
  15. {
  16. const string operatorName = "$project";
  17. var stage = new DelegatedPipelineStageDefinition<TInput, TOutput>(
  18. operatorName,
  19. (s, sr) =>
  20. {
  21. var renderedProjection = projection.Render(s, sr);
  22. BsonDocument document;
  23. if (renderedProjection.Document == null)
  24. document = new BsonDocument();
  25. else
  26. document = new BsonDocument(operatorName, renderedProjection.Document);
  27. return new RenderedPipelineStageDefinition<TOutput>(operatorName, document, renderedProjection.ProjectionSerializer);
  28. });
  29. return stage;
  30. }
  31. // sort
  32. public static PipelineStageDefinition<TInput, TInput> Sort<TInput>(
  33. SortDefinition<TInput> sort)
  34. {
  35. return new SortPipelineStageDefinition<TInput>(sort);
  36. }
  37. }

​ 上面就是这三个方法的源代码,三个方法分别使用FilterDefinition,ProjectionDefinition,SortDefinition实例创建PipelineStageDefinition对象,而所创建的也是后面讲的那两个派生类,这也验证了上面所说的两个类型的用途。

PipelineStageDefinition类总结

​ 从上面一步步可以得知,Driver为我们提供了三种创建聚合项的办法,其实这三种也应用于driver的各种使用上

  1. BsonDocument创建

  2. json字符串创建

  3. 使用PipelineStageDefinitionBuilder进行创建

PipelineDefinition

​ 说完管道项,下面就说一下整个聚合管道的操作类PipelineDefinition以及它的派生类

​ 首先PipelineDefinition这个父级类型,它跟PipelineStageDefinition一样是一个抽象类型,并且和PipelineStageDefinition相同的是它也有一个Render方法和两个隐式转换,多了几个静态的创建方法,使得更具有扩展性

  1. public abstract class PipelineDefinition<TInput, TOutput>
  2. {
  3. public abstract RenderedPipelineDefinition<TOutput> Render(IBsonSerializer<TInput> inputSerializer, IBsonSerializerRegistry serializerRegistry);
  4. // 使用管道项集合创建一个PipelineStagePipelineDefinition实例对象
  5. public static PipelineDefinition<TInput, TOutput> Create(
  6. IEnumerable<IPipelineStageDefinition> stages,
  7. IBsonSerializer<TOutput> outputSerializer = null)
  8. {
  9. return new PipelineStagePipelineDefinition<TInput, TOutput>(stages, outputSerializer);
  10. }
  11. // 使用BsonDocument集合创建一个BsonDocumentStagePipelineDefinition对象
  12. public static PipelineDefinition<TInput, TOutput> Create(
  13. IEnumerable<BsonDocument> stages,
  14. IBsonSerializer<TOutput> outputSerializer = null)
  15. {
  16. return new BsonDocumentStagePipelineDefinition<TInput, TOutput>(stages, outputSerializer);
  17. }
  18. // 使用json字符串集合创建一个BsonDocumentStagePipelineDefinition对象
  19. public static PipelineDefinition<TInput, TOutput> Create(
  20. IEnumerable<string> stages,
  21. IBsonSerializer<TOutput> outputSerializer = null)
  22. {
  23. return Create(stages?.Select(s => BsonDocument.Parse(s)), outputSerializer);
  24. }
  25. // 隐式转换
  26. // 将IPipelineStageDefinition集合隐式转换为PipelineStagePipelineDefinition对象
  27. public static implicit operator PipelineDefinition<TInput, TOutput>(List<IPipelineStageDefinition> stages)
  28. {
  29. return Create(stages);
  30. }
  31. // 将BsonDocument集合隐式转换为BsonDocumentStagePipelineDefinition对象
  32. public static implicit operator PipelineDefinition<TInput, TOutput>(List<BsonDocument> stages)
  33. {
  34. return Create(stages);
  35. }
  36. }

注:PipelineDefinition类中还封装了数组参数和其它内容,有兴趣的朋友可以自己去看看

​ 上面类型可以看出PipelineDefinition做了很多封装,为了使用更加便捷。从上面也看到了两个派生类型:PipelineStagePipelineDefinition和BsonDocumentStagePipelineDefinition

​ 其实PipelineDefinition派生类型一共有7个,我们能用到的是6个,我将这个7个类型分为:创建性,改变性和外部不可用性这三种,下面先来看看创建性

注:其实严格意义上是两种,外部不可用的派生类型属于创建性,外部不可用的派生类型也只是在特定情况下被内部用到。

创建性派PipelineDefinition

​ 创建性有3个,其中两个就是上面基类中创建的两个派生类型,另外一个是EmptyPipelineDefinition,顾名思义这是一个空的管道,这个跟创建空条件那个是极其相似的( Builders.Filter.Empty),

  1. // EmptyPipelineDefinition
  2. public sealed class EmptyPipelineDefinition<TInput> : PipelineDefinition<TInput, TInput>
  3. {
  4. public override IEnumerable<IPipelineStageDefinition> Stages => Enumerable.Empty<IPipelineStageDefinition>();
  5. //
  6. public override RenderedPipelineDefinition<TInput> Render(IBsonSerializer<TInput> inputSerializer, IBsonSerializerRegistry serializerRegistry)
  7. {
  8. var documents = Enumerable.Empty<BsonDocument>();
  9. return new RenderedPipelineDefinition<TInput>(documents, _inputSerializer ?? inputSerializer);
  10. }
  11. }
  12. // PipelineStagePipelineDefinition
  13. public sealed class PipelineStagePipelineDefinition<TInput, TOutput> : PipelineDefinition<TInput, TOutput>
  14. {
  15. private readonly IList<IPipelineStageDefinition> _stages;
  16. public PipelineStagePipelineDefinition(IEnumerable<IPipelineStageDefinition> stages, IBsonSerializer<TOutput> outputSerializer = null)
  17. {
  18. _stages = stages;
  19. _outputSerializer = outputSerializer;
  20. }
  21. public override IEnumerable<IPipelineStageDefinition> Stages => _stages;
  22. public override RenderedPipelineDefinition<TOutput> Render(IBsonSerializer<TInput> inputSerializer, IBsonSerializerRegistry serializerRegistry)
  23. {
  24. // 当前集合进行存储当前聚合管道所有聚合项的BsonDocument
  25. var pipeline = new List<BsonDocument>();
  26. IBsonSerializer currentSerializer = inputSerializer;
  27. foreach (var stage in _stages)
  28. {
  29. // 获取每一个聚合项的RenderedPipelineDefinition
  30. // 然后获取每个聚合项RenderedPipelineDefinition中的Bsondocument
  31. var renderedStage = stage.Render(currentSerializer, serializerRegistry);
  32. currentSerializer = renderedStage.OutputSerializer;
  33. if (renderedStage.Document.ElementCount > 0)
  34. {
  35. pipeline.Add(renderedStage.Document);
  36. }
  37. }
  38. return new RenderedPipelineDefinition<TOutput>(
  39. pipeline,
  40. _outputSerializer ?? (currentSerializer as IBsonSerializer<TOutput>) ?? serializerRegistry.GetSerializer<TOutput>());
  41. }
  42. // BsonDocumentStagePipelineDefinition
  43. public sealed class BsonDocumentStagePipelineDefinition<TInput, TOutput> : PipelineDefinition<TInput, TOutput>
  44. {
  45. private readonly List<BsonDocument> _stages;
  46. public BsonDocumentStagePipelineDefinition(IEnumerable<BsonDocument> stages, IBsonSerializer<TOutput> outputSerializer = null)
  47. {
  48. _stages = stages;
  49. _outputSerializer = outputSerializer;
  50. }
  51. public override IBsonSerializer<TOutput> OutputSerializer => _outputSerializer;
  52. public IList<BsonDocument> Documents
  53. {
  54. get { return _stages; }
  55. }
  56. // 获取当前聚合的所有聚合项
  57. public override IEnumerable<IPipelineStageDefinition> Stages => _stages.Select(s => new BsonDocumentPipelineStageDefinition<TInput, TOutput>(s, _outputSerializer));
  58. public override RenderedPipelineDefinition<TOutput> Render(IBsonSerializer<TInput> inputSerializer, IBsonSerializerRegistry serializerRegistry)
  59. {
  60. return new RenderedPipelineDefinition<TOutput>(
  61. _stages,
  62. _outputSerializer ?? (inputSerializer as IBsonSerializer<TOutput>) ?? serializerRegistry.GetSerializer<TOutput>());
  63. }
  64. }

​ 上面是这个三个派生类型基本实现,基本上也都没什么特别的地方,而逻辑也是在Render()这个方法中,EmptyPipelineDefinition中创建了一个空的Bsondocument对象集合实例化的RenderedPipelineDefinition,而BsonDocumentStagePipelineDefinition和PipelineStagePipelineDefinition分别以传入的Bsondocument集合和从管道项对象中调用的Render()中获取Bsondocument集合。从这里可以得出2点

1.RenderedPipelineStageDefinition的作用是为了提供其内部的Bsondocument然后创建RenderedPipelineDefinition对象

2.RenderedPipelineStageDefinition和RenderedPipelineDefinition的关系就像BsonDocumentPipelineStageDefinition和BsonDocumentStagePipelineDefinition关系类似,一个对应管道项,一个对应管道

  1. 至此,一切的源头都指向了<span style="color:#009BDB">RenderedPipelineDefinition</span>这个类,但是这个类在下面再介绍,先来看一下改变性的<span style="color:#009BDB">PipelineDefinition</span>

改变性PipelineDefinition

​ 为什么我叫它为改变性呢,因为它是在一个已有PipelineDefinition基础上进行的添加或者替换,下面来看看这三个派生类型

PrependedStagePipelineDefinition:在一个PipelineDefinition管道前面添加一个管道项

AppendedStagePipelineDefinition:在一个PipelineDefinition管道后面添加一个管道项

ReplaceOutputSerializerPipelineDefinition:替换一个PipelineDefinition的序列化对象类型

​ 其实看到这三个派生类就知道其作用了,所以在这里也不进行详细介绍了,只贴出它们的构造方法,有兴趣的朋友可以翻阅源码

  1. // PrependedStagePipelineDefinition
  2. public sealed class PrependedStagePipelineDefinition<TInput, TIntermediate, TOutput> : PipelineDefinition<TInput, TOutput>
  3. {
  4. public PrependedStagePipelineDefinition(
  5. PipelineStageDefinition<TInput, TIntermediate> stage,
  6. PipelineDefinition<TIntermediate, TOutput> pipeline,
  7. IBsonSerializer<TOutput> outputSerializer = null)
  8. {
  9. }
  10. }
  11. // AppendedStagePipelineDefinition
  12. public sealed class AppendedStagePipelineDefinition<TInput, TIntermediate, TOutput> : PipelineDefinition<TInput, TOutput>
  13. {
  14. public AppendedStagePipelineDefinition(
  15. PipelineDefinition<TInput, TIntermediate> pipeline,
  16. PipelineStageDefinition<TIntermediate, TOutput> stage,
  17. IBsonSerializer<TOutput> outputSerializer = null)
  18. {
  19. }
  20. }
  21. // ReplaceOutputSerializerPipelineDefinition
  22. public sealed class ReplaceOutputSerializerPipelineDefinition<TInput, TIntermediate, TOutput> : PipelineDefinition<TInput, TOutput>
  23. {
  24. public ReplaceOutputSerializerPipelineDefinition(
  25. PipelineDefinition<TInput, TIntermediate> pipeline,
  26. IBsonSerializer<TOutput> outputSerializer = null)
  27. {
  28. }
  29. }

外部不可用派生类

​ 这个外部不可用的派生类型是OptimizingPipelineDefinition ,按照翻译看起来像最优的管道,其实在执行操作时都会现将外部定义的PipelineDefinition转换为OptimizingPipelineDefinition 类型,首先先看看这个类型的定义

  1. internal class OptimizingPipelineDefinition<TInput, TOutput> : PipelineDefinition<TInput, TOutput>
  2. {
  3. private readonly PipelineDefinition<TInput, TOutput> _wrapped;
  4. public OptimizingPipelineDefinition(PipelineDefinition<TInput, TOutput> wrapped)
  5. {
  6. _wrapped = wrapped;
  7. }
  8. /// <inheritdoc />
  9. public override IEnumerable<IPipelineStageDefinition> Stages => _wrapped.Stages;
  10. public override RenderedPipelineDefinition<TOutput> Render(IBsonSerializer<TInput> inputSerializer, IBsonSerializerRegistry serializerRegistry)
  11. {
  12. var rendered = _wrapped.Render(inputSerializer, serializerRegistry);
  13. if (rendered.Documents.Count > 1)
  14. {
  15. // 如果有可能,进行组合一下$match
  16. var firstStage = rendered.Documents[0].GetElement(0);
  17. var secondStage = rendered.Documents[1].GetElement(0);
  18. if (firstStage.Name == "$match" && secondStage.Name == "$match")
  19. {
  20. var combinedFilter = Builders<BsonDocument>.Filter.And(
  21. (BsonDocument)firstStage.Value,
  22. (BsonDocument)secondStage.Value);
  23. var combinedStage = new BsonDocument("$match", combinedFilter.Render(BsonDocumentSerializer.Instance, serializerRegistry));
  24. rendered.Documents[0] = combinedStage;
  25. rendered.Documents.RemoveAt(1);
  26. }
  27. }
  28. return rendered;
  29. }
  30. }

​ 可以看到只是在Render()代码中进行了一个轻微的优化操作,这个优化类是针对OfType情况进行优化的,唯一的使用地方是在FilteredMongoCollectionBase这个抽象类中,而这个抽象类的实现类是OfTypeMongoCollection

  1. internal abstract class FilteredMongoCollectionBase<TDocument> : MongoCollectionBase<TDocument>, IFilteredMongoCollection<TDocument>
  2. {
  3. // 创建OptimizingPipelineDefinition
  4. private PipelineDefinition<TDocument, TResult> CreateFilteredPipeline<TResult>(PipelineDefinition<TDocument, TResult> pipeline)
  5. {
  6. var filterStage = PipelineStageDefinitionBuilder.Match(_filter);
  7. var filteredPipeline = new PrependedStagePipelineDefinition<TDocument, TDocument, TResult>(filterStage, pipeline);
  8. return new OptimizingPipelineDefinition<TDocument, TResult>(filteredPipeline);
  9. }
  10. }
  11. internal class OfTypeMongoCollection<TRootDocument, TDerivedDocument> : FilteredMongoCollectionBase<TDerivedDocument>
  12. where TDerivedDocument : TRootDocument
  13. {
  14. }

PipelineDefinitionBuilder类型

​ PipelineDefinitionBuilder类型是管道系列的一个帮助类,这个与PipelineStageDefinitionBuilder类相似,但又不尽相同,PipelineDefinitionBuilder中定义的都是PipelineDefinition对象的扩展方法,定义了一系列方便的方法

  1. public static class PipelineDefinitionBuilder
  2. {
  3. // $match
  4. public static PipelineDefinition<TInput, TOutput> Match<TInput, TOutput>(
  5. this PipelineDefinition<TInput, TOutput> pipeline,
  6. FilterDefinition<TOutput> filter)
  7. {
  8. return pipeline.AppendStage(PipelineStageDefinitionBuilder.Match(filter));
  9. }
  10. // $project
  11. public static PipelineDefinition<TInput, TOutput> Project<TInput, TIntermediate, TOutput>(
  12. this PipelineDefinition<TInput, TIntermediate> pipeline,
  13. ProjectionDefinition<TIntermediate, TOutput> projection)
  14. {
  15. return pipeline.AppendStage(PipelineStageDefinitionBuilder.Project(projection));
  16. }
  17. // AppendStage
  18. public static PipelineDefinition<TInput, TOutput> AppendStage<TInput, TIntermediate, TOutput> (
  19. this PipelineDefinition<TInput, TIntermediate> pipeline,
  20. PipelineStageDefinition<TIntermediate, TOutput> stage,
  21. IBsonSerializer<TOutput> outputSerializer = null)
  22. {
  23. return new AppendedStagePipelineDefinition<TInput, TIntermediate, TOutput>(pipeline, stage, outputSerializer);
  24. }
  25. public static PipelineDefinition<TInput, TOutput> As<TInput, TIntermediate, TOutput>(
  26. this PipelineDefinition<TInput, TIntermediate> pipeline,
  27. IBsonSerializer<TOutput> outputSerializer = null)
  28. {
  29. return new ReplaceOutputSerializerPipelineDefinition<TInput, TIntermediate, TOutput>(pipeline, outputSerializer);
  30. }
  31. }

​ 其实可以看出从上面几个个方法可以看出其本质还是使用AppendedStagePipelineDefinition和ReplaceOutputSerializerPipelineDefinition。Match()Project()都是调用了AppendStage(),而这个方法是创建了一个新的AppendedStagePipelineDefinition对象返回。而As()也是创建了一个新的ReplaceOutputSerializerPipelineDefinition返回。其本质没变,但是可以使得整个driver多了扩展性,更加方便了使用。有的聚合项像$addFields并没有封装方法,可能使用率不大,所以并没有封装,像这样的直接就调用AppendStage()即可

PipelineDefinition类总结

​ 通过上面介绍其实可以看出来了,Mongo的C# Driver中聚合操作使用起来特别方便,使用时先创建聚合项对象再创建聚合管道对象还是直接创建聚合管道对象或者直接使用隐式转换都可以。其实不止聚合,C# Driver中各个操作基本都是如此,使用起来都特别方便,既然创建聚合管道实例的方法特别多,所以在这也就不一一列出,只简单的列出几个

1.先实例化聚合项,再实例化聚合管道对象

2.直接使用隐式转换进行创建聚合管道对象

3.使用扩展方法进行创建

RenderedPipelineStageDefinition和RenderedPipelineDefinition介绍

​ 下面我们来说说RenderedPipelineStageDefinition和RenderedPipelineDefinition这两个类,这两个类叫做聚合项和聚合管道的提供者,它们真正提供了聚合的语句。上面已经简单说过,它们分别是聚合项实例和聚合管道实例创建的,并且在PipelineStagePipelineDefinition中也可以看到RenderedPipelineDefinition是根据RenderedPipelineStageDefinition内部BsonDocument进行实例化的,下面先来看一看这两个类型的内部结构

  1. // RenderedPipelineStageDefinition
  2. public class RenderedPipelineStageDefinition<TOutput> : IRenderedPipelineStageDefinition
  3. {
  4. private string _operatorName;
  5. private BsonDocument _document;
  6. private IBsonSerializer<TOutput> _outputSerializer;
  7. public RenderedPipelineStageDefinition(string operatorName, BsonDocument document, IBsonSerializer<TOutput> outputSerializer)
  8. {
  9. _operatorName = Ensure.IsNotNull(operatorName, nameof(operatorName));
  10. _document = Ensure.IsNotNull(document, nameof(document));
  11. _outputSerializer = Ensure.IsNotNull(outputSerializer, nameof(outputSerializer));
  12. }
  13. public BsonDocument Document
  14. {
  15. get { return _document; }
  16. }
  17. public IBsonSerializer<TOutput> OutputSerializer
  18. {
  19. get { return _outputSerializer; }
  20. }
  21. public string OperatorName
  22. {
  23. get { return _operatorName; }
  24. }
  25. IBsonSerializer IRenderedPipelineStageDefinition.OutputSerializer
  26. {
  27. get { return _outputSerializer; }
  28. }
  29. }
  30. // RenderedPipelineDefinition
  31. public class RenderedPipelineDefinition<TOutput>
  32. {
  33. private List<BsonDocument> _documents;
  34. private IBsonSerializer<TOutput> _outputSerializer;
  35. public RenderedPipelineDefinition(IEnumerable<BsonDocument> documents, IBsonSerializer<TOutput> outputSerializer)
  36. {
  37. _documents = Ensure.IsNotNull(documents, nameof(documents)).ToList();
  38. _outputSerializer = Ensure.IsNotNull(outputSerializer, nameof(outputSerializer));
  39. }
  40. public IList<BsonDocument> Documents
  41. {
  42. get { return _documents; }
  43. }
  44. public IBsonSerializer<TOutput> OutputSerializer
  45. {
  46. get { return _outputSerializer; }
  47. }
  48. }

​ 可以看到其实最重要是就是内部的BsonDocument这个属性,那么这个属性里面是什么呢,我们先来看一下

可以看出BsonDocument其实存放就是一个聚合项的json字符串,也就是

注:这个Render()是以序列化器类型实例和序列化注册实例进行序列化为字符串的

然后我来验证聚合的最后执行操作,也就是RenderedPipelineDefinition的作用,这个操作是在MongoCollectionImpl中,从下面代码可以看出,使用Render()方法获取聚合管道的真实语句。然后由此语句执行,由此可以看出其实一切的PipelineDefinition对象最终都是生成RenderedPipelineDefinition对象,这个对象携带着执行语句的json字符串形式。

  1. internal sealed class MongoCollectionImpl<TDocument> : MongoCollectionBase<TDocument>
  2. {
  3. public override IAsyncCursor<TResult> Aggregate<TResult>(IClientSessionHandle session, PipelineDefinition<TDocument, TResult> pipeline, AggregateOptions options, CancellationToken cancellationToken = default(CancellationToken))
  4. {
  5. // 获取当前聚合管道对象的语句
  6. var renderedPipeline = pipeline.Render(_documentSerializer, _settings.SerializerRegistry);
  7. options = options ?? new AggregateOptions();
  8. var last = renderedPipeline.Documents.LastOrDefault();
  9. if (last != null && last.GetElement(0).Name == "$out")
  10. {
  11. var aggregateOperation = CreateAggregateToCollectionOperation(renderedPipeline, options);
  12. ExecuteWriteOperation(session, aggregateOperation, cancellationToken);
  13. var findOperation = CreateAggregateToCollectionFindOperation(last, renderedPipeline.OutputSerializer, options);
  14. var forkedSession = session.Fork();
  15. var deferredCursor = new DeferredAsyncCursor<TResult>(
  16. () => forkedSession.Dispose(),
  17. ct => ExecuteReadOperation(forkedSession, findOperation, ReadPreference.Primary, ct),
  18. ct => ExecuteReadOperationAsync(forkedSession, findOperation, ReadPreference.Primary, ct));
  19. return deferredCursor;
  20. }
  21. else
  22. {
  23. var aggregateOperation = CreateAggregateOperation(renderedPipeline, options);
  24. return ExecuteReadOperation(session, aggregateOperation, cancellationToken);
  25. }
  26. }
  27. }

聚合操作的执行方法

​ 上面说了整个聚合管道类的体系,下面说一下最后调用的执行方法

​ 执行方法调用的是IMongoCollection对象的Aggregate()方法,这个方法在IMongoCollection类中具有两个重载,都是需要PipelineDefinition为参数的。

​ 在这个方法中还有一个AggregateOptions参数。这个类是执行聚合的一些选择操作。比如是否使用游标,如果内存不足情况下是否允许使用磁盘等等。。


  1. IAsyncCursor<TResult> Aggregate<TResult>(PipelineDefinition<TDocument, TResult> pipeline, AggregateOptions options = null, CancellationToken cancellationToken = default(CancellationToken));

Aggregate()方法会返回一个IAsyncCursor实例,这个对象代表一个游标。

其实在IMongoCollectionExtensions这个扩展类中还具有Aggregate()方法,这个方法也算是另外一种用法。因为这个方法参数并没有PipelineDefinition对象,并且返回类型也不再是IAsyncCursor,而是一个IAggregateFluent类型。IAggregateFluent类型具有一系列方法


  1. public static IAggregateFluent<TDocument> Aggregate<TDocument>(this IMongoCollection<TDocument> collection, AggregateOptions options = null);

Mongo C# Driver 聚合使用---深入浅出的更多相关文章

  1. Ignoring Extra Elements in mongoDB C# Driver

    MongoDB删除字段后会报错: Element ... does not match any field or property of class Customer. 需要在实体类增加 [BsonI ...

  2. node-mongo-native1.3.19连接mongo的最优方法

    最近需要在node下连接mongo,尝试了很多方法,本文简要总结一下 选择Driver 首先,基本上有4个常见的driver供选择 1.官方的是node-mongo-native 2.基于node-m ...

  3. mongo connections url string 的问题

    摘要 driver 连接Mongo DB的url其实很简单,就是几个变量拼接成一个url,和关系型数据库没什么不同.但是因为mongo有单个instance和replicaSet不同的部署策略,还有m ...

  4. mongodb高级聚合查询

    在工作中会经常遇到一些mongodb的聚合操作,特此总结下.mongo存储的可以是复杂类型,比如数组.对象等mysql不善于处理的文档型结构,并且聚合的操作也比mysql复杂很多. 注:本文基于 mo ...

  5. MongoDB的aggregate聚合

    聚合框架中常用的几个操作: $project:修改输入文档的结构.可以用来重命名.增加或删除域,也可以用于创建计算结果以及嵌套文档.(显示的列,相当遇sql 的) $match:用于过滤数据,只输出符 ...

  6. mongodb高级聚合查询(转)

    在工作中会经常遇到一些mongodb的聚合操作,特此总结下.mongo存储的可以是复杂类型,比如数组.对象等mysql不善于处理的文档型结构,并且聚合的操作也比mysql复杂很多. 注:本文基于 mo ...

  7. Mongo读书笔记1 -- GridFS

      一个Mongo文档最大4M. GridFS不依赖于MongoDB, 其他符合规范的驱动都可以访问它. GridFS包含两部分:一部分存储文件名和其他metadata; 另一部分存储实际的文件,通常 ...

  8. mongodb 高级聚合查询

    mongodb高级聚合查询   在工作中会经常遇到一些mongodb的聚合操作,特此总结下.mongo存储的可以是复杂类型,比如数组.对象等mysql不善于处理的文档型结构,并且聚合的操作也比mysq ...

  9. MongoDB框架Jongo的使用介绍

    1.Jongo可以用来做什么?   Jongo框架的目的是使在MongoDB中可以直接使用的查询Shell可以直接在Java中使用.在官网首页有一个非常简洁的例子:   SHELL:这种查询方式是Mo ...

随机推荐

  1. Quartz实现分布式可动态配置的定时任务

    关键词: 1. 定时任务 2. 分布式 3. 可动态配置触发时间 一般通过Quartz实现定时任务很简单.如果实现分布式定时任务需要结合分布式框架选择master节点触发也可以实现.但我们有个实际需求 ...

  2. mac-os安装autojump

    一 概念 autojump是一个命令行工具,它可以使用快捷命令,直接跳转到配置好的目录,而不用管现在身在何处,依赖zsh. 一 安装 安装zsh:sh -c "$(curl -fsSL ht ...

  3. 【机器学习】--鲁棒性调优之L1正则,L2正则

    一.前述 鲁棒性调优就是让模型有更好的泛化能力和推广力. 二.具体原理 1.背景 第一个更好,因为当把测试集带入到这个模型里去.如果测试集本来是100,带入的时候变成101,则第二个模型结果偏差很大, ...

  4. .NET Core微服务之基于Apollo实现统一配置中心

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.关于统一配置中心与Apollo 在微服务架构环境中,项目中配置文件比较繁杂,而且不同环境的不同配置修改相对频繁,每次发布都需要对应修改 ...

  5. 【工利其器】必会工具之(一)Source Insight篇

    前言         “Source Insight(以下简称SI)是世界上最好的编辑器”,说这句话不知道会不会出门被打呢?-_- 中国古话说得好,“文无第一,武无第二”,所以不敢说SI是最好的,但是 ...

  6. 卷积神经网络 CNN 学习笔记

    激活函数Relu 最近几年卷积神经网络中,激活函数往往不选择sigmoid或tanh函数,而是选择relu函数.Relu函数的定义 $$f(x)= max(0,x)$$ Relu函数图像如下图所示: ...

  7. 还在问跨域?本文记录js跨域的多种实现实例

    前言 众所周知,受浏览器同源策略的影响,产生了跨域问题,那么我们应该如何实现跨域呢?本文记录几种跨域的简单实现 前期准备 为了方便测试,我们启动两个服务,10086(就是在这篇博客自动生成的项目,请戳 ...

  8. .net core EFcore model生成数据

    创建数据库 (扫盲贴还劳烦大神们勿喷,谢谢) 打开数据库 输入如下代码 创建数据库 CREATE DATABASE [Blogging]; GO USE [Blogging]; GO CREATE T ...

  9. 什么是mybatis?

    [学习笔记] 什么是mybatis: Mybatis本质是一种半自动化的ORM框架,前身是ibatis,除了要pojo和映射关系之外,还需要些sql语句. 怎么看待ORM框架: 处理矛盾的,java程 ...

  10. axios+Vue上传文件显示进度

    一,前言 最近在用Vue,然后上传文件时需要显示进度,于是网上搜了一下,经过自己实测终于也弄明白了 二,效果 三,代码 HTML代码 <div id="app"> &l ...