Aggregate is not a function - mongodb/meteor - mongodb

I have this collection (Spieltag) with two documents in MongoDB:
0: Object Note:2.5 SaisonID:201516 SpielerID:105 SpieltagID:1 Tore:1 _id:"vkD5sMCdZdntoCFGP"
1: Object Note:3 SaisonsID:201516 SpielerID:105 SpieltagID:1 Tore:0 _id:"PrqokMS47K4vx4KR4"
I want to summarize Note (2.5+1) with a "where clause" on SpielerID.
This is what I have tried to use:
Spieltag.aggregate({ $match: {
{ SpielerID: { $gte: 105 } }
} },
{ $group: { _id : null, sum : { $sum: "$Note" } } });
But it doesn't work, throwing Aggregate is not a function. Any idea what's wrong?

First, you need to add the aggregate package for Meteor :
meteor add meteorhacks:aggregate
Second, you must pass an array parameter in aggregate like :
Spieltag.aggregate([{
$match: {
SpielerID: { $gte: 105 },
},
}, {
$group: {
_id: null,
sum: { $sum: '$Note' },
},
}]);

Related

Mongoose - filter matched documents and assign the resultant length to a field

I have this collection(some irrelevant fields were omitted for brevity):
clients: {
userId: ObjectId,
clientSalesValue: Number,
currentDebt: Number,
}
Then I have this query that matches all the clients for a specific user, then calculates the sum of all debts and sales and put those results in a separate field each of them:
await clientsCollection.aggregate([
{
$match: { userId: new ObjectId(userId) }
},
{
$group: {
_id: null,
totalSalesValue: { $sum: '$clientSalesValue' },
totalDebts: { $sum: '$currentDebt' },
}
},
{
$unset: ['_id']
}
]).exec();
This works as expected, it returns an array with only one item which is an object, but now I need to also include in that resultant object a field for the amount of debtors, that is for the amount of clients that have currentDebt > 0, how can I do that is the same query? is it possible?
PD: I cannot modify the $match condition, it need to always return all the clients for the corresponding users.
To include a count of how many matching documents have a positive currentDebt, you can use the $sum and $cond operators like so:
await clientsCollection.aggregate([
{
$match: { userId: new ObjectId(userId) }
},
{
$group: {
_id: null,
totalSalesValue: { $sum: '$clientSalesValue' },
totalDebts: { $sum: '$currentDebt' },
numDebtors: {
$sum: {
$cond: [{ $gt: ['$currentDebt', 0] }, 1, 0]
}
},
}
},
{
$unset: ['_id']
}
]).exec();

Combining two mongo results into one output?

