How can I use a Mongoose aggregate operation for this? Basically, I have two collections - 'Post' and 'Profile'. A user oid called "user" is the common reference between them. I want to use the ID from 'Post' to look up the "handle" element from 'Profile' and aggregate those results, so that the user's handle is included in the response.
Here is how I was able to achieve this when looking up a single post:
// @route GET api/posts/:id
// @desc Get post by id
// @access Public
router.get("/:id", (req, res) => {
Post.findById(req.params.id)
.then(post => {
Profile.findOne({ user: post.user })
.then(profile => res.json({ ...post._doc, userHandle: profile.handle }));
})
.catch(err => res.status(404).json({ "not found": err }));
});
However, when I tried to extend this approach (using a map operation) to the GET that returns ALL of the posts, it became unworkable.
// @route GET api/posts
// @desc Get all the posts
// @access Public
router.get("/", (req, res) => {
Post.find()
.sort({ date: -1 })
.then(posts =>
posts.map(
post =>
Profile.findOne({ user: post.user })
.then(profile => (
{
...JSON.parse(JSON.stringify(post)),
userHandle: profile.handle
}))
))
.then(posts => res.json(posts))
.catch(err => res.status(404).json({ "none found": err }));
});
In other words, if I have POST:
{
"_id": {
"$oid": "5d1523b98d9dd16d832a8c5e"
},
"user": {
"$oid": "5d1504e29dc0bd55461adca7"
},
"recipeName": "Pizza",
"ingredients": "Flour and sauch"
}
and PROFILE:
{
"_id": {
"$oid": "5d1505089dc0bd55461adca8"
},
"user": {
"$oid": "5d1504e29dc0bd55461adca7"
},
"handle": "jack",
"status": "Developer"
}
then what I really want is a JSON object like this (when I query a post): (notice 'userHandle' is now present)
{
"_id": {
"$oid": "5d1523b98d9dd16d832a8c5e"
},
"user": {
"$oid": "5d1504e29dc0bd55461adca7"
},
"recipeName": "Pizza",
"ingredients": "Flour and sauch",
"userHandle": "jack"
}
My GET needs to return a whole array of these posts.
Post.find().sort({date: -1}).then((posts) => {
Post.populate(posts, [{
path: 'user',
select: {}, // projection
model: User // User model
}], (err, posts) => {
if (err) return next(err);
return res.json(posts);
});
});
Read More About mongoose populate
If you don't want to include model
every time, you can add ref
(collection name) in the Schema
field, and it will automatically specify the model!
const postSchema = Schema({
...
user: { type: Schema.Types.ObjectId, ref: 'Users' }
});
getUserDetails: (request, response, next) => {
try {
let aggrQuery = [
{
$lookup: {
from: "Profiles",
localField: "Id",//key name of common id in post collection as a reference
foreignField: "profileId",// key name of common id in profile collection
as: "userDetails"// handle element as well as other data in profile collection will be stored in userDetails
}
},
{
"$project": {
//we have to carry "_id", "ingredients","recipeName","user" of post collection for the next projection,and whole user information is stored in "userDetails"
"_id": 1,
"ingredients":1,
"recipeName":1,
"user":1,
userDetails : { $arrayElemAt :
["$userDetails", 0]}
}
},
{
"$project": {
//finally we prepare required data set for response,"_id", "ingredients","recipeName","user" of post collection,and only userHandle of Profiles from userDetails array(which contains all profile data)
"_id": "$_id",
"ingredients":"$ingredients",
"recipeName":"$recipeName",
"user":"$user",
"userDetails":{
"userHandle" :"$userDetails.userHandle",
"additionField1":"$userDetails.additionField1",
"additionField2":"$userDetails.additionField2",
}
}
}
]
Post.aggregate(aggrQuery).exec((err, result) => {
if(err){
response.json({
isError: true,
statuscode: 404,
details: null
})
}
else{
response.json({
isError: false,
statuscode: 200,
details: result
})
}
})
}
catch{
response.json({
isError: true,
statuscode: 404,
details: null
});
}
},
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.