Related
The main collection is User, and we have a User profile collection which having experience details and other stuff. Also, we have a Skill collection.
USER
[{
"_id": "5f1eef8ec68d306fbbf13b0f",
"name": "John Davis",
"email": "John#gmail.com",
"__v": 0
},
{
"_id": "9q1eef8ec68d306fbbf13bh6",
"name": "Mik Luca",
"email": "Mik#gmail.com",
"__v": 0
}]
User profile
[{
"_id": "5f1eef8ec68d306fbbf13b10",
"other_skills": [
null
],
"user_id": "5f1eef8ec68d306fbbf13b0f",
"phone_number": "1234569870",
"location": "5f16b72617fee02922688751",
"primary_skills": [
{
"_id": "5f32635cf764cc40447503a6",
"years": 1,
"skill_id": "5f0da75907a96c3040b3667d"
}
]
},
{
"_id": "5f1eef8ec68d306fbbf13b10",
"other_skills": [
null
],
"user_id": "9q1eef8ec68d306fbbf13bh6",
"phone_number": "1234569870",
"location": "5f16b72617fee02922688751",
"primary_skills": [
{
"_id": "6s0da75907a96c3040b36690",
"years": 1,
"skill_id": "5f0da75907a96c3040b3667d"
}
]
}]
Skill
[{
"_id": "5f0da75907a96c3040b3667d",
"skill": "Mongo"
},
{
"_id": "6s0da75907a96c3040b36690",
"skill": "Node"
}]
I need to list the users with their user profile info and need to filter with skills as well
I have tried
db.getCollection("users").aggregate(
[
{
"$project" : {
"_id" : NumberInt(0),
"users" : "$$ROOT"
}
},
{
"$lookup" : {
"localField" : "users._id",
"from" : "userprofiles",
"foreignField" : "user_id",
"as" : "userprofiles"
}
},
{
"$unwind" : {
"path" : "$userprofiles",
"preserveNullAndEmptyArrays" : true
}
},
{
"$lookup" : {
"localField" : "userprofiles.primary_skills.skill_id",
"from" : "skills",
"foreignField" : "_id",
"as" : "skills"
}
},
{
"$unwind" : {
"path" : "$skills",
"preserveNullAndEmptyArrays" : true
}
},
{
"$match" : {
"skills._id" : ObjectId("5f0dce8d07a96c3040b36687")
}
}
],
{
"allowDiskUse" : true
}
);
But not getting the proper results.
How can I populate the user profile and skill information with the User list and filter the user list with Skill ids?
Greetings and thanks.
You can match filters inside lookup using lookup with pipeline,
$lookup with userProfile collection
pipelines $match to match profile id
other filters for profile like skill_id match here
$unwind deconstruct primary_skills array because we are going to lookup with skill_id
$lookup will skills collection
$unwind deconstruct primary_skills.skill_id array because we need it as object
$grpup reconstruct primary_skills array
$match if userProfiles not equal to empty []
db.users.aggregate([
{
$lookup: {
from: "usersProfile",
let: { id: "$_id" },
as: "userProfiles",
pipeline: [
{
$match: {
$expr: { $eq: ["$$id", "$user_id"] },
// match here user profile filters
"primary_skills.skill_id": "5f0da75907a96c3040b3667d"
}
},
{ $unwind: "$primary_skills" },
{
$lookup: {
from: "skills",
localField: "primary_skills.skill_id",
foreignField: "_id",
as: "primary_skills.skill_id"
}
},
{ $unwind: "$primary_skills.skill_id" },
{
$group: {
_id: "$_id",
other_skills: { $first: "$other_skills" },
phone_number: { $first: "$phone_number" },
location: { $first: "$location" },
primary_skills: {
$push: {
_id: "$primary_skills._id",
skill: "$primary_skills.skill_id.skill",
years: "$primary_skills.years"
}
}
}
}
]
}
},
{ $match: { userProfiles: { $ne: [] } } }
])
Playground
I have one query where I want to group subdocument. i have try some example but it is not working properly
Query
db.getCollection("checklistCombination").aggregate([
{
"$lookup": {
"from": "Users",
"localField": "userId",
"foreignField": "_id",
"as": "user"
}
},
{
"$lookup": {
"from": "checklist",
"localField": "checklistId",
"foreignField": "_id",
"as": "linkChecklist"
}
},
{ "$unwind": "$linkChecklist" },
{
"$lookup": {
"from": "orderDetail",
"localField": "linkChecklist.product",
"foreignField": "productRangeId",
"as": "orderProduct"
}
},
{
"$unwind": { "path": "$orderProduct", "preserveNullAndEmptyArrays": true }
},
{
"$lookup": {
"from": "companysuppliers",
"localField": "orderProduct.supplierId",
"foreignField": "_id",
"as": "comapnySupplier"
}
},
{
"$unwind": {
"path": "$comapnySupplier",
"preserveNullAndEmptyArrays": true
}
},
{
"$lookup": {
"from": "suppliers",
"localField": "comapnySupplier.supplierId",
"foreignField": "_id",
"as": "supplier"
}
},
{ "$unwind": { "path": "$supplier", "preserveNullAndEmptyArrays": true } },
{
"$project": {
"_id": 1,
"name": 1,
"user": 1,
"linkChecklist": 1,
"orderProduct": 1,
"orderProductStatusIndex": {
"$ifNull": ["$orderProduct.statusIndex", "0"]
},
"comapnySupplier": 1,
"supplier": 1
}
},
{ "$match": { "orderProductStatusIndex": "0" } },
{
"$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"supplier": {
"$push": {
"supplierId": "$supplier._id",
"supplierName": "$supplier.name",
"items": { "$sum": "$orderProduct.quantity" }
}
}
}
}
])
This query return below result
[{
"_id" : ObjectId("5cee224b97e765079c8c2839"),
"name" : "Dairy",
"supplier" : [
{
"supplierId" : ObjectId("5cee12a7a01ad50f5c2229ac"),
"supplierName" : "Bhagwandas Bherumal",
"items" : 10
}
]
},
{
"_id" : ObjectId("5cee1a19a01ad50f5c2229f2"),
"name" : "dairy/fruit",
"supplier" : [
{
"supplierId" : ObjectId("5cee12a7a01ad50f5c2229ac"),
"supplierName" : "Bhagwandas Bherumal",
"items" : 55
},
{
"supplierId" : ObjectId("5cee11f7a01ad50f5c2229a2"),
"supplierName" : "Agron India PVT. LTD",
"items" : 55
},
{
"supplierId" : ObjectId("5cee12a7a01ad50f5c2229ac"),
"supplierName" : "Bhagwandas Bherumal",
"items" : 10
}
]
}]
In result you can see there are two different results for Bhagwandas Bherumal in dairy/fruit (in array index 1 ). I want to group by this field and sum its items.
Expected Result
[
{
"_id" : ObjectId("5cee224b97e765079c8c2839"),
"name" : "Dairy",
"supplier" : [
{
"supplierId" : ObjectId("5cee12a7a01ad50f5c2229ac"),
"supplierName" : "Bhagwandas Bherumal",
"items" : 10
}
]
},
{
"_id" : ObjectId("5cee1a19a01ad50f5c2229f2"),
"name" : "dairy/fruit",
"supplier" : [
{
"supplierId" : ObjectId("5cee12a7a01ad50f5c2229ac"),
"supplierName" : "Bhagwandas Bherumal",
"items" : 65
},
{
"supplierId" : ObjectId("5cee11f7a01ad50f5c2229a2"),
"supplierName" : "Agron India PVT. LTD",
"items" : 55
}
]
}]
Hope this solves your problem :
db.getCollection("checklistCombination").aggregate([
{
"$lookup": {
"from": "Users",
"localField": "userId",
"foreignField": "_id",
"as": "user"
}
},
{
"$lookup": {
"from": "checklist",
"localField": "checklistId",
"foreignField": "_id",
"as": "linkChecklist"
}
},
{ "$unwind": "$linkChecklist" },
{
"$lookup": {
"from": "orderDetail",
"localField": "linkChecklist.product",
"foreignField": "productRangeId",
"as": "orderProduct"
}
},
{
"$unwind": { "path": "$orderProduct", "preserveNullAndEmptyArrays": true }
},
{
"$lookup": {
"from": "companysuppliers",
"localField": "orderProduct.supplierId",
"foreignField": "_id",
"as": "comapnySupplier"
}
},
{
"$unwind": {
"path": "$comapnySupplier",
"preserveNullAndEmptyArrays": true
}
},
{
"$lookup": {
"from": "suppliers",
"localField": "comapnySupplier.supplierId",
"foreignField": "_id",
"as": "supplier"
}
},
{ "$unwind": { "path": "$supplier", "preserveNullAndEmptyArrays": true } },
{
"$project": {
"_id": 1,
"name": 1,
"user": 1,
"linkChecklist": 1,
"orderProduct": 1,
"orderProductStatusIndex": {
"$ifNull": ["$orderProduct.statusIndex", "0"]
},
"comapnySupplier": 1,
"supplier": 1
}
},
{ "$match": { "orderProductStatusIndex": "0" } },
{ $group : {
_id : {
_id : '$_id',
name : '$name',
supplierId : "$supplier._id",
supplierName : "$supplier.name"
},
items : { "$sum": "$orderProduct.quantity"}
}
},
{
$group : {
_id : '$_id._id',
name : { "$first": "$_id.name" },
supplier : {
"$push": {
"supplierId": "$_id.supplierId",
"supplierName": "$_id.supplierName",
"items" : '$items'
}
}
}
}
])
So in my Database, I have the 3 collections, and they look like this:
Customers:
customers = [
{_id: 1, username: "jack", ... },
{_id: 2, username: "jane", ... }
...
]
Reviews:
reviews = [
{ _id: 1, customerID: 1, message: "my message", ...}
...
]
Comments:
comments = [
{ _id: 1, reviewID: 1, customerID: 2, message: "my response" ...}
...
]
Customers can post reviews, and can also comment on other reviews.
So, what I want is a mongodb aggregation query to:
Retrieve the reviews.
The data of the customer who made the review.
The comments on that review.
The data of the customers who made the comment on that review.
i.e
reviews = [
{
_id: 1,
username: "jack",
message: "my message"
comments: [
{ _id: 1, username: "jane", message: "my response", ...},
...
]
...
}
...
]
You can start from comments collection and $lookup with customers to get customer name, then you can $group all comments by review and $lookup twice (with reviews and customer). Every time you know that it's a one-to-one relationship you can use $unwind after $lookup. Try:
db.comments.aggregate([
{
$lookup: {
from: "customers",
localField: "customerID",
foreignField: "_id",
as: "customer"
}
},
{
$unwind: "$customer"
},
{
$project: {
_id: 1,
reviewID: 1,
username: "$customer.username",
message: 1
}
},
{
$group: {
_id: "$reviewID",
comments: { $push: { _id: "$_id", username: "$username", message: "$message" } }
}
},
{
$lookup: {
from: "reviews",
localField: "_id",
foreignField: "_id",
as: "review"
}
},
{
$unwind: "$review"
},
{
$lookup: {
from: "customers",
localField: "review.customerID",
foreignField: "_id",
as: "customer"
}
},
{
$unwind: "$customer"
},
{
$project: {
_id: 1,
message: "$review.message",
username: "$customer.username",
comments: 1
}
}
])
Outputs:
{ "_id" : 1, "comments" : [ { "_id" : 1, "username" : "jane", "message" : "my response" } ], "message" : "my message", "username" : "jack" }
EDIT:
If you want to start from reviews and filter it out to single movie you can then you can use $lookup with custom pipeline
db.reviews.aggregate([
{
$match: {
movieId: 1,
}
},
{
$lookup: {
from: "customers",
localField: "customerID",
foreignField: "_id",
as: "customer"
}
},
{
$unwind: "$customer"
},
{
$lookup: {
from: "comments",
let: { reviewId: "$_id" },
pipeline: [
{
$match: { $expr: { $eq: [ "$$reviewId", "$reviewID" ] } }
},
{
$lookup: {
from: "customers",
localField: "customerID",
foreignField: "_id",
as: "customer"
}
},
{
$unwind: "$customer"
},
{
$project: {
_id: 1,
message: 1,
username: "$customer.username"
}
}
],
as: "comments"
}
},
{
$project: {
_id: 1,
message: 1,
username: "$customer.username",
comments: 1
}
}
])
Brings the same output
You can use below aggregation with mongodb 3.6 and above
Reviews.aggregate([
{ "$lookup": {
"from": Customers.collection.name,
"let": { "customerID": "$customerID" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$_id", "$$customerID"] } } }
],
"as": "customer"
}},
{ "$unwind": "$customer" },
{ "$lookup": {
"from": Comments.collection.name,
"let": { "reviewID": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$reviewID", "$$reviewID"] } } },
{ "$lookup": {
"from": Customers.collection.name,
"let": { "customerID": "$customerID" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$_id", "$$customerID"] } } }
],
"as": "customer"
}},
{ "$unwind": "$customer" },
],
"as": "comments"
}}
])
I m stuck with a mongo aggregate query. Right now I have a collection which contains posts of various users (whose details are present in users collection).
I need a query to fetch only one post of each user (like group by in SQL)
POSTS collection data
{
language:'english',
status:'A',
desc:'Hi there',
userId:'5b891370f43fe3302bbd8918'
},{
language:'english',
status:'A',
desc:'Hi there - 2'
userId:'5b891370f43fe3302bbd8918'
},{
language:'english',
status:'A',
desc:'Hi there - 3'
userId:'5b891370f43fe3302bbd8001'
}
Here is my query
db.col('posts').aggregate([
{
$match: {
language: 'english',
status: "A"
}
}, {
$sample: { size: 10 }
}, {
$sort: { _id: -1 }
}, {
$lookup: {
from: 'users',
localField: 'userId',
foreignField: '_id',
as: 'ownerData'
}
}], (err, data) => { console.log(err,data) });
Desired Output
{
language:'english',
status:'A',
desc:'Hi there',
userId:'5b891370f43fe3302bbd8918',
ownerData:[[object]]
},{
language:'english',
status:'A',
desc:'Hi there - 3'
userId:'5b891370f43fe3302bbd8001',
ownerData:[[object]]
}
$group: will as group by of mysql. $first: will take first element of collection field from group. $lookup acts as join in mysql.
db.tempdate.aggregate([
{ $group :
{
_id : "$userId",
language : { $first: '$language' },
status : { $first: '$status' },
desc : { $first: '$desc' }
}
},
{ $lookup:
{
from: "user",
localField: "_id",
foreignField: "user_id",
as: "userData"
}
}
]).pretty();`
Output
`{
"_id" : "5b891370f43fe3302bbd8001",
"language" : "english",
"status" : "A",
"desc" : "Hi there - 3",
"userData" : [
{
"_id" : ObjectId("5ba3633a12b8613823f3056e"),
"user_id" : "5b891370f43fe3302bbd8001",
"name" : "Bhuwan"
}
]
}
{
"_id" : "5b891370f43fe3302bbd8918",
"language" : "english",
"status" : "A",
"desc" : "Hi there",
"userData" : [
{
"_id" : ObjectId("5ba3634612b8613823f3056f"),
"user_id" : "5b891370f43fe3302bbd8918",
"name" : "Harry"
}
]
}
Also, you can use group and $last
db.getCollection('posts').aggregate([
{ "$match": { "language": 'english', "status": "A" }},
{ "$group": {
"_id": "$userId",
"primaryId" : { "$last": "$_id" },
"language": { "$last": "$language" },
"status": { "$last": "$status" },
"desc": { "$last": "$desc" }
}},
{ "$lookup": {
"from": "users",
"localField": "_id",
"foreignField": "_id",
"as": "ownerData"
}},
{ $unwind:{path: '$ownerData',preserveNullAndEmptyArrays: true} //to convert ownerData to json object
}
])
You can use $group aggregation stage for the distinct userId and then use $lookup to get users data.
db.col('posts').aggregate([
{ "$match": { "language": 'english', "status": "A" }},
{ "$sample": { "size": 10 }},
{ "$sort": { "_id": -1 }},
{ "$group": {
"_id": "$userId",
"language": { "$first": "$language" },
"status": { "$first": "$status" },
"desc": { "$first": "$desc" }
}},
{ "$lookup": {
"from": "users",
"localField": "_id",
"foreignField": "_id",
"as": "ownerData"
}}
])
I have the following document
{
"userid": "5a88389c9108bf1c48a1a6a7",
"email": "abc#gmail.com",
"lastName": "abc",
"firstName": "xyz",
"__v": 0,
"friends": [{
"userid": "5a88398b9108bf1c48a1a6a9",
"ftype": "SR",
"status": "ACCEPT",
"_id": ObjectId("5a9585b401ef0033cc8850c7")
},
{
"userid": "5a88398b9108bf1c48a1a6a91111",
"ftype": "SR",
"status": "ACCEPT",
"_id": ObjectId("5a9585b401ef0033cc8850c71111")
},
{
"userid": "5a8ae0a20df6c13dd81256e0",
"ftype": "SR",
"status": "pending",
"_id": ObjectId("5a9641fbbc9ef809b0f7cb4e")
}]
},
{
"userid": "5a88398b9108bf1c48a1a6a9",
"friends": [{ }],
"lastName": "123",
"firstName": "xyz",
.......
},
{
"userid": "5a88398b9108bf1c48a1a6a91111",
"friends": [{ }],
"lastName": "456",
"firstName": "xyz",
...
}
First Query
Here I want to get userId from friends array ,which having status equals to "ACCEPT".
ie
[5a88398b9108bf1c48a1a6a9,5a88398b9108bf1c48a1a6a91111]
Second Query
After that, I have to make another query on the same collection to get details of each userid returned in the first query.
final Query will return details of [5a88398b9108bf1c48a1a6a9,5a88398b9108bf1c48a1a6a91111]
both userid ie
[
{
userid" : "5a88398b9108bf1c48a1a6a9",
"lastName" : "123",
"firstName" : "xyz"
},
{
"userid" : "5a88398b9108bf1c48a1a6a91111",
"lastName" : "456",
"firstName" : "xyz"
}
]
I have tried so far with
Users.find ({'_id':5a88389c9108bf1c48a1a6a7,"friends.status":'ACCEPT'}, (error, users) => {})
or
Users.find ({'_id':5a88389c9108bf1c48a1a6a7, friends: { $elemMatch: { status: 'ACCEPT' } } }, (error, users) => {})
Use the aggregation framework's $map and $filter operators to handle the task. $filter will filter the friends array based on the specified condition that the status should equal "ACCESS" and $map will transform the results from the filtered array to the desired format.
For the second query, append a $lookup pipeline step which does a self-join on the users collection to retrieve the documents which match the ids from the previous pipeline.
Running the following aggregate operation will produce the desired array:
User.aggregate([
{ "$match": { "friends.status": "ACCEPT" } },
{ "$project": {
"users": {
"$map": {
"input": {
"$filter": {
"input": "$friends",
"as": "el",
"cond": { "$eq": ["$$el.status", "ACCEPT"] }
}
},
"as": "item",
"in": "$$item.userid"
}
}
} },
{ "$lookup": {
"from": "users",
"as": "users",
"localField": "users",
"foreignField": "userid"
} },
]).exec((err, results) => {
if (err) throw err;
console.log(results[0].users);
});
I did not test it. just for an idea, give it a try and let me know.
db.Users.aggregate(
[
{
$unwind: "$friends"
},
{
$match:{ "$friends.status": "ACCEPT"}
},
{
$project:{ "FriendUserID":"$friends.userid"}
},
{
$lookup:{
from:"Users",
as: "FriendsUsers",
localField: "FriendUserID",
foreignField: "userid"
}
},
{
$project: { FriendsUsers.lastName:1,FriendsUsers.firstName:1 }
}
]
)
filtering nested elements
const products = await Product.aggregate<ProductDoc>([
{
$match: {
userId: data.id,
},
},
{
$project: {
promotions: {
$filter: {
input: '$promotions',
as: 'p',
cond: {
$eq: ['$$p.status', PromotionStatus.Started],
},
},
},
userId: 1,
name: 1,
thumbnail: 1,
},
},
]);
for multiple condition
cond: {
$and: [
{
$eq: [
"$$c.product",
"37sd87hjsdj3"
]
},
{
$eq: [
"$$c.date",
"date-jan-4-2022"
],
}
]
},