Data model reference using ObjectID in Mongoose - node.js

I have two mongoose models users and requests. One user can have multiple requests. To link these two data models, I am referencing the objectID of the user in the Request model. However when I am trying to fetch all the requests for a particular user ID, I don't get any result.
Users
var UserSchema = new Schema({
name: String,
username: String
});
module.exports = mongoose.model('User', UserSchema);
Requests
var RequestSchema = new Schema({
type: String,
user_id: { type : mongoose.Schema.Types.ObjectId, ref: 'User'}
});
module.exports = mongoose.model('Request', RequestSchema);
Below is the sample code which fetches the requests based on the User ID passed in the URL
exports.getRequests = function (req, res, next) {
var userId = req.params.id;
console.log('USER ID'+userId);
Request.findOne({'user_id': 'kenan'}, function (err, request) {
if (err) return next(err);
if (!requests){
console.log('NO REQUEST FOUND');
return res.send(401);
}
res.json(request);
});
};
I know I am using findOne which will return me a single resource. However this is failing and I am not getting any result. I have in my DB the data populated. Below is the URL for fetching the requests.
http://localhost:9000/api/V1/users/547ee1e82e47bb982e36e433/requests
Please help suggest as to what I am doing wrong over here.
Thanks

You need to populate the references to the users once you do a find().
Once you find all the requests, you need to resolve the references to
the users. Use populate to resolve the references.
After resolving, filter the requests based on the name that you
are searching for.
Updated Code:
Request
.find().populate({'path':'user_id',match:{'name':'kenan'}})
.exec(function (err, resp) {
var result = resp.filter(function(i){
return i.user_id != null;
});
console.log(result);
})

OK, the above answer is correct and thanks for your response. I incorrectly populated my Requests collection. This is just for every one's else knowledge. When you have a Object ID reference as one of the fields in the collection then make sure that you are inserting the ObjectID object instead of just the numerical value.
Wrong
{ "_id" : ObjectId("54803ae43b73bd8428269da3"), "user_id" : "547ee1e82e
47bb982e36e433", "name" : "Test", "service_type" : "test", "version" : "10"
, "environment" : "test", "status" : "OK", "__v" : 0 }
Correct
{ "_id" : ObjectId("54803ae43b73bd8428269da3"), "user_id" : ObjectId("547ee1e82e
47bb982e36e433"), "name" : "test", "service_type" : "test", "version" : "10"
, "environment" : "test", "status" : "OK", "__v" : 0 }

Related

findOneAndUpdate. Select array of messages, select message object and update likes array inside it

I use mongoose findOneAndUpdate method, but could't achieve the result. I always get the same object back. No errors. Here's my code.
User object:
{
"_id" : ObjectId("6273b64b607b1d228795f067"),
"username" : "User1",
"mail" : "user#inbox.com",
"password" : "$2b$12$NrJ9E8xSz1iCfmIPY0snC.e6x4B/ymqtH.at9uUWaCXTQUwJi1eem",
"messages" : [
{
"message" : "Brand new message",
"date" : 1652508162106.0,
"author" : "User1",
"likes" : []
}
],
"__v" : 16
}
Here's my backend code:
const {author} = req.params;
const {mssg, whoLiked} = req.body;
User.findOneAndUpdate({username: author, messages: mssg}, {$push: {likes: whoLiked}}, {new: true}, (err, user)=>{
err && console.log(err);
res.send(user)
})
})
Nothing udpates. Maybe I try to pick the message the wrong way... I know, it's possible to do this in one action. But I can't figure it out. What'd you say guys?
Ok, I finaly figured it out. Here's the code to push new person whoLiked into the likes array.
router.post("/:author/likeThePost", (req,res)=>{
const {author} = req.params;
const {mssg, whoLiked} = req.body;
console.log(mssg, whoLiked);
User.findOneAndUpdate({username: author, "messages.date": mssg.date}, {$push: {"messages.$.likes": whoLiked}}, {new: true}, (err, user)=>{
err && console.log(err);
res.send(user.messages)
})
})
Some comments:
I use mongoose findOneAndUpdate method. As far as I know there's the same node.js method, but the code might be different.
Let's break down the code a bit:
first you find the user who wrote the original message by username: "Some user name"
then you pick the message you need by some unique value. In my case this is just the timestamp: "messages.date": mssg.date . Same user just can't post two messages with the same Date.now() timestamp.
then you do what you need with the message've been picked:{$push: {"messages.$.likes": whoLiked}} Second dollar sign stands for picking right message. Looks like it's the index of it.

findByIdAndDelete doesn't actually delete a document, just nulls the value for each key

Newbie here using mongoose in node.js/express with MongoDB to try to build my own wedding website. I have in my app.js:
var Guest = require("./models/guest");
...
app.delete("/guests/:id", (req, res)=>{
Guest.findByIdAndDelete(req.params.id, (err, deletedGuest)=>{
if(err){
console.log(err);
res.redirect("/guests");
} else {
console.log("deleted: " + deletedGuest);
res.redirect("/guests");
}
})
})
with the model
var mongoose = require("mongoose");
var guestSchema = new mongoose.Schema({
name: String,
email: String,
isComing: Boolean,
})
module.exports = mongoose.model("Guest", guestSchema);
And there's an html table of guests with a delete button next to each one. So what happens is if I try to delete a guest by clicking the button next to their name, it sets that guest's name, email, and isComing to null, but doesn't delete it. In other words, for the guest:
{ "_id" : ObjectId("5ef811d5ea932d06d38537f5"), "name" : "Steve Stevenson", "email" : "steve#steve.com", "isComing" : true, "__v" : 0 }
If I press "delete," I get:
{ "_id" : ObjectId("5ef811d5ea932d06d38537f5"), "name" : null, "email" : null, "isComing" : null, "__v" : 0 }
The ID stays the same, it just removes all the values from the other keys, except the "__v" field. (Incidentally, I also have no idea what that is). Nothing gets logged in the console- no error or deleted guest. Any idea what I'm doing wrong? Other general advice is also welcome-- I'm trying to learn here.
It turns out the problem was I forgot to set method-override. So it was just sending a blank post request with that ID— hence the null values for each key. Whoops.

