Mongoose - REST API - schema querying another model
I am trying to avoid DB callback requests.
Assuming you have two schematics that look like this:
1st) User schema
username : {type: String, unique: true},
age : {type: Number}
2nd) Action plan
owner: [{type: Schema.Types.ObjectId, ref: 'User'}],
city: {type: String},
date: {type: Date}
So far so good.
Now let's say you have a route to /user/:id
, what you expect is get username
and age
, but what if I would also like to return the last activity on that route
EDIT: Please note that is latest activity
not a value in the database. it is automatically calculated asactivity.find({owner: ObjectId(id)}).sort({date: -1}).limit(1)
What is being done right now:
User.findOne({username:req.params.username}).lean().exec(function(err,userDoc)
{
if(err) return errHandler(err);
Activity.findOne({owner:userDoc.username}).sort({date:-1}).exec(function(err,EventDoc){
if(err) return errHandler(err);
userDoc.latest_activity = EventDoc._id;
res.json(userDoc);
res.end();
})
})
The problem with the above snippet is that it is difficult to maintain.What if we want to add more to this API functionality? We would end up with the hellish requests callback if we didn't execute Q.
We tried to look at Virtual, but the problem is that you cannot actually query inside mongoose Virtual as it returns a race state and you most likely won't get this document in time.
We also tried to look at the infill, but we were unable to do that as the filing documentation is very poor.
Question: Is there a way to make this more modular?
Is there a way to avoid the addon request callback?
For example, is it possible?
User.findOne({username:req.params.username}).lean().populate(
{path:'Event',sort:{Date:-1}, limit(1)}
).exec(function(req,res))...
Thank!
source to share
Inspired by @ BrianShambien's answer, you can go with saving the post, but instead of just keeping it _id
from the user, you only keep the last activity subdock. Then when you grab that user, it has the last action right there.
User model
username : {type: String, unique: true},
age : {type: Number},
last_activity: ActivitySchema
Then you create a fallback to save messages to ActivitySchema
ActivitySchema.post('save', function(doc) {
UserSchema.findOne({username: doc.owner}).exec(function(err, user){
if (err) errHandler(err);
user.last_activity = doc;
user.save(function(err) {
if (err) errHandler(err);
});
});
});
********** UPDATE ************
This should include an update for the user if it is not the owner but is part of this action.
ActivitySchema.post('save', function(doc) {
findAndUpdateUser(doc.owner, doc);
if (doc.participants) {
for (var i in doc.participants) {
findAndUpdateUser(doc.participants[i], doc);
}
}
});
var findAndUpdateUser = function (username, doc) {
UserSchema.findOne({username: username}).exec(function (err, user) {
if (err) errHandler(err);
user.last_activity = doc;
user.save(function (err) {
if (err) errHandler(err);
});
});
});
source to share
In this case, the best way to deal with this would be to add a save link to your schema Activity
to store the latter _id
in latest_activity
your schema path User
. This way, you will always have access to the id without having to make an additional request.
ActivitySchema.post('save', function(doc) {
UserSchema.findOne({username: doc.owner}).exec(function(err, user){
if (err)
console.log(err); //do something with the error
else if (user) {
user.latest_activity = doc._id;
user.save(function(err) {
if (err)
console.log(err); //do something with the error
});
}
});
});
source to share