Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB C# Aggregation with LINQ

I have a mongo object with these fields:

DateTime TimeStamp;
float    Value;

How can I get the aggregation pipeline, in C#, with LINQ, to get the minimum, maximum and average of Value over a specific timestamp range?

I have seen a few aggregation examples, but I don't quite get it. Having an example on a simple case like this would certainly (hopefully) make me understand it.

like image 824
Thomas Avatar asked Aug 28 '18 20:08

Thomas


2 Answers

You can use LINQ syntax which gets translated into Aggregation Framework's syntax. Assuming you have following Model class:

public class Model
{
    public DateTime Timestamp { get; set; }
    public float Value { get; set; }
}

you can use where to specify timestamp range and then use group with null as grouping key. MongoDB driver will translate Min, Max and Average from anonymous type into $max, $min and $avg from Aggregation Framework syntax

var q = from doc in Col.AsQueryable()
        where doc.Timestamp > DateTime.Now.AddDays(-3)
        where doc.Timestamp < DateTime.Now.AddDays(3)
        group doc by (Model)null into gr
        select new
        {
            Avg = (double)gr.Average(x => x.Value),
            Min = gr.Min(x => x.Value),
            Max = gr.Max(x => x.Value)
        };

var result = q.First();

List of accumulators supported by MongoDB driver can be found here.

EDIT: the (Model)null is required because the query has to be transformed to $group with _id set to null (docs) since you want to get one result with aggregates. Casting is required just for C# compiler purpose as doc is of type Model.

like image 125
mickl Avatar answered Oct 09 '22 18:10

mickl


The aggregation for this is done in two steps:

  1. $match - Retrieve documents with TimeStamp value between some defined minDate and maxDate.
  2. $group - Group on null. This will put all documents in a single group so we can apply an accumulator function across everything from the step 1 $match. The accumulator functions you're looking for are $min, $max, and $avg.

IMongoCollection<Entity> collection = GetMyCollection();

DateTime minDate = default(DateTime); // define this yourself
DateTime maxDate = default(DateTime); // define this yourself

var match = new BsonDocument
{ {
    "$match", new BsonDocument
    { {
        "TimeStamp", new BsonDocument
        { {
            "$and", new BsonDocument
            {
                { "$gt", minDate },
                { "$lt", maxDate }
            }
        } }
    } }
} };

var group = new BsonDocument
{ {
    "$group", new BsonDocument
    {
        { "_id", BsonNull.Value },
        { "min", new BsonDocument { { "$min", "Value" } } },
        { "max", new BsonDocument { { "$max", "Value" } } },
        { "avg", new BsonDocument { { "$avg", "Value" } } },
    }
} };

var result = collection.Aggregate(PipelineDefinition<Entity, BsonDocument>.Create(match, group)).Single();

double min = result["min"].AsDouble;
double max = result["max"].AsDouble;
double avg = result["avg"].AsDouble;
like image 22
Neil Avatar answered Oct 09 '22 20:10

Neil