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" }
}}
])
Related
i have a problem with aggregation framework in MongoDB (mongoose) this is the problem. i have the following database scheme.so what i want to do is count number of people who has access through Mobile only , Card only, or both. with out any order,
{
'_id': ObjectId,
'user_access_type': ['Mobile' , 'Card']
}
{
'_id': ObjectId,
'user_access_type': ['Card' , 'Mobile']
}
{
'_id': ObjectId,
'user_access_type': ['Mobile']
}
{
'_id': ObjectId,
'user_access_type': ['Card']
}
Now i am using this but it only groups by the order of the user_access_type array,
[ { "$group" : { "_id": {"User" : "$user_access_type"} , "count": {"$sum" : 1} }]
this is the output:
{
"_id": {
"User": [
"Card",
"Mobile"
]
},
"count": 1
},
{
"_id": {
"_id": "5f7dce2359aaf004985f98eb",
"User": [
"Mobile",
"Card"
]
},
"count": 1
},
{
"_id": {
"User": [
"Mobile"
]
},
"count": 1
},
{
"_id": {
"User": [
"Card"
]
},
"count": 1
},
vs what i want:
{
"_id": {
"User": [
"Card",
"Mobile" // we can say both
]
},
"count": 2 // does not depend on order
},
{
"_id": {
"User": [
"Mobile"
]
},
"count": 1
},
{
"_id": {
"User": [
"Card"
]
},
"count": 1
},
You can use other option as well using $function,
$function can allow to add javascript code, you can use sort() to sort the array
db.collection.aggregate([
{
$addFields: {
user_access_type: {
$function: {
body: function(user_access_type){
return user_access_type.sort();
},
args: ["$user_access_type"],
lang: "js"
}
}
}
},
{
$group: {
_id: "$user_access_type",
count: { $sum: 1 }
}
}
])
Second option,
If user_access_type array having always unique elements then you can use $setUnion operator on user_access_type array as self union, some how this will re-order array in same order,
db.collection.aggregate([
{
$addFields: {
user_access_type: {
$setUnion: "$user_access_type"
}
}
},
{
$group: {
_id: "$user_access_type",
count: { $sum: 1 }
}
}
])
Playground
i want to add isFavorite as true/false, if user mark that combination as favorite."isFavorite" : 0 means user already marked that item as favorite.my query is
db.getCollection('itemorders').aggregate([
{ "$match": {"customerId" : ObjectId("5e78a07c0ce36c23dcf85e6a"), 'isCart': 0}},
{ "$lookup": {
"from": "itemlistorders",
"let": { "cartId": { "$toObjectId" : "$_id" }},
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$cartId", "$$cartId"] }}},
],
"as": "itemOrdered"
} },
{$unwind: { path: "$itemOrdered", preserveNullAndEmptyArrays: true }},
{ "$lookup": {
"from": "favoritedrinks",
"let": { "uniqueCartKey": "$itemOrdered.uniqueCartKey" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$uniqueFavKey", "$$uniqueCartKey"] }}},
{ "$addFields": { "isFavorite": true }}
],
"as": "mixerList"
} },
{$unwind: { path: "$itemOrdered", preserveNullAndEmptyArrays: true }},
{
"$group": {
"_id": "$_id",
"grantTotal" : { "$first": "$grantTotal" },
"customerName" : { "$first": "$customerName" },
"barName" : { "$first": "$barName" },
"itemOrdered": { "$addToSet": "$itemOrdered" },
}
},{
$project: {
'_id': 1,
'grantTotal': 1,
'customerName': 1,
'barName': 1,
'itemOrdered': 1,
}
},
]);
My output after excusting this is :
{
"_id" : ObjectId("5e8224768910e83e908d0108"),
"itemOrdered" : [
{
"_id" : ObjectId("5e82257250c82e1abc16d856"),
"barId" : ObjectId("5e55f60eff2f842de4ae6184"),
"customerId" : ObjectId("5e78a07c0ce36c23dcf85e6a"),
"isCart" : 0,
"isAlcoholActive" : 1,
"alcoholId" : "5e81d13e50c82e1abc0a1ebb",
"alcoholName" : "Irish Coffee",
"cartId" : ObjectId("5e8224768910e83e908d0108"),
"itemTotal" : 250,
"noofDrinks" : 2
},
{
"_id" : ObjectId("5e8224e88910e83e908d0110"),
"isCart" : 0,
"isAlcoholActive" : 1,
"isFavorite" : 0,
"customerId" : ObjectId("5e78a07c0ce36c23dcf85e6a"),
"barId" : ObjectId("5e55f60eff2f842de4ae6184"),
"noofDrinks" : 1,
"itemTotal" : 200,
"mixerList" : [
{
"isMixerActive" : 0,
"_id" : ObjectId("5e8224e88910e83e908d0111"),
"mixerId" : "5e820b0250c82e1abc12cffc",
"mixerName" : "Pineapple juice"
}
],
"alcoholId" : "5e81d17150c82e1abc0a2607",
"alcoholName" : "Fireball",
"cartId" : ObjectId("5e8224768910e83e908d0108"),
}
]
};
i want to add isFavorite in each subdocument,so that i can identify whether user favorite this item or not.
below query worked for my scenerio.
db.getCollection('itemorders').aggregate([
{ "$match": {"customerId" : ObjectId("5e78a07c0ce36c23dcf85e6a"), 'isCart': 0}},
{ "$lookup": {
"from": "itemlistorders",
"let": { "cartId": { "$toObjectId" : "$_id" }},
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$cartId", "$$cartId"] }}},
],
"as": "itemOrdered"
} },
{$unwind: { path: "$itemOrdered", preserveNullAndEmptyArrays: true }},
{ "$lookup": {
"from": "favoritedrinks",
"let": { "uniqueCartKey": "$itemOrdered.uniqueCartKey","customerId1": "$itemOrdered.customerId", "modifierfavId": "$itemOrdered._id" },
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$uniqueFavKey", "$$uniqueCartKey"
]
},
{
$eq: [
"$customerId",
"$$customerId1"
]
}
]
}
}
},{ "$addFields": { "isFavorite": 'true' }}
],
"as": "itemListOrdered"
} },
{ "$addFields": { "itemOrdered.isFavorite": { "$gt": ["$itemOrdered.isFavorite", null] } }},
{
"$group": {
"_id": "$_id",
"grantTotal" : { "$first": "$grantTotal" },
"customerName" : { "$first": "$customerName" },
"barName" : { "$first": "$barName" },
"orderDate" : { "$first": "$orderDate" },
"itemOrdered": { "$push": "$itemOrdered" },
}
}
])
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 have two collections like this :
1st collection name is Promotions:
{
"_id": "A019283847466",
"code": "AAA",
"aliases" : [ "AAA1","AAA2","AAA3"]
}
2nd Collection name is PromotionUsages:
{
"customerId": "_1234567890"
"code": "AAA1"
}
{
"customerId": "_0987654321"
"code": "AAA1"
}
Expected output is :
{
"code": "AAA"
"aliasCode": "AAA1"
"countUsages": 2
}
I used mongo $group and $aggregate but I am not getting required output
any help please
Thank You!!!
You can try below aggregation
db.collection.aggregate([
{ "$unwind": "$aliases" },
{ "$addFields": { "aliasesCode": "$aliases" }},
{ "$lookup": {
"from": PromotionUsages.collection.name,
"let": { "aliases": "$aliases" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$code", "$$aliases" ] } } },
{ "$count": "countUsages" }
],
"as": "aliases"
}},
{ "$unwind": "$aliases" },
{ "$project": { "code": 1, "aliasCode": 1, "countUsages": "$aliases.countUsages" }}
])
Output
{
"code": "AAA"
"aliasCode": "AAA1"
"countUsages": 2
}
Made some changes in solution suggested by Anthony Winzlet
db.promotions.aggregate([
{ "$unwind": "$aliases" },
{ "$addFields": { "aliasesCode": "$aliases" }},
{ "$lookup": {
"from": "promotionusages",
"let": { "aliases": "$aliases" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$code", "$$aliases" ] } } },
{ "$count": "countUsages" }
],
"as": "aliases"
}},
{ "$unwind": "$aliases" },
{ "$project": { "_id" : 0, "code": 1, "aliasesCode": 1, "countUsages": "$aliases.countUsages" }}
])
I have a simple datastructure in mongodb:
{
_id: ObjectID,
name: 'Name',
birthday: '25.05.2001'
items: [
{
_id: ObjectID,
name: 'ItemName',
info: 'ItemInfo',
},
{
_id: ObjectID,
name: 'ItemName',
info: 'ItemInfo',
}
]
}
Now i want a query, that takes a ObjectID (_id) of an item as criteria and gives me back the object with all items in the array AND projects a new field "selected" with value true or false into a field in the result of each array item:
I tried that with this query:
{ $unwind: '$items' },
{
$project: {
selected: {
$cond: { if: { 'items._id': itemObjectID }, then: true, else: false },
},
},
},
but MongoDB gives me back an error:
MongoError: FieldPath field names may not contain '.'.
Have no clue why its not working, any help or ideas? Thank you very much!
What you are missing here is $eq aggregation operator which checks the condition for the equality.
You can try below aggregation here if you want to check for ObjectId then you need to put mongoose.Types.ObjectId(_id)
db.collection.aggregate([
{ "$unwind": "$items" },
{ "$addFields": {
"items.selected": {
"$eq": [
1111,
"$items._id"
]
}
}},
{ "$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"items": {
"$push": {
"_id": "$items._id",
"selected": "$items.selected"
}
}
}}
])
Will give following output
[
{
"_id": ObjectId("5a934e000102030405000000"),
"items": [
{
"_id": 1111,
"selected": true
},
{
"_id": 2222,
"selected": false
}
],
"name": "Name"
}
]
You can check it here
#Ashish: Thank you very much for your help! Your answer helped me to build the right query for me:
db.collection.aggregate([
{
$unwind: "$items"
},
{
$project: {
"items.name": 0,
"birthday": 0
}
},
{
"$addFields": {
"items.selected": {
"$eq": [
1111,
"$items._id"
]
}
}
},
{
$group: {
_id: "$_id",
"name": {
"$first": "$name"
},
items: {
$push: "$items"
}
}
},
{
$match: {
"items._id": {
$eq: 1111
}
}
},
])
and leads to a result that looks like:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"items": [
{
"_id": 1111,
"selected": true
},
{
"_id": 2222,
"selected": false
}
],
"name": "Name"
}
]