Search code examples
c#mongodbaggregation-frameworkmongodb-.net-driver

How to get the fields/options of an aggregation command from a MongoDB IPipelineStageDefinition in C#?


I need to verify in a unit test that the AggregateAsync method received a specific aggregation command that is represented via a PipelineDefinition<BsonDocument, BsonDocument> instance. The command is $collStats with the additional field/option storageStats. How can I check that the pipeline contains exactly this command, including the additional field?

I can get the individual stages of the pipeline with Stages, so I can verify that there is only a single stage (containing the command above) and then get this stage:

PipelineDefinition<BsonDocument, BsonDocument> pipeline = ...; // obtained from some callback
IPipelineStageDefinition stage = pipeline.Stages.Single();

Now, I can easily check if this stage contains the $collStats command with OperatorName:

if (stage.OperatorName == "$collStats")
{
    // check for additional "storageStats" field
}

But how can I check whether this command also contains the field storageStats? Unfortunately, there does not seem to be a similar property like OperatorFields. The only solution I currently found is to serialize the stage, and then check whether this serialized representation contains the specific field:

using MongoDB.Bson.Serialization;

BsonDocument command = stage.Render(
    BsonSerializer.SerializerRegistry.GetSerializer<BsonDocument>(),
    BsonSerializer.SerializerRegistry).Document;
BsonDocument fields = command.GetValue("$collStats").ToBsonDocument();

if (fields.Contains("storageStats"))
{
    // finally ...
}

This feels way too complex and like the wrong way to do. Is there a simpler solution which I am missing?


Solution

  • To validate a MQL query, you have to render it. The reason for this is that a pipeline can be created in different ways and not always it's based on already rendered BsonDocument. So it might be rendering a stage as in your code above or Render the whole pipeline (which basically the same):

            var registry = BsonSerializer.SerializerRegistry;
            var serializer = registry.GetSerializer<BsonDocument>();
            var renderedStages = pipeline.Render(serializer, registry).Documents
    

    You may also configure event subscriber on CommandStartedEvent (see here), but I doubt a server call is what you need here