I am writing a query to do aggregation with respect to one field, however, I want to exclude this aggregation for one value of that field
let coAuthorCutThreshold =100
network.aggregate([
{ $group: { _id: '$title', title :{$ne: "class notes"}, count: { $sum: 1 } }} ,
{ $match: { count: { $gt: coAuthorCutThreshold } } },
{ $sort: { count: -1 } }
]).forEach(function (obj) {
cutTitles.push(obj._id)
});
I want to do aggregation with respect to title but do to not want to do aggregation where the title is "class notes". I tried many command but did not work
I want to include all those values in cutTitles except where title is "class notes"
Match in order to filter out the documents you don't want to group before the group stage.
let coAuthorCutThreshold = 100
network.aggregate([
{ $match: { title: { $ne: "class notes" }}},
{ $group: { _id: '$title', count: { $sum: 1 }}},
{ $match: { count: { $gt: coAuthorCutThreshold }}},
{ $sort: { count: -1 }}
]).forEach(function (obj) {
cutTitles.push(obj._id)
});
Related
I did a lot of research on MongoDB aggregation grouping, but couldn't find a solution.
I have the following document structure:
{
"_id":"6053e0ef22b8e60015a23da8",
"shoppinglist":[
{
"_id":"606ae2e34e4416001538104f",
"items":[
{
"_id":"6071c5ed8f669f0015e6eebe",
"product_id":"605852c28ea29f0015653d6f",
},
...
]
}
}
My goal is to group the items in each shopping list object using the product_id, so that my result looks like this:
{
"_id":"6053e0ef22b8e60015a23da8",
"shoppinglist":[
{
"_id":"606ae2e34e4416001538104f",
"items":[
{
"_id":"6071c5ed8f669f0015e6eebe",
"product_id":"605852c28ea29f0015653d6f",
"count": 3 //3 items with the product_id = 605852c28ea29f0015653d6f
},
...
]
}
}
Can someone help me with this, I'm desperate.
$unwind deconstruct shoppinglist array
$unwind deconstruct shoppinglist.items array
$group by _id and product_id, and get required fields using $first and get count using $sum
$group by _id and shoppinglist._id and reconstruct array of items
$group by _id and reconstruct array of shoppinglist
db.collection.aggregate([
{ $unwind: "$shoppinglist" },
{ $unwind: "$shoppinglist.items" },
{
$group: {
_id: {
_id: "$_id",
product_id: "$shoppinglist.items.product_id"
},
shoppinglist_id: { $first: "$shoppinglist._id" },
items_id: { $first: "$shoppinglist.items._id" },
count: { $sum: 1 }
}
},
{
$group: {
_id: {
_id: "$_id._id",
shoppinglist_id: "$shoppinglist_id"
},
items: {
$push: {
items_id: "$items_id",
product_id: "$_id.product_id",
count: "$count"
}
}
}
},
{
$group: {
_id: "$_id._id",
shoppinglist: {
$push: {
_id: "$_id.shoppinglist_id",
items: "$items"
}
}
}
}
])
Playground
I am looking for a query for a $match stage in my aggregation which do almost the same, as in this question, but..
if field (named rank in my case) doesn't exists in document, add document to results
but if field, exists, apply $operator condition (in my case it's $max) to this field, and add all documents that suits this condition to the results.
MongoPlayground with example collection.
Result should be like this:
[
{
"method": 3,
"item": 1,
"rank": 3 //because it has field named rank, and suits condition {rank: $max}
},
{
"method": 4,
"item": 1 //we need this, because document doesn't have rank field at all
},
{
"method": 5,
"item": 1 //we need this, because document doesn't have rank field at all
}
]
Things, that I have tried already:
{
$match: {
$or: [
{item: id, rank: {$exists: true, $max: "$rank"}}, //id === 1
{item: id, rank: {$exists: false}} //id === 1
]
}
}
UPD: As for now, probably I don't limit with $match stage only, $project is also relevant after default match, so I could request every document during $match stage by id no matter, have the doc rank field or not, and then, during $project stage do a "separation" by rank $exists
Try this one:
db.collection.aggregate([
{
$match: {
item: id
}
},
{
$group: {
_id: "$item", //<- Change here your searching field
max: {
$max: "$rank" //<- Change here your field to apply $max
},
data: {
$push: "$$ROOT"
}
}
},
{
$unwind: "$data"
},
{
$match: {
$expr: {
$or: [
{
$eq: [
{
$type: "$data.rank"
},
"missing"
]
},
{
$eq: [
"$data.rank",
"$max"
]
}
]
}
}
},
{
$replaceWith: "$data"
}
])
MongoPlayground
I have found an answer, separated from #Valijon's method, but it's also based on the logic above. My query is:
db.collection.aggregate([
{
$match: {
item: id
}
},
{
$project: {
method: 1,
item: 1,
rank: {
$ifNull: [
"$rank",
0
]
}
}
},
{
$group: {
_id: "$item",
data: {
$addToSet: "$$ROOT"
},
min_value: {
$min: "$rank"
},
max_value: {
$max: "$rank"
}
}
},
{
$unwind: "$data"
},
{
$match: {
$or: [
{
$expr: {
$eq: [
"$data.rank",
"$max_value"
]
}
},
{
$expr: {
$eq: [
"$data.rank",
"$min_value"
]
}
},
]
}
}
])
My query is based on $project stage which gives the empty field value 0. It also could be -1, or any value that isn't used in collection. And then I separate results.
MongoPlayground
I am trying to aggregate on my User collection and $project out the fields I need. Then I want to set let variables in $lookup to be able to use the variable to find matching documents.
I want to do it this way, because I will have many $lookups by the variables.
However I am not able to get this working correctly. What am I doing wrong with the variables?
$project: {
_id: 1,
name: 1,
goal: 1
}
},
{
$lookup: {
from: "goals",
let: { user: "$_id" },
pipeline: [
{
$match: {
"user": "$$user"
}
},
{ $project: { _id: 0, leads: 0 } },
],
as: "goal"
}
},
{
$project: {
_id: 1,
name: 1,
goal: 1
}
},
You need to use $expr to use variable name inside the $match stage.
{ $match: { $expr: { $eq: ['$user', '$$user'] }}}
If I want to sum up numerical values in MongoDB I do it like this:
totalOpenBalance: {
$sum: "$openBalance"
} // sum up all "openBalance" values
But what I'm wondering is, what operator do I use when I want to sum up instances of something? Say if I have a property such as customer_id, and data that looks like this:
{
"customer_id" : 445,
"other_prop" : value
},
{
"customer_id" : 446,
"other_prop" : value
},
Note that I don't want to sum up the values assigned to "customer_id", but rather tally up how many instances of "customer_id" are in the collection of data. In other words, according to the data above, I should get "2" as my output. What operator do I use to do that?
To clarify, this is a step I need to add to an aggregation pipeline I'm using to generate a mongo view.
Any of the below should get you going:
Simple find:
db.collection.find({
"customer_id": { $exists: true }
}).count()
Aggregate with $count:
db.collection.aggregate({
$match: {
"customer_id": { $exists: true }
}
}, {
$count: "numberOfInstances"
})
Aggregate with $group:
db.collection.aggregate({
$match: {
"customer_id": { $exists: true }
}
}, {
$group: {
_id: null,
"numberOfInstances": { $sum: 1 } // count instances
}
})
You can simply use find and $exists and then count the returned rows
db.collection.find( { customer_id: { $exists: true } } ).count()
Or if you want to use aggregate (which I don't think you should do for such simple task) this is how you can do it.
db.collection.aggregate({
$match: {
"customer_id": {
$exists: true
}
}
}, {
$group: {
_id: null,
"total": {
$sum: 1
}
}
})
Here total property will give you the number of instances containing customer_id in them
Model.aggregate([
{
'$group': {
'_id': '$id',
'name': { '$first': '$name' },
'tof': { $sum: { $eq:["$tof",true] } },
}
}
]) .... // rest of the code.
I am trying to sum the tof (true or false) field, but only if the value is true, but is not working like the way i am trying..
What i am doing wrong here? How proceed?
Thanks!!
Instead of extracting and iterating, as you would in this case, documents you don't need using $cond you can just omit them from the find:
Model.aggregate([
{
'$match': {'tof': true}
},
{
'$group': {
'_id': '$id',
'name': { '$first': '$name' },
'tof': { $sum: 1 },
}
}
])
However, since you are using multiple counts you need $cond ( http://docs.mongodb.org/manual/reference/operator/aggregation/cond/ ) so here is an example:
Model.aggregate([
{
'$group': {
'_id': '$id',
'name': { '$first': '$name' },
'tof': { $sum: {$cond: [{$eq:['$tof', true]}, 1, 0]} },
}
}
])