I'm currently running a query that looks like this:
courses = await Enrollment.aggregate([
{
$match: {
userId: userId
}
},
{
$lookup: {
from: 'courses',
localField: 'course',
foreignField: '_id',
as: 'course'
}
},
{
$unwind: '$course'
},
{
$project: {
course: {
courseCode: true,
name: true,
officialCode: true,
}
}
}
]);
This produces results that look like this
[{
"_id": "61e0652ba5c2fe5bdcdbdc23",
"course": {
"courseCode": "code2",
"name": "Terst class 2",
"officialCode": "test 202"
}
}]
I'm wondering if there is a way for me to bring courseCode, name and officialCode to the "highest level" of the document?
Thank you in advance.
You can do it with $replaceRoot aggregation pipeline. Add this as the last step:
{
"$replaceRoot": {
"newRoot": "$course"
}
}
Working example
Related
Couldn't find an answer here
I got a collection of segments, where an example segment looks like this:
{
_id: {
$oid: "62e5778e34362ad54db6b3f9"
},
name: "Good-Natured Developers"
}
And a collection of users, where an example user looks like this:
{
_id: {
$oid: "62e5225dfdc41ba4f7effefa"
},
age: {
$numberInt: "43"
},
segment_ids: [
{
$oid: "62e577a034362ad54db6b40a"
},
{
$oid: "62e5782f34362ad54db6b497"
},
]
}
How can I query how many users are there in every segment?
I tried this aggregation:
const usersInSegment = await userCollection
.aggregate([
{ $unwind: '$users'},
{ $project: { "_id": 1} },
{
$lookup: {
from: 'segments',
localField: 'segment_ids',
foreignField: '_id',
as: 'joined'
}
}
])
.limit(25)
.toArray()
But I get an empty array.
I used mondo db version 5.0.5
And faced with strange behaviour
I have query with lookup and unwind and afetr that need apply $in operator and look like I have wrong behaviour, I faced with data which don't have fake_dta in device_tree_tbl.ancestors property, any idea why ?
db.getCollection('metric').aggregate(
{
$lookup: {
from: "device_tree",
localField: "pub_key_digest",
foreignField: "node_id",
as: "device_tree_tbl"
}
},
{
$unwind: {
'path': '$device_tree_tbl'
}
},
{
$match: {
'device_tree_tbl.ancestors': {$in: ['fake_dta']}
}
}
)
sorry about that, If someone faced with unexpected behaviour in aggregate query first of all need to check square brackets :)
works like needed
db.getCollection('metric').aggregate([
{
$match:
{ 'label': 'network', 'metric': 'rx_bytes' }
},
{
$lookup: {
from: "device_tree",
localField: "pub_key_digest",
foreignField: "node_id",
as: "device_tree_tbl"
}
},
{
$unwind: {
'path': '$device_tree_tbl'
}
},
{
$match: {
'device_tree_tbl.ancestors': {$in: ['1573394823429_4']}
}
},
{
$group: {
"_id": "$id"
}
},
{
$sort: { _id: 1 }
}
])
I have found a few questions that relate to this (here and here) but I have been unable to interpret the answers in a way that I can understand how to do what I need.
I have 3 collections: Organisations, Users, and Projects. Every project belongs to one user, and every user belongs to one organisation. From the user's id, I need to return all the projects that belong to the organisation that the logged-in user belongs to.
Returning the projects from the collection that belong to the user is easy, with this query:
const projects = await Project.find({ user: req.user.id }).sort({ createdAt: -1 })
Each user has an organisation id as a foreign key, and I think I need to do something with $lookup and perhaps $unwind mongo commands, but unlike with SQL queries I really struggle to understand what's going on so I can construct queries correctly.
EDIT: Using this query
const orgProjects = User.aggregate(
[
{
$match: { _id: req.user.id }
},
{
$project: { _id: 0, org_id: 1 }
},
{
$lookup: {
from: "users",
localField: "organisation",
foreignField: Organisation._id,
as: "users_of_org"
}
},
{
$lookup: {
from: "projects",
localField: "users_of_org._id",
foreignField: "user",
as: "projects"
}
},
{
$unset: ["organisation", "users_of_org"]
},
{
$unwind: "$projects"
},
{
$replaceWith: "$projects"
}
])
Seems to almost work, returning the following:
Aggregate {
_pipeline: [
{ '$match': [Object] },
{ '$project': [Object] },
{ '$lookup': [Object] },
{ '$lookup': [Object] },
{ '$unset': [Array] },
{ '$unwind': '$projects' },
{ '$replaceWith': '$projects' }
],
_model: Model { User },
options: {}
}
assuming your documents have a schema like this, you could do an aggregation pipeline like below with 2 $lookup stages.
db.users.aggregate(
[
{
$match: { _id: "user1" }
},
{
$project: { _id: 0, org_id: 1 }
},
{
$lookup: {
from: "users",
localField: "org_id",
foreignField: "org_id",
as: "users_of_org"
}
},
{
$lookup: {
from: "projects",
localField: "users_of_org._id",
foreignField: "user_id",
as: "projects"
}
},
{
$unset: ["org_id", "users_of_org"]
},
{
$unwind: "$projects"
},
{
$replaceWith: "$projects"
}
])
I am doing an aggregation in Paper collection like below
const papers = await Paper.aggregate([
{
"$lookup": {
"from": "reviews",
"localField": "reviewId",
"foreignField": "_id",
"as": "review"
}
},
{ $unwind: '$review' }
]);
It returns the result that contains review object which has a reviews array like:
[
{
...
review: {
_id: 5f1638770f3a8d20f8c1beeb,
reviews: [Array],
},
...
}
]
If I make the review more clear, it is like below:
{
_id: 5f1638770f3a8d20f8c1beeb
reviews: [
{
_id: 5f164395857bcdd1d8674b69,
reviewerId: 5f15b28d534b5886c0d9eb8a
},
{
_id: 5f164395857bcdd1d8674b6a,
reviewerId: 5f1358c523dc2367c43a6311
}
]
}
In above, reviewerId inside reviews array refers to user id from "users" collection. I want to get users name, email, and address in reviews array like below:
{
reviews: [
{
_id: 5f164395857bcdd1d8674b69,
reviewerId: 5f15b28d534b5886c0d9eb8a
reviewer : {
name:"some_name",
email:"abc#example.com"
}
},
{
_id: 5f164395857bcdd1d8674b6a,
reviewerId: 5f1358c523dc2367c43a6311
reviewer : {
name:"some_name",
email:"efg#example.com"
}
}
]
}
How can I achieve it?
Hopefully, the structure of your collection almost similar as I mention below in the Mongo playground.
db.reviews.aggregate([
{
$unwind: {
path: "$reviews",
preserveNullAndEmptyArrays: false
}
},
{
$lookup: {
from: "user",
localField: "reviews.reviewerId",
foreignField: "_id",
as: "reviews.reviewer"
}
},
{
$group: {
_id: "$_id",
question: {
$first: "$question"
},
reviews: {
$addToSet: "$reviews"
}
}
}
])
Working Mongo playground
I have two collections :
Student
{
_id: ObjectId("657..."),
name:'abc'
},
{
_id: ObjectId("593..."),
name:'xyz'
}
Library
{
_id: ObjectId("987..."),
book_name:'book1',
issued_to: [
{
student: ObjectId("657...")
},
{
student: ObjectId("658...")
}
]
},
{
_id: ObjectId("898..."),
book_name:'book2',
issued_to: [
{
student: ObjectId("593...")
},
{
student: ObjectId("594...")
}
]
}
I want to make a Join to Student collection that exists in issued_to array of object field in Library collection.
I would like to make a query to student collection to get the student data as well as in library collection, that will check in issued_to array if the student exists or not if exists then get the library document otherwise not.
I have tried $lookup of mongo 3.6 but I didn`t succeed.
db.student.aggregate([{$match:{_id: ObjectId("593...")}}, $lookup: {from: 'library', let: {stu_id:'$_id'}, pipeline:[$match:{$expr: {$and:[{"$hotlist.clientEngagement": "$$stu_id"]}}]}])
But it thorws error please help me in regard of this. I also looked at other questions asked at stackoverflow like. question on stackoverflow,
question2 on stackoverflow but these are comapring simple fields not array of objects. please help me
I am not sure I understand your question entirely but this should help you:
db.student.aggregate([{
$match: { _id: ObjectId("657...") }
}, {
$lookup: {
from: 'library',
localField: '_id' ,
foreignField: 'issued_to.student',
as: 'result'
}
}])
If you want to only get the all book_names for each student you can do this:
db.student.aggregate([{
$match: { _id: ObjectId("657657657657657657657657") }
}, {
$lookup: {
from: 'library',
let: { 'stu_id': '$_id' },
pipeline: [{
$unwind: '$issued_to' // $expr cannot digest arrays so we need to unwind which hurts performance...
}, {
$match: { $expr: { $eq: [ '$issued_to.student', '$$stu_id' ] } }
}, {
$project: { _id: 0, "book_name": 1 } // only include the book_name field
}],
as: 'result'
}
}])
This might not be a very good answer, but if you can change your schema of Library to:
{
_id: ObjectId("987..."),
book_name:'book1'
issued_to: [
ObjectId("657..."),
ObjectId("658...")
]
},
{
_id: "ObjectId("898...")",
book_name:'book2'
issued_to: [
ObjectId("593...")
ObjectId("594...")
]
}
Then when you do:
{
$lookup: {
from: 'student',
localField: 'issued_to',
foreignField: '_id',
as: 'issued_to_students', // this creates a new field without overwriting your original 'issued_to'
}
},
You should get, based on your example above:
{
_id: ObjectId("987..."),
book_name:'book1'
issued_to_students: [
{ _id: ObjectId("657..."), name: 'abc', ... },
{ _id: ObjectId("658..."), name: <name of this _id>, ... }
]
},
{
_id: "ObjectId("898...")",
book_name:'book2'
issued_to: [
{ _id: ObjectId("593..."), name: 'xyz', ... },
{ _id: ObjectId("594..."), name: <name of this _id>, ... }
]
}
You need to $unwind the issued_to from library collection to match the issued_to.student with _id
db.student.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(id) } },
{ "$lookup": {
"from": Library.collection.name,
"let": { "studentId": "$_id" },
"pipeline": [
{ "$unwind": "$issued_to" },
{ "$match": { "$expr": { "$eq": [ "$issued_to.student", "$$studentId" ] } } }
],
"as": "issued_to"
}}
])