Group count 2 item object array mongodb - mongodb

I have the following array:
[
{
“counter”: 123456,
“user”: “USER1”,
“last”: “USER1”
},
{
“counter”: 123,
“user”: “USER1”,
“last”: “USER2”
},
{
“counter”: 111,
“user”: “USER2”,
“last”: “USER2”
},
{
“counter”: 1122,
“user”: “USER2”,
“last”: “USER2”
},
{
“counter”: 112233,
“user”: “USER1”,
“last”: “USER2”
},
]
I'm doing the following query on mongodb:
{$group: {
_id: “$user”,
total: {$sum: 1},
last: {$sum: {$cond: [
{$eq: ['$last’, '$user']}, 1, 0
]}}
}}
I would like to get the following result:
[
{
_id: “USER1”
total: 3,
last: 1
},
{
_id: “USER2”
total: 2,
last: 4
}
]
But I get this:
[
{
_id: “USER1”
total: 3,
last: 1
},
{
_id: “USER2”
total: 2,
last: 2
}
]
When I make the group I can not count the last item satisfactorily
How can I get the expected result? Thank you for your help.

You have only 2 "USER2" then can't $sum 4 $last on that _id.
try using compose _id if you need by index in user.
db.teste.aggregate([
{$group:{
_id: {user:"$user",last:"$last"},
total: {$sum:1},
last: {
$sum: {
$cond:[ {$eq: ["$last", user]}, 1, 0]
}
}
}}
])
If you need to add string or other occurrences in different fields. Query is:
db.teste.aggregate([
{$group:{
_id: "$user"
}},
{$lookup:{
from: "teste",
let: { indice: "$_id" },
pipeline: [
{$group:{
_id: null,
user:{$sum:{$cond:[
{$eq:["$user", "$$indice"]}, 1, 0
]}},
last:{$sum:{$cond:[
{$eq:["$last", "$$indice"]}, 1, 0
]}}
}},
{$project:{
_id: 0
}}
],
as: "res"
}}
])
From zero to hero:
//First we'll extract only what we need find, on this case distinct users
db.teste.aggregate([
{$group:{
_id: "$user"
}}
])
//Here we will get the indexes of the search and reinsert in our filter using $let (nodejs var)
db.teste.aggregate([
{$group:{
_id: "$user"
}},
{$lookup:{
from: "teste",
let: { indice: "$_id" },
pipeline: [],
as: "res"
}}
])
//Let's counter the total of reinserted elements
db.teste.aggregate([
{$group:{
_id: "$user"
}},
{$lookup:{
from: "teste",
let: { indice: "$_id" },
pipeline: [
{$group:{
_id: null,
total:{$sum:1}
}}
],
as: "res"
}}
])
//Now let's test a true condition
db.teste.aggregate([
{$group:{
_id: "$user"
}},
{$lookup:{
from: "teste",
let: { indice: "$_id" },
pipeline: [
{$group:{
_id: null,
user:{$sum:{$cond:[
{$eq:[true, true]}, 1, 0
]}}
}}
],
as: "res"
}}
])
//cond tested let's extract what we want
db.teste.aggregate([
{$group:{
_id: "$user"
}},
{$lookup:{
from: "teste",
let: { indice: "$_id" },
pipeline: [
{$group:{
_id: null,
user:{$sum:{$cond:[
{$eq:["$user", "$$indice"]}, 1, 0
]}},
last:{$sum:{$cond:[
{$eq:["$last", "$$indice"]}, 1, 0
]}}
}}
],
as: "res"
}}
])
//Let's take the _id of the sub-colection because we do not need it
db.teste.aggregate([
{$group:{
_id: "$user"
}},
{$lookup:{
from: "teste",
let: { indice: "$_id" },
pipeline: [
{$group:{
_id: null,
user:{$sum:{$cond:[
{$eq:["$user", "$$indice"]}, 1, 0
]}},
last:{$sum:{$cond:[
{$eq:["$last", "$$indice"]}, 1, 0
]}}
}},
{$project:{
_id: 0
}}
],
as: "res"
}}
])

Related

how to use $match after $group in mongodb aggregation

I have 4 products. I want to know the count of product-4 for users who has product-1 or product-2
Sample data:
[
{
"user_id": 1,
"product_type": "product-1"
},
{
"user_id": 1,
"product_type": "product-4"
},
{
"user_id": 1,
"product_type": "product-4"
},
{
"user_id": 2,
"product_type": "product-1"
}
]
user-1 has two product-4 and one product-1 (that counts 2)
user-2 has only product-1, but no product-4 (hence that does not count)
This is how I tried
db.collection.aggregate([
{
$match: {
product_type: {
$in: [
"product-1​",
"product-2",
],
},
},
},
{
$group: {
_id: "$user_id",
},
},
{
$match: {
user_id: { $in: "$_id"}, // I want to use $group's result in here
product_type: "product-4",
},
}
]);
Expected results are:
[
{
"_id": 1,
"count": 2
},
{
"_id": 2,
"count": 0
}
]
Note:
I dont have a backend, I have to this using mongodb only.
Does this answer your question?
db.collection.aggregate([
{$group: {_id: "$user_id", data: {$push: "$product_type"}}},
{$match: {$expr: {$or: [
{$in: ["product-1", "$data"]},
{$in: ["product-2", "$data"]}
]}}},
{$project: {
count: {
$size: {
$filter: {
input: "$data",
cond: {$eq: ["$$this", "product-4"]}
}
}
}
}}
])
See how it works on the playground example

mongodb aggregation with filter options

I have two collections where I'm trying to do an aggregation query with filter options. I have looked online but I couldn't find solution for this.
Col 1
[
{
_id: ObjectId('st_123'),
stud_num: 123,
school: ObjectId('sc_123'),
gender: 'M'
},
{
_id: ObjectId('st_234'),
stud_num: 123,
school: ObjectId('sc_234'),
gender: 'F'
},
{
_id: ObjectId('st_345'),
stud_num: 123,
school: ObjectId('sc_345'),
gender: 'M'
}
]
Col 2
[
{
_id: ObjectId('f_123'),
stud_health_id: ObjectId('st_123'),
schoolYear: ObjectId('sy123')
},
{
_id: ObjectId('f_234'),
stud_health_id: ObjectId('st_234'),
schoolYear: ObjectId('sy234')
},
{
_id: ObjectId('f_345'),
stud_health_id: ObjectId('st_890'),
schoolYear: ObjectId('sy234')
},
{
_id: ObjectId('f_456'),
stud_health_id: ObjectId('st_345'),
schoolYear: ObjectId('sy345')
}
]
I am trying to filter the records from collection 1 which doesn't have entry in collection 2 with extra params.
If I send {schoolYear: ObjectID('sy234)} then it should return the first and third document of collection 1 because for that year those two students doesn't have record.
One option is using $lookup and $match:
db.col1.aggregate([
{$lookup: {
from: "col2",
as: "col2",
let: {schoolYear: "sy234", stud_id: "$_id"},
pipeline: [
{$match: {$expr: {
$and: [
{$eq: ["$schoolYear", "$$schoolYear"]},
{$eq: ["$stud_health_id", "$$stud_id"]}
]
}
}
}
]
}
},
{$match: {"col2.0": {$exists: false}}},
{$unset: "col2"}
])
See how it works on the playground example

MongoDB Aggregation with Sum and Multiple Group Results

Let's say I have these collections members and positions
[
{
"church":"60dbb265a75a610d90b45c6b", "parentId":"60dbb265a75a610d90b45c6b", name: "Jonah John", status: 1, birth: "1983-01-01", position: "60f56f59-08be-49ec-814a-2a421f21bc08"
},
{
"church":"60dbb265a75a610d90b45c6b", "parentId":"60dbb265a75a610d90b45c6b", name: "March John", status: 1, birth: "1981-01-23", position: "60f56f59-08be-49ec-814a-2a421f21bc08"
},
{
"church":"60dbb265a75a610d90b45c6b", "parentId":"60dbb265a75a610d90b45c6b",name: "Jessy John", status: 0, birth: "1984-08-01", position: "e5bba609-082c-435a-94e3-0997fd229851"
}
]
[
{_id: "60f56f59-08be-49ec-814a-2a421f21bc08", name: "Receptionist"},
{_id: "5c78ba5a-3e6c-4d74-8d4a-fa23d02b8003", name: "Curtain"},
{_id: "e5bba609-082c-435a-94e3-0997fd229851", name: "Doorman"}
]
I want to aggregate in a way I can get:
inactiveMembers
activeMembers
totalMembers
totalPositionsOcuppied
And two arrays with:
positionsOcuppied {name, quantity}
birthdays {month, quantity.
I need an output like this:
{
"_id": {
"church":"60dbb265a75a610d90b45c6b",
"parentId":"60dbb265a75a610d90b45c6b"
},
"inactiveMembers":1,
"activeMembers":2,
"totalMembers":3,
"birthdays": [
{january:2}, {august:1}
],
"positionsOcuppied": [
{Doorman: 1}, {Receptionist:2}
],
"totalPositionsOcuppied": 3
}
How can I do that?
PS.: Very sorry for unclear values...
Update:
$addFields with birthMonth string
$lookup to add positions
$facet to $group by birthdays, positionsOcuppied, and all docs tougher as other
$map to format birthdays and positionsOcuppied
Format the answer
db.people.aggregate([
{$addFields: {
birthMonth: {
$arrayElemAt: [
["","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
{$month: {$toDate: "$birth"}}
]
}
}
},
{$lookup: {from: "positions", localField: "position", foreignField: "_id",
as: "position"}},
{$facet: {
birthdays: [{$group: {_id: "$birthMonth", count: {$sum: 1}}}],
positionsOcuppied: [{$group: {_id: {$first: "$position.name"}, count: {$sum: 1}}}],
other: [
{$group: {_id: 0,
activeMembers: {$sum: "$status"},
totalMembers: {$sum: 1},
church: {$first: "$church"},
parentId: {$first: "$parentId"},
totalPositionsOcuppied: {$sum: {$size: "$position"}}
}
}
]
}
},
{$set: {
birthdays: {
$map: {input: "$birthdays", in: [{k: "$$this._id", v: "$$this.count"}]}
},
positionsOcuppied: {
$map: {input: "$positionsOcuppied", in: [{k: "$$this._id", v: "$$this.count"}]}
},
other: {$first: "$other"}
}
},
{$set: {
"other.birthdays": {
$map: {input: "$birthdays", in: {$arrayToObject: "$$this"}}
},
"other.positionsOcuppied": {
$map: {input: "$positionsOcuppied", in: {$arrayToObject: "$$this"}}
},
"other.inactiveMembers": {
$subtract: ["$other.totalMembers", "$other.activeMembers"]
},
"other._id": {church: "$other.church", parentId: "$other.parentId"},
birthdays: "$$REMOVE",
"other.church": "$$REMOVE",
"other.parentId": "$$REMOVE",
positionsOcuppied: "$$REMOVE"
}
},
{$replaceRoot: {newRoot: "$other"}}
])
See how it works on the playground example

Aggregate function two match with lookup mongodb

db.setting.aggregate([
{
$match: {
status: true,
deleted_at: 0,
_id: {
$in: [
ObjectId("5c4ee7eea4affa32face874b"),
ObjectId("5ebf891245aa27c290672325")
]
}
}
},
{
$lookup: {
from: "site",
localField: "_id",
foreignField: "admin_id",
as: "data"
}
},
{
$project: {
name: 1,
status: 1,
numberOfRecord: {
$size: "$data"
}
}
},
{
$sort: {
numberOfRecord: 1
}
}
])
I would like to fetch record and number of record which are greater then equal to 2020-01-01 tried to add below code but doesn't get success.
How can I make this correct please guide thanks in advance. Here is playground https://mongoplayground.net/p/GU8WbTVqo2I
{
$match: {
"data.createdAt": {
$gte: new Date("2020-01-01")
}
}
},
Output should be
[
{
"_id": ObjectId("5ebf891245aa27c290672325"),
"name": "Menz",
"numberOfRecord": 0,
"status": true
},
{
"_id": ObjectId("5c4ee7eea4affa32face874b"),
"name": "Dave",
"numberOfRecord": 1, // instead 2 bcoz this is only gte "2020-01-01"
"status": true
}
]
You can use $unwind to split up the array, but using $match will completely eliminate documents that don't have any matching documents, so you would need to used $group with a conditional count, perhaps:
db.setting.aggregate([
{$match: {
status: true,
deleted_at: 0,
_id: {
$in: [
ObjectId("5c4ee7eea4affa32face874b"),
ObjectId("5ebf891245aa27c290672325")
]
}
}},
{$lookup: {
from: "site",
localField: "_id",
foreignField: "admin_id",
as: "data"
}},
{$unwind: {
path: "$data",
preserveNullAndEmptyArrays: true
}},
{$group: {
_id: "$_id",
name: {$first: "$name"},
status: {$first: "$status"},
numberOfRecord: {
$sum:{
$cond:{
if:{
$gte:[
"$data.createdAt",
new Date("2020-01-01")
]
},
then: 1,
else: 0
}
}
}
}},
{$sort: { numberOfRecord: 1 }}
])
Playground

let in lookup whit objectId

I want to use some ObjectId in my let but i have the same error every time :
Failed to execute script.
Error: invalid object id: length
Details:
#(shell):6:36
My query :
db.projects.aggregate([
{$lookup: {
from: 'pointValueChangements',
let: {id: '$_id'},
pipeline: [
{ $match: { projectId: new ObjectId('$$id'), date: {$lte: new Date()}}},
{ $sort: { date: -1}},
{ $limit: 1},
],
as: 'pointValue',
}},
{$project: {pointValue: 1}}
])
If i replace new ObjectId('$$id') by '$$id' the query work but the result is not good.
If i replace '$$id' by ObjectId(someMongoId) all is ok (but is not good because this id match only for one row...)
Try this query
db.projects.aggregate([
{$lookup: {
from: 'pointValueChangements',
let: {id: '$_id'},
pipeline: [
{ $match: {
$expr:
{
$and:
[
{ $eq: ['$projectId', '$$id'] },
{ date: {$lte: new Date() }},
],
},
}},
{ $sort: { date: -1}},
{ $limit: 1},
],
as: 'pointValue',
}},
{$project: {pointValue: 1}}
])