Mongoose - find by referenced field _id - mongodb

Collection Schema
const notificationsSchema = new mongoose.Schema({
content: {
type: String,
required: true,
maxlength: 1000
},
recipient: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
}
}
});
Corresponding database entry
{
"_id" : ObjectId("607c1ebc3c2e16b610d74464"),
"content" : "Test Content",
"recipient" : "607c1e2c0bb25343e53abf45" <--- Existing _id of a user
}
Trying to find by recipient field
deleteUser: combineResolvers(async (_, { id }) => {
try {
const user = await User.findById(id);
console.log(user.id); <-- returns 607c1e2c0bb25343e53abf45
// Option 1
const notification = await Notification.findOne({ recipient: user.id });
console.log(notification); <-- returns null
// Option 2
const userId = mongoose.Types.ObjectId(user.id);
const notificationObjectId = await Notification.findOne({ recipient: userId });
console.log(notificationObjectId); <-- returns null
// Working alternative
const notificationAlternative = await Notification.findOne({ content: "Test Content" });
console.log(notificationAlternative); <-- returns the correct document
return true;
} catch (error) {
throw error;
}
})
Just to clarify, I am not trying to find the user by the recipient field, I am trying to find the notification document by the recipient field.
Why can I not retrieve the document by the recipient id? What am I missing?

The error most probably would be because of difference in the datatypes while executing const notification = await Notification.findOne({ recipient: user.id }); . Check the datatype of user.id and need to convert to string user.id.toString()

Related

How find in mongoose by an array property

I have defined my Conversation scheme like this:
const { Schema, model } = require("mongoose");
const ConversationSchema = Schema(
{
members: {
type: Array,
},
},
{ timestamps: true }
);
module.exports = model("Conversation", ConversationSchema);
My problem is that when I want to create a conversation model I search first if there is already a conversation.
const newConversation = async (req, res = response) => {
try {
const { senderId, receiverId } = req.body;
const conversation = await Conversation.find({
members: { $in: [senderId, receiverId] },
});
if (conversation.length === 0) {
const dbConversation = new Conversation({
members: [senderId, receiverId],
});
await dbConversation.save();
return res.status(201).json({
ok: true,
conversation: dbConversation
});
} else {
return res.status(403).json({
ok: false,
msg: "Conversation already exist",
});
}
} catch (err) {
return res.status(500).json({
ok: false,
msg: "Please contact with administrator",
});
}
};
senderId and receivedId are the ids of the users that are in that conversation, but it doesn't work.
How can I make it check if there is already a conversation with both ids?
Per the comments, we came to understand that the thing that wasn't working about the current code was always taking the code path that returned the message that the "Conversation already exist". This meant that the following query was always returning data:
const conversation = await Conversation.find({
members: { $in: [senderId, receiverId] },
});
The logic here does not match the logic implied in the question. This syntax uses the $in operator to find documents whose members array has at least one of the values passed to it (here the senderId and the receiverId).
To instead find documents where both of those people are present in the members array, you want to use the $all operator instead:
const conversation = await Conversation.find({
members: { $all: [senderId, receiverId] },
});
Working Mongo Playground example here.

Mongoose throws E11000 duplicate key error when updating a document with .save() method

I have a user model as shown below:
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
minlength: 3,
maxlength: 30,
validate: {
validator: function(v) {
return /^[a-zA-Z0-9]+$/.test(v);
},
message: "Your user name must be alphanumeric."
},
unique: true
},
email: {
type: String,
required: true,
validate: {
validator: function(v) {
return /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")#(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/.test(v);
},
message: "Invalid e-mail address."
},
unique: true
},
password: {
type: String,
required: true,
minlength: 4,
maxlength: 1024
},
isAdmin: {
type: Boolean,
default: false
},
devices: [{
type: mongoose.SchemaTypes.ObjectId,
ref: 'Device'
}],
joinDate: {
type: Date,
default: Date.now
}
});
const User = mongoose.model('User', userSchema);
I have a users.js Express.js router to manage users. One of these routes update the existing user with specified user ID. Here's the route:
// Modify a user's profile
router.put('/:userId', [auth, authAdmin], async function(req, res, next) {
if(!isValidObjectID(req.params.userId)) return res.status(400).send({ message: 'Given ID is not valid.', status: 400 });
const { error } = validate(req.body);
if(error) return res.status(400).send({ message: error.details[0].message, status: 400 });
let user = await User.findOne({ email: req.body.email });
if(user && user._id && user._id != req.params.userId) return res.status(400).send({ message: 'E-mail address is already in use.', status: 400 });
user = await User.findOne({ username: req.body.username });
if(user && user._id && user._id != req.params.userId) return res.status(400).send({ message: 'Usename is already in use.', status: 400 });
user = await User.findById(req.user._id);
user.username = req.body.username;
user.email = req.body.email;
if(req.body.isAdmin) user.isAdmin = req.body.isAdmin;
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(req.body.password, salt);
try {
user = await user.save();
return res.send(_.omit(user.toObject(), 'password'));
} catch(exception) {
console.log('Put 1:', exception);
}
});
When I use this route to update an existing user's only username I get MongoServerError: E11000 duplicate key error collection: iotapi.users index: email_1 dup key: { email: "test#gmail.com" } error. There's something which doesn't make sense. I also have another route just for users to update their email addresses. That route does almost the same functionality except updating username. It works very well, but when I update username along with the email, it throws the error.
I tried to use .findOneByIdAndUpdate() method as well to update documents but It didn't work out. I got the same error.
There is a typo
user = await User.findById(req.user._id);
Should be
user = await User.findById(req.params.userId);
update
Ok, not a typo but a genuine mistake then.
In the condition
let user = await User.findOne({ email: req.body.email });
if(user && user._id && user._id != req.params.userId)
You return 400 only when user with given email exists and its id differs from the Id send in the query string. In other words, when Ids are the same the code continues.
Then you reach the line where the user is loaded from auth session:
user = await User.findById(req.user._id);
This id can be different from the one sent in the request, so you try to update it with email of the other user. It cause duplication error.