MongoDB and Express - dynamic queries for routes with several id's

App is using Node and Express, and I have a MongoDB document with this structure, from db.users.find().pretty():
{
"_id" : ObjectId("hash1"),
"username" : "user1",
"notes" : [
{
"_id" : ObjectId("hash2"),
"data" : "This is note 1"
}
],
}
{
"_id" : ObjectId("hash3"),
"username" : "user2",
"notes" : [
{
"_id" : ObjectId("hash4"),
"data" : "This is note 2"
}
],
}
I need to remove a notes subdocument.
Using findByIdAndUpdate and pull, the route is:
app.delete('/users/:id/notes/:id', callback );
and the query:
var fieldsToSet = {
$pull: {notes: {_id: req.params.id}}
};
var options = { new: true };
req.app.db.models.User
.findByIdAndUpdate(req.user._id, fieldsToSet, options, callback)
I have no problem if I logged in as user1 and want to delete a note with hash2 ObjectId (see this as as a real hash generated by MongoDB), but if I still logged in as user1 and I want to delete a note with hash4 there is a problem, because I will always get a user._id with hash1. And i cant use req.params.id twice, it will always return me the last :id .
How can I get both id's dynamically?
Thanks
You need to have two different params for this, which makes sense since they're two things:
app.delete('/users/:userId/notes/:noteId', callback );
Then you can access the one you want with req.params.nodeId or req.params.userId

How can I query whether an objectID is in an array in MongoDB with Node.js?

This is how my model looks like in Mongo:
{
"_id" : ObjectId("5951552c5bb94058b203d3e3"),
"roleName" : "Bartender",
"userList" : [
ObjectId("5926ff5088a3da5bf0d3d43b")
],
"deadline" : ISODate("2017-06-26T18:40:44.153Z"),
"__v" : 1
}
I would like to query 2 things together.
1, -Show me the model of the "role" I'm looking for.
2, -Give me true or false depending on whether the user's ID is to be found in the userList array.
exports.roleView = function(req,res, next){
const userID = req.params.userId;
const roleID = req.params.roleId;
const findUser = Role.findById({ _id: roleID
}).populate('userList', 'userName')
.then((result)=>{return result})
.catch((err)=>{return err});
const userinRole = Role.findOne({
_id:roleID, userList: userID})
.then((result)=>{return true})
.catch((err)=>{return false});
return Promise.all([findUser, userinRole])
.then((role)=>{
res.json(role);
});
};
Yet, every time don't have a match in userinRole, Mongo would just simply freeze without response.
Is it actually the proper way, or would you approach it differently? Thank you so much!

MongoDb Node.js Driver - findOneAndUpdate() behavior without a field given

I noticed a strange behavior with the mongodb node.js driver findOneAndUpate()...
I mistakenly gave it just a objectId string....thinking it would default to searching by _id field of a document.... so, when I used
User.prototype.updatePetArray = function(user, petElement) {
return this.collection.findOneAndUpdate(user,
{ $push: { pets: petElement } },
{ returnOriginal: false,
maxTimeMS: QUERY_TIME});
}
it pulled up and modified this document, which does not have this number at all:
{ "_id" : ObjectId("56d4e2a381c9c28b3056f792"), "username" : "bob123", "location" : "AT", ...}
Why did it modify this document when 56d4d35881c9c28b3056f78a is not in the document?
After I test it following your code with one non-exist ObjectID,
var col = db.collection('users');
col.findOneAndUpdate('56cd129222222', {fname: 'Daved'}, function(err, r) {
if (err)
console.log(err);
else
console.log(r);
db.close();
});
As a result the first document in the collection was changed .
Per this doc
findOneAndUpdate(filter, update, options, callback)
filter: Specify an empty document { } to update the first document returned in the collection.
So I think this non-exist ObjectId is consider to the same behavior with {}
After reviewing the source code of findAndModify, eventually, the
// Execute the command
self.s.db.command(queryObject
is invoked, and the queryObject is
var queryObject = {
'findandmodify': self.s.name
, 'query': query
};
So I test runCommand in mongo shell with non-exist ObjectId as below, as result, the first document is returned.
> db.users.runCommand({findandmodify: 'users', query: '123ddae344', update: true
})
{
"lastErrorObject" : {
"updatedExisting" : true,
"n" : 1
},
"value" : {
"_id" : ObjectId("56c687275d81735019263d1f"),
"fname" : "Daved"
},
"ok" : 1
}
The docs keep saying the filter parameter should be an object.
The wrong behavior is likely to some side effect of mis-interpreting the value, being a string not an object (and maybe a truthy value, non-empty string).

Resources