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

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 ]

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.

How to create dynamic query in mongoose for update. i want to update multiple data(Not all) with the help of Id

If I'm doing this, the field which I don't want to update is showing undefined. Any solution? (Like generating dynamic query or something)
exports.updateStudentById = async (req, res) => {
try {
const updateAllField = {
first_name: req.body.first_name,
last_name: req.body.last_name,
field_of_study: req.body.field_of_study,
age: req.body.age,
};
const data = await student_master.updateOne(
{ _id: req.body._id },
{ $set: updateAllField }
);
res.json({ message: "Student Data Updated", data: data });
} catch (error) {
throw new Error(error);
}
};
You can go for a dynamic query creation .Example
const requestBody = {
first_name: "John",
last_name: "Cena",
field_of_study: ""
}
const query={};
if(requestBody.first_name){
query["first_name"]=requestBody.first_name
}
if(requestBody.last_name){
query["last_name"]=requestBody.last_name
}
Check for the fields that are present in req.body and create a dynamic query
and when updating using mongoose use this
const data = await student_master.updateOne(
{ _id: req.body._id },
{ $set: query }
);
In this way only those fields would be updated which are present in your req.body

Mongoose populate method not populating all fields after updating document

I change the order of an array and then populate doesn't work. It still works on the documents that I did not try to update.
I have a seccion model with preguntas inside of it
SECCION MODEL
const Schema = mongoose.Schema;
const seccionSchema = new Schema({
titulo: { type: String },
preguntas: [
{
type: Schema.Types.ObjectId,
ref: 'Pregunta',
},
],
});
PREGUNTA MODEL
const Schema = mongoose.Schema;
const preguntaSchema = new Schema({
titulo: { type: String, required: true },
});
A "Seccion" document with 2 "preguntas" looks something like this:
{
"preguntas": [
"5fb0a48ccb68c44c227a0432",
"5fb0a48ccb68c44c227a0433"
],
"_id": "5fb0a50fcb68c44c227a0436",
"titulo": "Seccion 2",
"__v": 3
}
What I want to do is change the order in the preguntas array, so that the _ids end up being [33, 32] instead of [32, 33] (last two digits of the _ids). I can update using ".save()" method or ".findOneAndUpdate()". Here is the code using "findOneAndUpdate":
router.put('/:id', async (req, res) => {
const seccionId = req.params.id;
try {
const seccion = await Seccion.findOneAndUpdate(
{ _id: seccionId },
{ preguntas: ['5fb0a48ccb68c44c227a0433', '5fb0a48ccb68c44c227a0432'] },
{ new: true }
);
res.json(seccion);
} catch (err) {
res.json({ message: err });
}
});
The result works (and the order is changed in mongo atlas):
{
"preguntas": [
"5fb0a48ccb68c44c227a0433",
"5fb0a48ccb68c44c227a0432"
],
"_id": "5fb0a50fcb68c44c227a0436",
"titulo": "Seccion 2",
"__v": 4
}
But when I try to use the "populate()" method of mongoose, only one of the "preguntas ids" or none are populated (below only the "pregunta" with id ending in 32 was populated):
{
"preguntas": [
{
"_id": "5fb0a48ccb68c44c227a0432",
"titulo": "Pregunta 3",
"__v": 0
}
],
"_id": "5fb0a50fcb68c44c227a0436",
"titulo": "Seccion 2",
"__v": 4
}
So my guess is that the populate method is not working correctly, but it does work on the other "Seccion" documents where I have not tryed to change the order of the "preguntas" inside the array. Here is the code that uses the "populate" method:
router.get('/:id', async (req, res) => {
const seccionId = req.params.id;
try {
const seccion = await Seccion.findById(seccionId).populate('preguntas');
res.json(seccion);
} catch (err) {
res.json({ message: err });
}
});
It has been really hard to make the populate work, I really appreciate anyone's help
Are you sure that pregunta of id 5fb0a48ccb68c44c227a0433 exists in the preguntas collection?
The way populate works is that it does a second find operation on the preguntas collection with the ids in the array, if a document is not found, it will be omitted from the array.
The issue is probably that the one ending with 33 does not exist in the preguntas collection.

Update query adding ObjectIDs to array twice

I am working on a table planner application where guests can be assigned to tables. The table model has the following Schema:
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
const tableSchema = new mongoose.Schema({
name: {
type: String,
required: 'Please provide the name of the table',
trim: true,
},
capacity: {
type: Number,
required: 'Please provide the capacity of the table',
},
guests: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Guest',
}],
});
module.exports = mongoose.model('Table', tableSchema);
Guests can be dragged and dropped in the App (using React DND) to "Table" React components. Upon being dropped on a table, an Axios POST request is made to a Node.js method to update the Database and add the guest's Object ID to an array within the Table model:
exports.updateTableGuests = async (req, res) => {
console.log(req.body.guestId);
await Table.findOneAndUpdate(
{ name: req.body.tablename },
{ $push: { guests: req.body.guestId } },
{ safe: true, upsert: true },
(err) => {
if (err) {
console.log(err);
} else {
// do stuff
}
},
);
res.send('back');
};
This is working as expected, except that with each dropped guest, the Table model's guests array is updated with the same guest Object ID twice? Does anyone know why this would be?
I have tried logging the req.body.guestID to ensure that it is a single value and also to check that this function is not being called twice. But neither of those tests brought unexpected results. I therefore suspect something is wrong with my findOneAndUpdate query?
Don't use $push operator here, you need to use $addToSet operator instead...
The $push operator can update the array with same value many times
where as The $addToSet operator adds a value to an array unless the
value is already present.
exports.updateTableGuests = async (req, res) => {
console.log(req.body.guestId);
await Table.findOneAndUpdate(
{ name: req.body.tablename },
{ $addToSet : { guests: req.body.guestId } },
{ safe: true, upsert: true },
(err) => {
if (err) {
console.log(err);
} else {
// do stuff
}
},
);
res.send('back');
};
I am not sure if addToSet is the best solution because the query being executed twice.
If you used a callback and a promise simultaneously, it would make the query executes twice.
So choosing one of them would make it works fine.
Like below:
async updateField({ fieldName, shop_id, item }) {
return Shop.findByIdAndUpdate(
shop_id,
{ $push: { menuItems: item } },
{ upsert: true, new: true }
);
}

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);
});
});
};