How to update document with subdocument, or create new one if none are found

I'm trying to create a new subdocument object in an array when a user calls a command OR update the existing document based on their id. However, everything I've tried either gives me errors or it overrides the existing subdocument with the current user which is not what I want.
enter image description here
Basically I want to add another object in the array ( "1": Object ) that is a second user tracking whether they've used the command or not.
I can't remember all variations on code, but the current code I'm using:
const query = {
_id: guild.id,
members : [{
_id: user.id
}]
}
const update = {
members: {
_id: user.id,
bot: user.bot,
commandUsed: true
}
}
const options = {upsert: true, new: true}
await mongo().then(async () => {
console.log('Updating to...in use')
try {
// Find the document
await commandUsageSchema.findOneAndUpdate(query, update, options, function(error, result) {
if (!error) {
// If the document doesn't exist
if (!result) {
// Create it
result = new userSchema;
}
// Save the document
result.save(function(error) {
if (!error) {
// Do something with the document
} else {
throw error;
}
})
}
})
is creating a duplicate key error which is frustrating. Here is the layout of my schemas:
const mongoose = require('mongoose')
const reqString =
{
type: String,
required: true
}
const userSchema = mongoose.Schema({
_id: reqString,
bot: Boolean,
commandUsed: Boolean
}, {unique: true})
const commandUseSchema = mongoose.Schema({
_id: reqString,
members: [userSchema]
})
module.exports = mongoose.model('command-usage-checker', commandUseSchema)
I'm using mongoose and mongoDB (of course) with javascript and DiscordJS.

Cast to ObjectId failed for value "comments" at path "_id" for model "post"

Comments is an array nested inside Post Schema. I want to update corresponding post by push a new comment to the comments array. But got the error: CastError: Cast to ObjectId failed for value "comments" at path "_id" for model "post"
Read related posts
Tried to use "mongoose.Types.ObjectId", didn't work
Mongoose version ^5.5.4
All the ID I am using here are valid
const PostSchema = new Schema({
...
comments: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'user',
},
body: {
type: String,
required: [true, 'Content required'],
},
}
],
...
});
PostRouter.put('/posts/comments', (req, res) => {
const { id } = req.query;
const userID = req.body.user;
const body = req.body.body;
const comment = {
user: userID,
body: body,
};
Posts
.update({ _id: id }, { $push: { comments: comment }})
.then(result => {
res.status(200).json(result.ok);
})
.catch(err => console.log(err));
});
I have a similar one: add a "friendID" to User Modal "friends" array. works as expected.
const senderID = req.query.sender;
const recipientID = req.query.recipient;
Users .update({ _id: recipientID }, { $push: { friends: senderID }})
.then(result => res.status(200).json(result.ok))
.catch(err => console.log(err));
but the "comment" I try to add here is an object instead of a valid ID string.
I think the problem is inside "Comments" array, because "comment.user" is ref from my "User" Schema. Don't know how to solve this nested question with cast error.
mongoose.Types.ObjectId is redundant if userID and _id are valid mongodb _id.
PostRouter.put('/posts/comments', (req, res) => {
const { id } = req.query;
const userID = req.body.user;
const body = req.body.body;
const comment = {
user: userID,
body: body,
};
Posts
.update({ _id: id }, { $push: { comments: comment }})
.then(result => {
res.status(200).json(result.ok);
})
.catch(err => console.log(err));
});

Mongoose - how to move object to another collection

My db include following collections:
users
deleted_users
My code is following:
const name = { type: String, required: true, index: { unique: true } };
const UserSchema = new mongoose.Schema({ name });
const DeletedUserSchema = new mongoose.Schema({ name }, {
versionKey: 'version',
});
const UserModel = mongoose.model('User', UserSchema);
const DeletedUserModel = mongoose.model('Deleted_user', DeletedUserSchema);
router.put('/:id/move', (req, res) => {
UserModel.findOne(
{ _id: id }
).then((user) => {
if (!user) {
return fail(...);
}
console.log(`moving user width id ${id}`);
const newUser = new DeletedUserModel(user);
return newUser.save()
.then(
() => {
console.log('ok');
})
.catch((err) => {
console.log('catch err ', err);
});
});
}
but I always receive
{ Error
at model.wrappedPointCut [as save] (/~/prj/node_modules/mongoose/lib/services/model/applyHooks.js:111:29)
at UserModel.findOne.then (/~/prj/src/routes/user/index.js:123:20)
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
message: 'No matching document found for id "58dd804c434bdc1848d491cd"',
name: 'VersionError' }
Can you check that this id you are querying is not a String but an ObjectId because I think you are passing a String as id.