I have problems with MongoDB's aggregation.
In my "Job" document, it has creatorParent(single value) and Children(array of mongodb object id). In the "User" document, user has children array with child details.
When user request for retrieving this document I want aggregate child details, if array contains id of child.
I wrote an aggregation with some help, It worked for creatorParent but whatever I've tried, it didn't worked for children.
db.getCollection('Jobs').aggregate([
{
$unwind: {
path : "$children"
}
},
{
$lookup: {
"from" : "Users",
"localField" : "creatorparent",
"foreignField" : "_id",
"as" : "creatorparent"
}
},
{
$lookup: {
"from" : "Users",
"localField" : "children",
"foreignField" : "children",
"as" : "children"
}
}
])
Users document:
{
"_id" : ObjectId("58daf84877733645eaa9b44f"),
"email" : "meto93#gmail.com",
"password" : "vpGl+Fjnef616cRgNbCkwaFDpSI=",
"passwordsalt" : "99397F4A9D3A499D96694547667E74595CE994D2E83345D6953EF866303E8B65",
"children" : [
{
"_id" : ObjectId("58daf84977733645eaa9b450"),
"name" : "Mert",
"age" : 5,
"additionalinformation" : "ilk cocuk",
"creationtime" : ISODate("2017-03-28T23:56:56.952Z"),
"userid" : ObjectId("58daf84877733645eaa9b44f"),
"gender" : null
},
{
"_id" : ObjectId("58daf84977733645eaa9b451"),
"name" : "Sencer",
"age" : 7,
"additionalinformation" : "ikinci cocuk",
"creationtime" : ISODate("2017-03-28T23:56:56.952Z"),
"userid" : ObjectId("58daf84877733645eaa9b44f"),
"gender" : null
}
]
}
Job
{
"_id" : ObjectId("58db0a2d77733645eaa9b453"),
"creationtime" : ISODate("2017-03-29T01:13:17.509Z"),
"startingtime" : ISODate("2017-04-03T13:00:00.000Z"),
"endingtime" : ISODate("2017-04-03T17:00:00.000Z"),
"children" : [
ObjectId("58daf84977733645eaa9b450"),
ObjectId("58daf84977733645eaa9b451")
],
"creatorparent" : ObjectId("58daf84877733645eaa9b44f"),
"applicants" : []
}
Try this:
db.jobs.aggregate(
[
{
$unwind: {
path : "$children",
}
},
{
$lookup: {
"from" : "users",
"localField" : "creatorparent",
"foreignField" : "_id",
"as" : "creatorparent"
}
},
{
$lookup: {
"from" : "users",
"localField" : "children",
"foreignField" : "children._id",
"as" : "children"
}
},
{
$addFields: {
children : {$arrayElemAt : ["$children",0]}
}
},
{
$addFields: {
"children":"$children.children"
}
},
{
$unwind: {
path : "$children",
}
},
{
$group: {
"_id": "$_id",
"name": { "$first": "$name" },
"jobstatus" : { "$first": "$jobstatus" },
"hourlyrate" : { "$first": "$hourlyrate" },
"creatorparent" : { "$first" : "$creatorparent" },
"children": { "$addToSet": "$children" }
}
},
]
);
Related
I have user collection having data like this
{
"_id" : ObjectId("5da594c15324fec81d000027"),
"password" : "******",
"activation" : "Active",
"userType" : "Author",
"email" : "something#gmail.com",
"name" : "Something",
"profilePicture" : "profile_pictures/5da594c15324fec81d0000271607094354423image.png",
"__v" : 0
}
On the other hand userlog has data like this
{
"_id" : ObjectId("5fcb7bb4485c34a41900002b"),
"duration" : 2.54,
"page" : 1,
"activityDetails" : "Viewed Page for seconds",
"contentType" : "article",
"activityType" : "articlePageStayTime",
"bookId" : ObjectId("5f93e2cc74153f8c1800003f"),
"ipAddress" : "::1",
"creator" : ObjectId("5da594c15324fec81d000027"),
"created" : ISODate("2020-12-05T12:23:16.867Z"),
"__v" : 0
}
What I need is data like below
{
"_id" : ObjectId("5da594c15324fec81d000027"),
"password" : "******",
"activation" : "Active",
"userType" : "Author",
"email" : "something#gmail.com",
"name" : "Something",
"profilePicture" : "profile_pictures/5da594c15324fec81d0000271607094354423image.png",
"userlogs":
[{
"_id" : ObjectId("5fcb7bb4485c34a41900002b"),
"duration" : 2.54,
"page" : 1,
"activityDetails" : "Viewed Page for seconds",
"contentType" : "article",
"activityType" : "articlePageStayTime",
"bookId" : ObjectId("5f93e2cc74153f8c1800003f"),
"ipAddress" : "::1",
"creator" : ObjectId("5da594c15324fec81d000027"),
"created" : ISODate("2020-12-05T12:23:16.867Z"),
"__v" : 0
}]
}
I am trying to find all the user except admin with their log for each month. So my condition is user wont be admin and date will be between two range. But it is not working. My current code is below which is returning empty dataset-
User
.aggregate([
{
$match: {
userType: {
$ne:"admin"
}
},
"$and": [
{
"userlogs.created": {
$lte: dateCompare.end
}
},
{
"userlogs.created": {
$gte: dateCompare.start
}
}
]
},
{
$lookup:{
from: "userlogs", //or Races.collection.name
localField: "_id",
foreignField: "creator",
as: "userlogs"
},
},
]
I am using mongodb version 3.2
Try this
User.aggregate([
{
$match: {
userType: {
$ne:"admin"
}
},
{
$graphLookup:{
from: "userlogs", //or Races.collection.name
startWith: "$_id",
connectToField:"creator",
connectFromField:"_id",
maxDepth:0,
as: "userlogs",
restrictSearchWithMatch:{created:{
$lte:dateCompare.end,
$gte:dateCompare.start
}}
},
} ]
so I am trying to do a $lookup with Mongodb but I have a strange output.
I have two collections, "sites" and "consumptions".
sites :
{
"_id" : ObjectId("5b26db6e7f59e825909da106"),
"siteId" : 49,
"industry" : "Commercial Property",
"sub_industry" : "Shopping Center/Shopping Mall",
"square_feet" : 497092,
"latitude" : 41.2161756,
"longitude" : -78.14809154,
"timezone" : "America/New_York",
"timezone_offset" : "-04:00",
"__v" : 0
}
consumptions :
{
"_id" : ObjectId("5b26db907f59e825909f3d2a"),
"timestamp" : 1325382000,
"dttm_utc" : ISODate("2012-01-01T00:40:00Z"),
"value" : 2.8956,
"estimated" : 0,
"anomaly" : "",
"site" : [
{
"_id" : ObjectId("5b26db727f59e825909da16a")
}
],
"__v" : 0
}
This is the $lookup I am trying to do :
db.consumptions.aggregate([
{
$lookup:
{
from: "sites",
localField: "site.id",
foreignField: "id",
as: "site"
}
}
])
The expected output would be to have the detail of the site in each consumption :
{
"_id" : ObjectId("5b26db907f59e825909f3d2a"),
"timestamp" : 1325382000,
"dttm_utc" : ISODate("2012-01-01T00:40:00Z"),
"value" : 2.8956,
"estimated" : 0,
"anomaly" : "",
"site" : [
{
"_id" : ObjectId("5b26db6e7f59e825909da106"),
"siteId" : 49,
"industry" : "Commercial Property",
"sub_industry" : "Shopping Center/Shopping Mall",
"square_feet" : 497092,
"latitude" : 41.2161756,
"longitude" : -78.14809154,
"timezone" : "America/New_York",
"timezone_offset" : "-04:00",
"__v" : 0
}
],
"__v" : 0
}
This is the output I am getting with the $lookup :
{
"_id" : ObjectId("5b26db907f59e825909f3d2a"),
"timestamp" : 1325382000,
"dttm_utc" : ISODate("2012-01-01T00:40:00Z"),
"value" : 2.8956,
"estimated" : 0,
"anomaly" : "",
"site" : [
{
"_id" : ObjectId("5b26db6e7f59e825909da0f3"),
"siteId" : 6,
"industry" : "Commercial Property",
"sub_industry" : "Shopping Center/Shopping Mall",
"square_feet" : 161532,
"latitude" : 34.78300117,
"longitude" : -106.8952497,
"timezone" : "America/Denver",
"timezone_offset" : "-06:00",
"__v" : 0
},
{
"_id" : ObjectId("5b26db6e7f59e825909da0f4"),
"siteId" : 8,
"industry" : "Commercial Property",
"sub_industry" : "Shopping Center/Shopping Mall",
"square_feet" : 823966,
"latitude" : 40.32024733,
"longitude" : -76.40494239,
"timezone" : "America/New_York",
"timezone_offset" : "-04:00",
"__v" : 0
}, ... (all the sites details are listed)
],
"__v" : 0
}
Thank you in advance for your help !
You need to first $unwind the site array to match site._id to the foreign field _id and then $group to rolling back into the arrays again.
db.collection.aggregate([
{ "$unwind": "$site" },
{ "$lookup": {
"from": Site.collection.name,
"localField": "site._id",
"foreignField": "_id",
"as": "site"
}},
{ "$unwind": "$site" },
{ "$group": {
"_id": "$_id",
"value": { "$first": "$value" },
"estimated": { "$first": "$estimated" },
"anomaly": { "$first": "$anomaly" },
"timestamp": { "$first": "$timestamp" },
"dttm_utc": { "$first": "$dttm_utc" },
"site": { "$push": "$site" }
}}
])
And if you have mongodb 3.6 then you can try this
db.collection.aggregate([
{ "$unwind": "$site" },
{ "$lookup": {
"from": Site.collection.name,
"let": { "siteId": "$site._id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$siteId" ] } } }
],
"as": "site"
}},
{ "$unwind": "$site" },
{ "$group": {
"_id": "$_id",
"value": { "$first": "$value" },
"estimated": { "$first": "$estimated" },
"anomaly": { "$first": "$anomaly" },
"timestamp": { "$first": "$timestamp" },
"dttm_utc": { "$first": "$dttm_utc" },
"site": { "$push": "$site" }
}}
])
Make sure you should put Site.collection.name correctly
I think that The $lookup doesn't work directly with an array.
try using $unwind first.
Where I arrived was this I needed to put the user data on the athele:
{
"_id" : ObjectId("5963c6aa1aaf2c1c702ea86f"),
"updatedAt" : ISODate("2017-07-10T18:25:46.941+0000"),
"createdAt" : ISODate("2017-07-10T18:25:46.933+0000"),
"name" : "athleta01",
"nickname" : "01",
"email" : "aaaa#terra.com.br",
"birthday" : ISODate("1986-09-15T03:00:00.000+0000"),
"gender" : "0",
"disable" : false,
"deletedAt" : false,
"profile" : [
{
"departament" : ObjectId("5963c6281aaf2c1c702ea86c"),
"profession" : "athlete",
"_id" : ObjectId("5963c6aa1aaf2c1c702ea870")
}
],
"photo" : null,
"__v" : NumberInt(0)
}
doc Athlete:
{
"_id" : ObjectId("5963c6aa1aaf2c1c702ea871"),
"updatedAt" : ISODate("2017-07-10T19:53:08.285+0000"),
"createdAt" : ISODate("2017-07-10T18:25:46.948+0000"),
"position" : "atacante",
"shirt" : NumberInt(15),
"document" : "87956421345",
"weight" : "10.5",
"height" : "2.73",
"profileId" : ObjectId("5963c6aa1aaf2c1c702ea870"),
"disable" : false,
"deletedAt" : false,
"device" : {
"activityLevel" : "vai atuazliar",
"maxHeartRate" : NumberInt(45),
"reposeHeartRate" : NumberInt(90),
"codSensor" : "1a2s4e1s"
},
"__v" : NumberInt(0)
}
Already tried to make the exit with the project and to set up a group also I did not have the result that I expected. I think I'm missing out on some point.
db.users.aggregate(
[
{
$match: {
"profile.departament" : ObjectId("5963c6281aaf2c1c702ea86c")
}
},
{
$lookup: {
"from" : "institution",
"localField" : "profile.departament",
"foreignField" : "departament._id",
"as" : "user_departament"
}
},
{
$lookup: {
"from" : "athlete",
"localField" : "profile._id",
"foreignField" : "profileId",
"as" : "athlete_departament"
}
},
{
$unwind: {
path: "$athlete_departament",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "users",
localField: "athlete_departament.profileId",
foreignField: "profile._id",
as: "username",
}
},
{
$group: {
_id: { name: "$user_departament.name", cnpj: "$user_departament.cnpj", photo: "$user_departament.photo"},
departaments: {
$push : "$user_departament.departament"
},
athletes: {
$push : '$athlete_departament',
}
}
},
{$unwind : "$departaments"},
{$unwind : "$departaments"},
{$unwind : "$_id.name"},
{$unwind : "$_id.cnpj"},
{$unwind : "$_id.photo"},
{$unwind : "$athletes"},
{$unwind : "$athletes"},
{
$group : {
_id : "$_id",
departaments : {$addToSet : "$departaments"},
athletes : {$addToSet : "$athletes"}
}
},
{ "$project": {
//_id : { name: "$_id", departaments: { $concatArrays: [ {$arrayElemAt: [ "$departaments", 0 ]}, "$profiles", "$athletes" ] }}
_id : { institution: "$_id", departaments: { info: "$departaments", profiles: "$profiles", athletes: { username: "$username", athlete: "$athletes" }, }}
//_id : { name: "$_id", departaments: { "departament": { $concatArrays: [ { $arrayElemAt: ["$departaments", 0]}, "$athletes" ] }}}
//_id : { name: "$_id", departaments: { $concatArrays: [ {$arrayElemAt: [ "$departaments", 0 ]}, "$profiles", "$athletes" ] }}
}},
{$unwind : "$_id.departaments.info"},
{$unwind : "$_id.departaments.info"}
]);
{
"id": "1",
"label" : "alex",
"children" : [
{
"id" : "rJqS8aW-W"
},
{
"id" : "SkF8UaW-W"
}
],
}
{
"label" : "felix",
"id" : "rJqS8aW-W",
"children" : [
{
"id" : "S1gcBUT---"
},
{
"id" : "r1ltUUpZbb"
}
]
}
{
"label" : "tom",
"id" : "SkF8UaW-W",
"children" : [
{
"id" : "S1gcBUT---"
},
{
"id" : "r1ltUUpZbb"
}
]
}
....
What I want to do is, replace id fields with the documents.
{
"id": "1",
"label" : "alex",
"children" : [
{
"id" : "rJqS8aW-W"
},
{
"id" : "SkF8UaW-W"
}
],
details: [
{
"label" : "felix",
"id" : "rJqS8aW-W",
"children" : [
{
"id" : "S1gcBUT---"
},
{
"id" : "r1ltUUpZbb"
}
]
},
{
"label" : "tom",
"id" : "SkF8UaW-W",
"children" : [
{
"id" : "S1gcBUT---"
},
{
"id" : "r1ltUUpZbb"
}
]
}
]
}
...
To be able to achieve this, I wrote;
db.collection.aggregate([
{$lookup: {from: "collection" , localField: "children.id", foreignField: "id", as: "details"}}
])
But it doesn't return as I expect. Can't we use $lookup with arrays? I mean I want to search based on a field in the array.
edit:
some modifications like,
db.collection.aggregate([
{$lookup: {from: "collection" , localField: "$children.id", foreignField: "id", as: "details"}}
])
or
db.collection.aggregate([
{$lookup: {from: "collection" , localField: "children.$.id", foreignField: "id", as: "details"}}
])
also throws error.
What should I do? flatting the children id field and than $lookup?
I want to get document with foreign key by using $lookup and $match on MongoDB.
There is a "Jobs" collection which stores Job document. In Job document there are two field using as foreing key "creatorParent" and "Children".
CreatorParent is a foreign key for "Users" collection and Children array contains id for user's children.
When I list the whole jobs, I want to retrieve detail from "Users" collection for both CreatorParent ID and ChildrenID. I want to marshall "Job" document with ParentDetail and ChildDetail. I don't want to write a custom method for that. Is it possible to handle it with MongoDB query?
By the way I'm beginner on MongoDB so should store needed details on Children and CreatorParent instead of storing ObjectId?
Users document:
{
"_id" : ObjectId("58daf84877733645eaa9b44f"),
"email" : "meto93#gmail.com",
"password" : "vpGl+Fjnef616cRgNbCkwaFDpSI=",
"passwordsalt" : "99397F4A9D3A499D96694547667E74595CE994D2E83345D6953EF866303E8B65",
"children" : [
{
"_id" : ObjectId("58daf84977733645eaa9b450"),
"name" : "Mert",
"age" : 5,
"additionalinformation" : "ilk cocuk",
"creationtime" : ISODate("2017-03-28T23:56:56.952Z"),
"userid" : ObjectId("58daf84877733645eaa9b44f"),
"gender" : null
},
{
"_id" : ObjectId("58daf84977733645eaa9b451"),
"name" : "Sencer",
"age" : 7,
"additionalinformation" : "ikinci cocuk",
"creationtime" : ISODate("2017-03-28T23:56:56.952Z"),
"userid" : ObjectId("58daf84877733645eaa9b44f"),
"gender" : null
}
]
}
Job
{
"_id" : ObjectId("58db0a2d77733645eaa9b453"),
"creationtime" : ISODate("2017-03-29T01:13:17.509Z"),
"startingtime" : ISODate("2017-04-03T13:00:00.000Z"),
"endingtime" : ISODate("2017-04-03T17:00:00.000Z"),
"children" : [
ObjectId("58daf84977733645eaa9b450"),
ObjectId("58daf84977733645eaa9b451")
],
"creatorparent" : ObjectId("58daf84877733645eaa9b44f"),
"applicants" : []
}
If I understood it correctly. A similar solution is achievable using MongoDB 3.4's $addFields and $lookup aggregation steps.
Mongo aggregation:
[
{
$addFields: {
"job":"$$ROOT"
}
},
{
$unwind: {
path : "$children"
}
},
{
$lookup: {
"from" : "users",
"localField" : "creatorParent",
"foreignField" : "_id",
"as" : "creatorParent"
}
},
{
$lookup: {
"from" : "users",
"localField" : "children",
"foreignField" : "_id",
"as" : "children"
}
},
{
$group: {
"_id": "$_id",
"job": { "$first": "$job" },
"creatorParent" : { "$first" : "$creatorParent" },
"children": { "$addToSet": { $arrayElemAt: [ "$children", 0 ] } }
}
}
]
The output will look like the following:
{ "_id" : ObjectId("58da9cb6340c630315348114"),
"job" : {
"_id" : ObjectId("58da9cb6340c630315348114"),
"name" : "Developer",
"creatorParent" : ObjectId("58da9c79340c630315348113"),
"children" : [
ObjectId("58da9c6d340c630315348112"),
ObjectId("58da9c5f340c630315348111")
],
"hourly_rate" : 12.0,
"additional_information" : "other infos"
},
"creatorParent" : [
{
"_id" : ObjectId("58da9c79340c630315348113"),
"name" : "The Boss",
"age" : 40.0
}
],
"children" : [
{
"_id" : ObjectId("58da9c5f340c630315348111"),
"name" : "James",
"age" : 28.0
},
{
"_id" : ObjectId("58da9c6d340c630315348112"),
"name" : "Andrew",
"age" : 26.0
}
]}
UPDATE:
If you substitute the last $group stage with this:
{
"_id": "$_id",
"name": { "$first": "$name" },
"jobstatus": { "$first": "$jobstatus" },
"hourlyrate": { "$first":"$hourlyrate" },
"creatorparent" : { "$first" : "$creatorparent" },
"children": { "$addToSet": { $arrayElemAt: [ "$children", 0 ] } }
}
Then you can achieve what you would like to, but in this $group stage you have to specify every field of job one-by-one with the $first expression.