How find in mongoose by an array property - mongodb

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.

Related

Reading an array into another array and storing all objects into a list using mongoose

I have a model (user) which contains an array (discontinued items). IDs are stored within this array, all of which belong to a specific item model. Now I would like to list all users where this array is not empty and then, in the same step, read out all articles from this array using their ID. Unfortunately, I can't do this because I get "undefinded" in the console when I print. What is that? Thank you very much
export const AlleUserMitArtikel = async (req, res) => {
try {
const alleUser = await User.find({
eingestellteArtikel: { $exists: true, $not: { $size: 0 } },
});
const liste = await Promise.all(
alleUser.map(async (user) => {
console.log(user); //Displays the correct object in the console, see below
user.eingestellteArtikel.map(async (id) => {
console.log(id); //Displays the correct ID, see Error section first two entries
return await Artikel.find({ _id: id });
});
})
);
console.log(liste); //Displays undefined
res.status(200).json(alleUser);
} catch (error) {
console.log(error);
}
};
USER MODEL:
{
_id: new ObjectId("630f36f0295ec768e2072c10"),
eingestellteArtikel: [ '630fe7caabfdf4387030a723', '63105cbedae68f22984ba434' ],
createdAt: 2022-08-31T10:24:48.845Z,
updatedAt: 2022-09-01T07:18:22.044Z,
__v: 0,
}
Undefined message:
630fe7caabfdf4387030a723
63105cbedae68f22984ba434
[ undefined, undefined ]

Adding multiple items to mongodb

I have this schema with
const userSchema = new mongoose.Schema(
{
skills: [{ name: { type: String, unique: true }, level: { type: Number } }],
and I am trying, after getting an array of objects from the client, to add all of them at once under a user in MongoDB
my old implementation when it was only an array is this one below. I have no idea how to go about it now tho. Could anyone help me?
const { email } = session;
const { skill } = req.body;
if (req.method === 'POST') {
try {
const user = await User.findOne({ email });
const updatedUser = await User.findOneAndUpdate(
{ email },
{ skills: [...user.skills, { name: skill }] }
);
const { email } = session;
// object from the client
const { skills } = req.body;
if (req.method === 'POST') {
try {
const updatedUser = await User.findOneAndUpdate(
{ email },
{ $set: {skills} }
);
the best is getting the array correctly formatted from front-end, if not use a map before call findOneAndUpdate

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.

Mongoose - find by referenced field _id

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()

How to update embedded document in mongoose?

I've looked through the mongoose API, and many questions on SO and on the google group, and still can't figure out updating embedded documents.
I'm trying to update this particular userListings object with the contents of args.
for (var i = 0; i < req.user.userListings.length; i++) {
if (req.user.userListings[i].listingId == req.params.listingId) {
User.update({
_id: req.user._id,
'userListings._id': req.user.userListings[i]._id
}, {
'userListings.isRead': args.isRead,
'userListings.isFavorite': args.isFavorite,
'userListings.isArchived': args.isArchived
}, function(err, user) {
res.send(user);
});
}
}
Here are the schemas:
var userListingSchema = new mongoose.Schema({
listingId: ObjectId,
isRead: {
type: Boolean,
default: true
},
isFavorite: {
type: Boolean,
default: false
},
isArchived: {
type: Boolean,
default: false
}
});
var userSchema = new mongoose.Schema({
userListings: [userListingSchema]
});
This find also doesn't work, which is probably the first issue:
User.find({
'_id': req.user._id,
'userListings._id': req.user.userListings[i]._id
}, function(err, user) {
console.log(err ? err : user);
});
which returns:
{ stack: [Getter/Setter],
arguments: [ 'path', undefined ],
type: 'non_object_property_call',
message: [Getter/Setter] }
That should be the equivalent of this mongo client call:
db.users.find({'userListings._id': ObjectId("4e44850101fde3a3f3000002"), _id: ObjectId("4e4483912bb87f8ef2000212")})
Running:
mongoose v1.8.1
mongoose-auth v0.0.11
node v0.4.10
when you already have the user, you can just do something like this:
var listing = req.user.userListings.id(req.params.listingId);
listing.isRead = args.isRead;
listing.isFavorite = args.isFavorite;
listing.isArchived = args.isArchived;
req.user.save(function (err) {
// ...
});
as found here: http://mongoosejs.com/docs/subdocs.html
Finding a sub-document
Each document has an _id. DocumentArrays have a special id method for looking up a document by its _id.
var doc = parent.children.id(id);
* * warning * *
as #zach pointed out, you have to declare the sub-document's schema before the actual document 's schema to be able to use the id() method.
Is this just a mismatch on variables names?
You have user.userListings[i].listingId in the for loop but user.userListings[i]._id in the find.
Are you looking for listingId or _id?
You have to save the parent object, and markModified the nested document.
That´s the way we do it
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Profile.findById(req.params.id, function (err, profile) {
if (err) { return handleError(res, err); }
if(!profile) { return res.send(404); }
var updated = _.merge(profile, req.body);
updated.markModified('NestedObj');
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, profile);
});
});
};