Related
I want to merge two document in same collection by grouping two fields, one field is just a normal text field (WorkFlow), and another one is an array field (Service), i was able to group the normal field(WorkFlow) but am stuck to group the array field, please help me I have a short in time to do it, I've gone through some article but I couldn't find any solution, please help me to overcome this..
** i need to get the output, in any method if its not in aggregate then any other solutions
I have the document in collection like below:
[{
"_id": {
"$oid": "62f9fd54259335683bc54ac3"
},
"WorkFlow": "Vendor",
"Service": [
{
"value": "6235b52ea216f20e1b00ad43"
},
{
"value": "6235b538a216f20e1b00ad46"
}
],
"AssetServiceClassification": "Business",
"LevelArray": [
{
"Level": "6235b4f5a216f20e1b00ad36",
"StaffName": "620a23bbc7e6a4378ff8ad74",
"Designation": "61efa3a5a444008633b223dd"
},
{
"Level": "6235b500a216f20e1b00ad39",
"StaffName": "620b4d4995c3061565e63b08",
"Designation": "61efa3aca444008633b223e0"
}
]
},
{
"_id": {
"$oid": "62f9f5f9e26c8912b86c61b8"
},
"WorkFlow": "Vendor",
"Service": [
{
"value": "6235b538a216f20e1b00ad46"
},
{
"value": "6235b52ea216f20e1b00ad43"
}
],
"AssetServiceClassification": "Normal",
"LevelArray": [
{
"Level": "6235b4f5a216f20e1b00ad36",
"StaffName": "620a2351c7e6a4378ff8ad4c",
"Designation": "61efa3a5a444008633b223dd"
},
{
"Level": "6235b500a216f20e1b00ad39",
"StaffName": "620a2387c7e6a4378ff8ad60",
"Designation": "61efa3aca444008633b223e0"
}
]
}]
and I need an output structure like below:
[{
"WorkFlow": "Vendor",
"Service": [
{
"value": "6235b52ea216f20e1b00ad43"
},
{
"value": "6235b538a216f20e1b00ad46"
}
],
"Assets": [
{
"_id": "62f9fd54259335683bc54ac3",
"AssetServiceClassification": "Business",
"LevelArray": [
{
"Level": "6235b4f5a216f20e1b00ad36",
"StaffName": "620a23bbc7e6a4378ff8ad74",
"Designation": "61efa3a5a444008633b223dd"
},
{
"Level": "6235b500a216f20e1b00ad39",
"StaffName": "620b4d4995c3061565e63b08",
"Designation": "61efa3aca444008633b223e0"
}
]
},
{
"_id": "62f9f5f9e26c8912b86c61b8",
"AssetServiceClassification": "Normal",
"LevelArray": [
{
"Level": "6235b4f5a216f20e1b00ad36",
"StaffName": "620a2351c7e6a4378ff8ad4c",
"Designation": "61efa3a5a444008633b223dd"
},
{
"Level": "6235b500a216f20e1b00ad39",
"StaffName": "620a2387c7e6a4378ff8ad60",
"Designation": "61efa3aca444008633b223e0"
}
]
}
]
}]
I think this pipeline will help you get the output you want:
db.collection.aggregate([
{
"$unwind": "$Service"
},
{
"$sort": {
"Service.value": 1
}
},
{
"$group": {
"_id": {
"_id": "$_id",
"WorkFlow": "$WorkFlow",
"LevelArray": "$LevelArray",
"AssetServiceClassification": "$AssetServiceClassification"
},
"Service": {
"$push": "$Service"
}
}
},
{
"$project": {
"_id": "$_id._id",
"WorkFlow": "$_id.WorkFlow",
"LevelArray": "$_id.LevelArray",
"AssetServiceClassification": "$_id.AssetServiceClassification",
"Service": "$Service"
}
},
{
"$group": {
"_id": {
"WorkFlow": "$WorkFlow",
"Service": "$Service"
},
"Assets": {
"$push": {
"_id": "$_id",
"LevelArray": "$LevelArray",
"AssetServiceClassification": "$AssetServiceClassification"
}
}
}
}
])
I want to convert my existing code to mongoDB aggregation framework.
Here's my code that I want to convert in aggregation.
const comments = await Comments.find({post:req.params.id})
.populate({
path:"comments",
populate:{
path:"author",
select:"name photo"
},
select:{
createdAt:1,
author:1,
_id:1,
body:1,
likes:1
}
})
.populate("author","photo name")
.select({
createdAt:1,
author:1,
_id:1,
body:1,
comments:1,
likes:1
})
.sort("-createdAt");
You can use below aggregation
Comments.aggregate([
{ "$match": { "post": mongoose.Types.ObjectId(req.params.id) } },
{ "$sort": { "createdAt": -1 } },
{
"$lookup": {
"from": Comment.collection.name,
"let": { "comments": "$comments" },
"pipeline": [
{ "$match": { "$expr": { "$in": ["$_id", "$$comments"] } } },
{
"$lookup": {
"from": Author.collection.name,
"let": { "author": "$author" },
"pipeline": [{ "$match": { "$expr": { "$eq": ["$_id", "$$author"] } } }, { "$project": { "name": 1, "photo": 1 } }],
"as": "author"
}
},
{ "$unwind": "$author" },
{ "$project": { "createdAt": 1, "author": 1, "_id": 1, "body": 1, "likes": 1 } }
],
"as": "comments"
}
},
{
"$lookup": {
"from": Author.collection.name,
"let": { "author": "$author" },
"pipeline": [{ "$match": { "$expr": { "$eq": ["$_id", "$$author"] } } }, { "$project": { "name": 1, "photo": 1 } }],
"as": "author"
}
},
{ "$unwind": "$author" },
{
"$project": {
"createdAt": 1,
"author": 1,
"body": 1,
"comments": 1,
"likes": 1
}
}
])
I have a collection similar to this:
[
{
"_id":1,
"name":"breakfast",
"time":"10.00"
},
{
"_id":3,
"name":"lunch",
"time":"12.07"
},
{
"_id":2,
"name":"breakfast",
"time":"10.10"
},
{
"_id":4,
"name":"lunch",
"time":"12.45"
}
]
I want to aggregate into something like this:
{
"breakfast":[
{
"_id":1,
"name":"breakfast",
"time":"10.00"
},
{
"_id":2,
"name":"breakfast",
"time":"10.10"
}
],
"lunch":[
{
"_id":3,
"name":"lunch",
"time":"12.07"
},
{
"_id":4,
"name":"lunch",
"time":"12.45"
}
]
}
I have only managed to group them but I can't change the key meals to either breakfast or lunch depending on the meal.name(group name)
$group: {
_id: { meal: '$meal.name' },
meals: { $push: '$meal' },
}
Using the above code I have managed to produce the output below. My only challenge is changing the key meals to either breakfast or lunch as explained above in the subgroups.
{
"meals":[
{
"_id":1,
"name":"breakfast",
"time":"10.00"
},
{
"_id":2,
"name":"breakfast",
"time":"10.10"
}
],
"meals":[
{
"_id":3,
"name":"lunch",
"time":"12.07"
},
{
"_id":4,
"name":"lunch",
"time":"12.45"
}
]
}
Here you can have your answer .
After "grouping" to add to an array you similarly $push all that content into array by the "name" grouping key and then convert into keys of a document in a $replaceRoot with $arrayToObject:
db.collection.aggregate([
{ "$group": {
"_id": "$name",
"data": { "$push": "$$ROOT" }
}},
{ "$group": {
"_id": null,
"data": {
"$push": {
"k": "$_id",
"v": "$data"
}
}
}},
{ "$replaceRoot": {
"newRoot": { "$arrayToObject": "$data" }
}}
])
OUTPUT
[
{
"breakfast": [
{
"_id": 1,
"name": "breakfast",
"time": "10.00"
},
{
"_id": 2,
"name": "breakfast",
"time": "10.10"
}
],
"lunch": [
{
"_id": 3,
"name": "lunch",
"time": "12.07"
},
{
"_id": 4,
"name": "lunch",
"time": "12.45"
}
]
}
]
You can check the result of above query in this LINK
I'm using mongoDB 3.6 on node.js 8.11.1 and working with MongoDB Node.js Driver.
I have two collections, 'group' and 'user':
group:
[
{
"_id":1,
"groupName":"group1",
"users":[
{
"userId":1,
"isAdmin":"false"
},
{
"userId":2,
"isAdmin":"true"
}
]
},
{
"_id":2,
"groupName":"group2",
"users":[
{
"userId":2,
"isAdmin":"false"
},
{
"userId":3,
"isAdmin":"true"
}
]
}
]
user:
[
{
"_id":1,
"username":"user1",
"firstname":"a",
"lastname":"aa",
"mobileNo":"+1111111"
},
{
"_id":2,
"username":"user2",
"firstname":"b",
"lastname":"bb",
"mobileNo":"+2222222"
},
{
"_id":3,
"username":"user3",
"firstname":"c",
"lastname":"cc",
"mobileNo":"+3333333"
}
]
I need an aggregate to return something like this:
[
{
"_id":1,
"groupName":"group1",
"members":[
{
"isAdmin":"false",
"username":"user1",
"firstname":"a",
"lastname":"aa"
},
{
"isAdmin":"true",
"username":"user2",
"firstname":"b",
"lastname":"bb"
}
]
},
{
"_id":2,
"groupName":"group2",
"members":[
{
"isAdmin":"false",
"username":"user2",
"firstname":"b",
"lastname":"bb"
},
{
"isAdmin":"true",
"username":"user3",
"firstname":"c",
"lastname":"cc"
}
]
}
]
At "members" in result, "isAdmin" return from "users" at group collection and "username", "firstname" and "lastname" came from user collection
Many thanks,
Milad.
You can try below aggregation from mongodb 3.6 and above
db.group.aggregate([
{ "$unwind": "$users" },
{ "$lookup": {
"from": Users.collection.name,
"let": { "userId": "$users.userId", "isAdmin": "$users.isAdmin" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$userId" ] } } },
{ "$project": { "isAdmin": "$$isAdmin", "username": 1, "firstName": 1, "lastName": 1 }}
],
"as": "members"
}},
{ "$unwind": "$members" },
{ "$group": {
"_id": "$_id",
"members": { "$push": "$members" },
"groupName": { "$first": "$groupName" }
}}
])
I have query something like this:
Message.aggregate([{
"$match": {
$or: [{
"to": userId
}, {
"from": userId
}]
}
},
{
"$sort": {
"createDate": -1
}
},
{
"$group": {
"_id": "$conversationId",
"from": {
"$first": "$from"
},
"to": {
"$first": "$to"
},
"content": {
"$first": "$content"
},
"createDate": {
"$first": "$createDate"
},
"unreaded": {
"$sum": {
"$cond": {
if: {
$and: [{
"$eq": [
"$unreaded", 1
]
},
{
"$eq": ["$to", userId]
}
]
},
then: 1,
else: 0
}
}
}
}
},
{
"$sort": {
"createDate": -1
}
},
{
"$lookup": {
"from": "users",
"localField": "from",
"foreignField": "_id",
"as": "from"
}
},
{
"$lookup": {
"from": "users",
"localField": "to",
"foreignField": "_id",
"as": "to"
}
},
{
"$unwind": {
"path": "$from"
}
},
{
"$unwind": {
"path": "$to"
}
},
{
"$project": {
"from.firstName": "$from.firstName",
"from.lastName": "$from.lastName",
"from.picture": "$from.picture",
"to.firstName": "$to.firstName",
"to.lastName": "$to.lastName",
"to.picture": "$to.picture",
"content": 1,
"createDate": 1,
"unreaded": 1,
"reciver": {
"$cond": {
if: {
"$eq": ["$from._id", mongoose.Types.ObjectId(userId)]
},
then: {
"firstName": "$to.firstName",
"lastName": "$to.lastName",
"_id": "$to._id"
},
else: {
"firstName": "$from.firstName",
"lastName": "$from.lastName",
"_id": "$from._id"
}
}
}
}
},
{
"$limit": 50
}
I am able now to limit records to 50 per request but problem is when I try to make pagination... I get this error when I try to add skip next to limit:
Error: Arguments must be aggregate pipeline operators
any idea how can I do that?
Check your args and correct mistake.
Message.aggregate([{"$match": {$or: [{"to": userId}, {"from": userId}]}}, ..., {$skip: 1}, {$limit: 1}])