Reputation: 2724
I have a collection with this document in MongoDB
string _id;
string item;
string category;
int mobileView;
int webView;
I want to take 10 items sorted descending by the total sum of mobileView
and webView
, is there anyway I can achieve this directly just by querying, or should I get all the data and loop through it all and sum the mobileView
and webView
manually before taking only the 10 with highest total?
I've been reading about the Aggregation Framework
, but from what I understand, it Sum
only one field that's been grouped by the query. Did what I understand is wrong, if so how to do it using the Aggregation Framework
.
(I don't want the total Sum
of the View
to be grouped by the same category
, I need it to only Sum
the View
on each item before sort it)
Upvotes: 1
Views: 1648
Reputation: 1
I actually wrote a Linq provider, MongoLinqPlusPlus, to generate simple aggregation framework pipelines such as yours.
So with MongoLinqPlusPlus your query simply becomes:
collection.QueryablePlusPlus()
.Select(c => new {
OriginalDoc = c,
SortKey = c.mobileView + c.webView
})
.OrderByDescending(c => c.SortKey)
.Take(10)
.Select(c => c.OriginalDoc)
(* It would be nice if we could do the arithmetic in the sort (.OrderByDescending(c => c.mobileView + c.webView) but the aggregation framework doesn't support this directly. So I chose to require the developer the explicitly create the projection rather than have it implicitly created by the Linq provider.)
Upvotes: 0
Reputation: 151122
The aggregation framework is what you want here. Not only for "aggregating" but for "manipulation" of documents as well. So basically you want the $add
operator and the $sort
pipeline to get your desired result.
Will take me a few moments to fire up a Visual Studio, so here is the shell form that is not hard to translate:
db.collection.aggregate([
{ "$project": {
"item": 1,
"category": 1,
"mobileView": 1,
"webView": 1,
"totalView": { "$add": [ "$mobileView", "$webView" ] }
}},
{ "$sort": { "totalView": -1 } }
])
So basically "calculate" a field and then use that "projected" result as the field to $sort
on.
Just BSON documents, so it should not be to hard.
Specifically for C# syntax do something like this:
var project = new BsonDocument {
{
"$project",
new BsonDocument()
.Add( "item", 1 )
.Add( "category", 1 )
.Add( "mobileView", 1 )
.Add(
"totalView",
new BsonDocument()
.Add( "$add", new BsonArray {
"$mobileView", "$webView"
})
)
}
};
var sort = new BsonDocument {
{
"$sort",
new BsonDocument {
{ "totalView", -1 }
}
}
};
var pipeline = new[] { project, sort };
var results = collection.Aggregate(pipeline);
All of the $project
items just as with $group
are "explicitly" named fields to include in the stage output. In the aggregation framework, the fields you want are "all or nothing" and you must specify each one you expect to be visible in the next pipeline stage or indeed at the end of the pipeline. Fields not mentioned no longer exist as far as the pipeline is concerned.
Think unix style "pipe" |
where you "chain" results of one command to another, which is a clear analogy for what is happening here.
Upvotes: 3