I am using mongoDB, but I am a complete beginner. I have two different queries where I want to combine them both into one output (I'm hoping the answer is a single query)
Query 1:
db.fin.aggregate([
{ "$match": { "misc.incident_characteristics": { "$not": /Officer Involved Incident/ } } },
{ $group: {
_id: "NonOfficerInvolved",
nInjured: { $avg: "$casualties.n_injured" },
nKilled: { $avg: "$casualties.n_killed" }
}
}
])
Which returns
{ "_id" : "NonOfficerInvolved", "nInjured" : 0.5048153043227224, "nKilled" : 0.24339953718948618 }
Query 2:
db.fin.aggregate([
{ $match: { "misc.incident_characteristics": "Officer Involved Incident" } },
{ $group: {
_id: "OfficerInvolved",
nInjured: { $avg: "$casualties.n_injured" },
nKilled: { $avg: "$casualties.n_killed" }
}
}
])
Which returns
{ "_id" : "OfficerInvolved", "nInjured" : 0.3599233845980508, "nKilled" : 0.358965692073686 }
I would like to get the result of both into one table as seen below. Is it possible to do this in one query?
{ "_id" : "NonOfficerInvolved", "nInjured" : 0.5048153043227224, "nKilled" : 0.24339953718948618 }
{ "_id" : "OfficerInvolved", "nInjured" : 0.3599233845980508, "nKilled" : 0.358965692073686 }
Yes, you can definitely do that in one query.
Instead of just matching against the magic string, store the match result in a new field as a boolean or string, then group on that new field.
db.fin.aggregate([
{ "$addFields": { type:{
$cond:[
{$eq:["$misc.incident_characteristics","Officer Involved Incident"]},
"OfficerInvolved",
"NonOfficerInvolved"
]
}}},
{ $group: {
_id: "$type",
nInjured: { $avg: "$casualties.n_injured" },
nKilled: { $avg: "$casualties.n_killed" }
}
}
])
To match using a regular expression, replace the line
{$eq:["$misc.incident_characteristics","Officer Involved Incident"]},
with
{$regexMatch:{
input:"$misc.incident_characteristics",
regex:"Officer Involved Incident"
options:"i"
}},

how to add a dot (.) inside a field name?

When I run this command :
db.runCommand(
{
aggregate:"myColl",
pipeline:[
{
$group:{
_id:{os_name:"$os_name",os_version:"$os_version"},
"events.login":{$sum:"$events.login"},
count:{$sum:NumberInt(1)}}
}
],
cursor:{}
}
)
I receive the error:
The field name 'events.login' cannot contain '.'
How can i do to keep the '.' in the returned field name (ie: events.login)
It's not quiet clear what you're trying to do, So look at these :
Error :
The field name 'events.login' cannot contain '.'
It's because as in $group stage when it creates a new field on each document for a given name, You'll not be able to create it like this : "events.login" means cannot create a sub-doc it has to be an object to a top level field events basically you can not use . notation, So to make it work you need to have one more stage like this :
db.collection.aggregate([{
$group: {
_id: { os_name: "$os_name", os_version: "$os_version" },
"events": { $sum: "$events.login" },
count: { $sum: NumberInt(1) }
}
}, { $addFields: { 'events.login': '$events' } }])
Test : MongoDB-Playground
If in a case you need to update login field inside events field & to retain all other fields inside events, try below query which would get last document in iteration of each _id (this will last document inserted to DB on that _id criteria) & append login to it :
db.collection.aggregate([{
$group: {
_id: { os_name: "$os_name", os_version: "$os_version" },
"login": { $sum: "$events.login" }, 'events': { $last: '$events' },
count: { $sum: NumberInt(1) }
}
}, { $addFields: { 'events.login': '$login' } }, { $project: { login: 0 } }])
Test : MongoDB-Playground

Delete multiple occurences of same object

In our collection, there's structure like:
Object: //below is object metadata from mongo
_id
created_at
lang
source
object: //this is real object data from our db
id
created_at
object_class
I ran below query on this collection:
db.getCollection('foo').aggregate(
[
{
$match: {
lang: 'bar',
pushed_at:{
$gte: new ISODate("2015-11-09T00:00:00.000Z"),
$lt: new ISODate("2015-11-10T00:00:00.000Z")
}
}
},
{
$group: {
_id: "$object.id",
occurences: {$sum: 1}
}
},
{
$match: {
occurences: {$gt: 1}
}
}
])
Which returned:
It appears that we got duplicate entries in our collection. By duplicate I mean objects with same Object.object.id.
I'd like to remove redundant occurences using results from agreggate function I used. Notice that I don't want to delete anything, just rendundant ones, so above aggregate returns occurences: 1.
How to do this, also using results from aggregation?
I think you can try that in the shell :
db.foo.aggregate(
[
{
$match: {
lang: 'bar',
pushed_at:{
$gte: new ISODate("2015-11-09T00:00:00.000Z"),
$lt: new ISODate("2015-11-10T00:00:00.000Z")
}
}
},
{
$group: {
_id: "$object.id",
occurences: {$sum: 1}
}
},
{
$match: {
occurences: {$gt: 1}
}
}
]).result.forEach(function(x) {
if(x.occurences > 1) {
for(i=0;i<x.occurences - 1;i++) {
db.foo.remove({"object.id":x._id}, true);
}
}
}
);

Mongoose cant seem to aggregate a sum and return all the items

I have this mongoose query that im running
db.accounts.aggregate([{
$unwind: "$Publishers"
}, {
$group: {
_id: "$Profile._id",
reachTotal: {
$sum: "$Publishers.reach"
},
Publishers: {
$push: "$Publishers"
},
Profile: {
$first: "$Profile"
}
}
}, {
$sort: {
reachTotal: 1
}
}])
It works fine, but the problem is that some of the records dont have '$Publishers.reach'. Mongoose doesn't return those records with the sum of null, undefined or 0. Is there a way to have mongoose return them?
You'll have to tell mongodb what to do in the form of a cond statement or the ifNull statement depending on your document structure.
http://docs.mongodb.org/manual/reference/operator/aggregation/cond/
http://docs.mongodb.org/manual/reference/operator/aggregation/ifNull/
...
reachTotal: { $sum: { $ifNull: ["$Publishers.reach", 0] } }