collection Status:
[
{
_id: '1',
StatusName: '....',
Comments: [
{
id_: '2',
CommentName: '.....',
Likes: ['user1', 'user2', 'user3']
},
{
id_: '3',
CommentName: '.....',
Likes: ['user2', 'user3']
}
]
},
{
_id: '3',
StatusName: '....',
Comments: [
{
id_: '4',
CommentName: '.....',
Likes: ['user1', 'user2', 'user3', 'user4']
},
{
id_: '5',
CommentName: '.....',
Likes: ['user1', 'user3']
}
]
}
]
I want count Like of Comment _id = '2' and Status _id: '1'
One way is to use aggregation framework :
db.status.aggregate(
[
{$match : {_id : "1", }},
{$unwind: "$Comments"},
{$match: {"Comments.id_": "2"}},
{$unwind: "$Comments.Likes"},
{$group: {_id: {"statusId":"$_id","CommentId":"$Comments.id_"},
"likes": {$sum:1}}
}
] )
Related
simple collection:
[
{
_id: '123',
name: 'product1',
quantity: 10
},{
_id: '456',
name: 'product2',
quantity: 20
},{
_id: '789',
name: 'product1',
quantity: 30
},{
_id: '145',
name: 'product3',
quantity: 30
}
]
what im trying to get:
[
{
_id: 'product1',
quantity: 40
},{
_id:'product2',
quantity: 20
}
]
I want to filter by product1 and product2 and group by the 2 products with the total quantity.
what i tried:
db.CustomerOrders.aggregate([
{
"$match":{
"$or":[{"name":"product1"},{"name": "product2"}
]}},
{
"$group": {_id:"$name", quantity:{$sum:"$quantity"}}
}
}])
Code doesnt seem to work
Use $in in your $match stage.
db.collection.aggregate([
{
"$match": {
name: {
$in: [
"product1",
"product2"
]
}
}
},
{
$group: {
_id: "$name",
quantity: {
$sum: "$quantity"
}
}
}
])
Mongo Playground
Currently I'm using aggregation in MongoDB. There is a field with province and religion in my collections. I'm doing this
const data = await submit.aggregate([
{ "$group": { _id: { province: "$province" ,religion:"$religion"}, count: { $sum: 1 } } },
])
My output Looks like this:
[
{ _id: { religion: 'a', province: 'aa' }, count: 1 },
{ _id: { religion: b, province: 'bb' }, count: 2 },
{ _id: { religion: 'c', province: 'bb'}, count: 2 },
{ _id: { religion: 'd', province: 'cc' }, count: 1 }
]
Expect Output:
[
{ _id: { religion: 'a ' }, count: 1 },
{ _id: { religion: 'a' }, count: 1 },
{ _id: { religion: null }, count: 6 },
{ _id: { religion: 'c' }, count: 1 },
{ _id: { religion: 'd' }, count: 2 },
{ _id: { religion: 'e' }, count: 6 },
{ _id: { religion: 'f' }, count: 15 },
{ _id: { religion: 'g' }, count: 2 },
] [
{ _id: { province: 'aa' }, count: 19 },
{ _id: { province: 'bb' }, count: 2 },
{ _id: { province: 'cc' }, count: 21 },
]
You seek 2 different $group at the same time -- this is exactly what $facet is for. Think of $facet like "multi-group." Given an input set similar to the following:
{ religion: 'a', province: 'aa' },
{ religion: 'b', province: 'aa' },
{ religion: 'c', province: 'aa' },
{ religion: 'c', province: 'bb' },
{ religion: 'd', province: 'bb' },
{ religion: 'e', province: 'cc' },
{ religion: 'f', province: 'aa' },
{ religion: 'f', province: 'aa' },
{ religion: 'f', province: 'aa' },
{ religion: 'f', province: 'cc' }
Then this pipeline:
db.foo.aggregate([
{$facet: {
"by_religion": [
{$group: {_id: '$religion', N:{$sum:1}}}
],
"by_province": [
{$group: {_id: '$province', N:{$sum:1}}}
],
}}
]);
yields this output:
{
"by_religion" : [
{
"_id" : "b",
"N" : 1
},
{
"_id" : "e",
"N" : 1
},
{
"_id" : "d",
"N" : 1
},
{
"_id" : "a",
"N" : 1
},
{
"_id" : "f",
"N" : 4
},
{
"_id" : "c",
"N" : 2
}
],
"by_province" : [
{
"_id" : "bb",
"N" : 2
},
{
"_id" : "cc",
"N" : 2
},
{
"_id" : "aa",
"N" : 6
}
]
}
The OP seeks to further refine the output by doing some data-as-LVAL workup and although this is in general considered a poor design practice, it has certain useful applications. Add this stage after $facet:
,{$project: {
// Reading this from insider-out:
// We use $map to turn the array of objects:
// [ {_id:'d',N:1},{_id:'f',N:4}, ... ]
// into an array of K-v pairs (array of array):
// [ ['d',1] , ['f',4] , ... ]
// That sets us up for $arrayToObject which will take
// that array of arrays and turn it into an object:
// {'d':1, 'f':4, ... }
// The target field name is the same as the input so
// we are simply overwriting the field.
"by_religion": {$arrayToObject: {$map: {
input: '$by_religion',
in: [ '$$this._id', '$$this.N' ]
}}
},
"by_province": {$arrayToObject: {$map: {
input: '$by_province',
in: [ '$$this._id', '$$this.N' ]
}}
}
}}
to yield:
{
"by_religion" : {
"d" : 1,
"b" : 1,
"c" : 2,
"f" : 4,
"a" : 1,
"e" : 1
},
"by_province" : {
"bb" : 2,
"cc" : 2,
"aa" : 6
}
}
A variation on the lval/rval workup uses this $project instead of the one immediately above:
,{$project: {
"by_religion": {$map: {
input: '$by_religion',
in: {$arrayToObject: [ [{k:'$$this._id',v:'$$this.N'}] ]}
}},
"by_province": {$map: {
input: '$by_province',
in: {$arrayToObject: [ [{k:'$$this._id',v:'$$this.N'}] ]}
}},
}}
which yields an array:
{
"by_religion" : [
{"b" : 1},
{"c" : 2},
{"a" : 1},
{"f" : 4},
{"d" : 1},
{"e" : 1}
],
"by_province" : [
{"cc" : 2},
{"aa" : 6},
{"bb" : 2}
]
}
I am sure it was answered before but I can't find any proper explanation, maybe the aggregate methods I assumed are not relevant here.
I have a mongo collection containing an array which points to sub-docs
{
_id: '123',
name: 'my shop',
items: [
{
itemId: '234',
},
{
itemId: '345',
},
]
}
This is the collection of sub docs:
{
_id: '234',
name: 'apple',
amount: 13
},
{
_id: '345',
name: 'orange',
amount: 25
},
How can I replace in the upper document the id-reference to the actual document content.
Desired final result:
{
_id: '123',
name: 'my shop',
items: [
{
itemId: '234',
name: 'apple',
amount: 13
},
{
itemId: '345',
name: 'orange',
amount: 25
},
]
}
Let's say sample data for shop collection is:
{
_id: '123',
name: 'my shop',
items: [
{
itemId: '234',
},
{
itemId: '345',
},
]
}
And the sample data for item collection is:
{
_id: '234',
name: 'apple',
amount: 13
},
{
_id: '345',
name: 'orange',
amount: 25
}
Then the aggregation will be like this one:
db.shop.aggregate([{
$lookup: {
from: 'item',
localField: 'items.itemId',
foreignField: '_id',
as: 'items'
}
}])
I have two contracts.
// UserInfo
{ _id: '1', name: 'Alice' }
{ _id: '2', name: 'Bob' }
{ _id: '3', name: 'Charlie' }
// Application
{ _id: '00', userId: '1', jobId: 'gg' }
{ _id: '01', userId: '1', jobId: 'fb' }
{ _id: '02', userId: '2', jobId: 'ms' }
I want to get:
{ name: 'Alice', jobIds: [ 'gg', 'fb' ] }
{ name: 'Bob', jobIds: [ 'ms' ] }
{ name: 'Charlie', jobIds: [] }
Note that Charlie doesn't have applications.
My current code:
UserInfo.aggregate([
{
$lookup: {
from: 'applications',
let: { userId: '$_id' },
pipeline: [
{
$match: {
$expr: { $eq: ['$userId', '$$userId'] }
}
},
{
$project: { jobId: 1, _id: 0 }
}
],
as: 'jobIds'
}
}
])
I got:
{ _id: '1', name: 'Alice', jobIds: [ { jobId: 'gg' }, { jobId: 'fb' } ] }
{ _id: '2', name: 'Bob', jobIds: [ { jobId: 'ms' } ] }
{ _id: '3', name: 'Charlie', jobIds: [] }
How to get jobIds: [ 'gg', 'fb' ] instead of jobIds: [ { jobId: 'gg' }, { jobId: 'fb' } ]?
Haven't found proper way in Aggregation Pipeline Stages.
Use $addFields stage at the end of the pipeline.
UserInfo.aggregate([
{ "$lookup": {
"from": "applications",
"let": { "userId": '$_id' },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$userId", "$$userId"] }}},
{ "$project": { "jobId": 1, "_id": 0 }}
],
"as": "jobIds"
}},
{ "$addFields": { "jobIds": "$jobIds.jobId" }}
])
Id need a mongo aggregate that given the sample data:
{
'employeeNumber': '1',
'companyId': '1',
'role': 'D',
'dateHired':ISODate("2013-11-26T00:00:00.0Z")
...
}
{
'employeeNumber': '1',
'companyId': '1',
'role': 'S',
'dateHired':ISODate("2013-11-26T00:00:00.0Z")
...
}
{
'employeeNumber': '1',
'companyId': '2',
'role': 'D',
'dateHired':ISODate("2013-11-26T00:00:00.0Z")
...
}
{
'employeeNumber': '2',
'companyId': '1',
'role': 'D',
'dateHired':ISODate("2013-11-26T00:00:00.0Z")
...
}
queries for a given companyId (e.g. companyId = 1, using match stage probably) and would return something like:
{
'employeeNumber': '1',
'companyId': '1',
'role': 'D','S'
'dateHired':ISODate("2013-11-26T00:00:00.0Z")
...
}
notice that
{
'employeeNumber': '1',
'companyId': '2',
'role': 'D'
'dateHired':ISODate("2013-11-26T00:00:00.0Z")
...
}
is not returned.
Ideally it would return the whole object as the collection has 10/12 fields.
By using aggregation you will not get exact expected output but you can get output like following:
{ "role" : [ "D" ], "employeeNumber" : "2" }
{ "role" : [ "S" ], "employeeNumber" : "3" }
{ "role" : [ "D", "S" ], "employeeNumber" : "1" }
And the query will be like:
db.collection.aggregate({
$group: {
_id: "$employeeNumber",
"role": {
"$push": "$role"
}
}
}, {
$project: {
"employeeNumber": "$_id",
"role": 1,
"_id": 0
}
})
Edit After question edit:
db.collection.aggregate({
$group: {
_id: {
employeeNumber: "$employeeNumber",
"companyId": "$companyId"
},
"role": {
"$push": "$role"
},
"dateHired": {
$last: "$dateHired"
}
}
}, {
$project: {
"employeesNumber": "$_id.employeeNumber",
"comapnyId": "$_id.companyId",
"role": 1,
"dateHired": 1,
"_id": 0
}
})