I want to use mongoose to find in an array of objects by id.
I have this list:
{
"data":[
{
"_id":"60ce0ea7eb945a22288fd0ba",
"parent":"50ce0e44eb945a22288fd0b1",
"label":"label 1-2",
"ancestors":[
{
"_id":"50ce0e44eb945a22288fd0b1",
"label":"label 1-1"
},
{
"_id":"40ce077e90c6262bdc21aa44",
"label":"label 1"
}
]
},
{
"_id":"50ce0e44eb945a22288fd0b1",
"parent":"60ce077e90c6262bdc21aa55",
"label":"label 1-1",
"ancestors":[
{
"_id":"40ce077e90c6262bdc21aa44",
"label":"label 1"
}
]
},
{
"_id":"40ce077e90c6262bdc21aa44",
"parent":null,
"label":"label 1",
"ancestors":[]
}
]
}
This is the schema:
const categorySchema = new mongoose.Schema(
{
label: {
type: String,
required: true
},
parent: {
type: ObjectId,
default: null,
ref: 'category'
},
ancestors: [
{
_id: {
type: ObjectId,
ref: 'category'
},
label: String
}
]
},
{ timestamps: true }
);
I tried to do this:
async getDescendants(req, res) {
let { pId } = req.body;
if (!pId) {
return res.json({ error: 'All filled must be required' });
} else {
try {
const data = await patternModel
.find({ 'ancestors._id': pId })
.select({
_id: false,
label: true
})
.exec();
if (data) {
return res.json({ data });
}
} catch (err) {
return res.json({ err: err });
}
}
}
this is my actual result:
{
"data": []
}
when I change .find({ 'ancestors._id': pId }) to .find({ 'ancestors.label': label }) it works but not for the id.
It is not a simple field. It is an array of subdocuments. Use elemMatch.
Edit: When querying _id fields you will have to wrap convert them into ObjectIds (specific to Mongo).
let newPid = mongoose.Types.ObjectId(pId);
const data = await patternModel.find({ ancestors: { $elemMatch : { _id: newPid} } })
.select({ _id: false,label: true })
.exec();
Related
Here is my Schema
I am trying to add replies array inside answers array. If someone answers a question and if someone wants to reply on the given answer
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema;
const questionSchema = new mongoose.Schema(
{
postedBy: {
type: ObjectId,
required: true,
ref: "User",
},
question: {
type: String,
required: true,
},
photo: {
data: String,
required: false,
},
answers: [
{
userId: { type: ObjectId, ref: "User" },
answerType: {
data: String,
required: false,
},
answer: String,
replies: [
{
userId: { type: ObjectId, ref: "User" },
reply: String,
replyType: {
data: String,
required: false,
},
},
],
},
],
questionType: {
data: String,
required: false,
},
createdAt: {
type: Date,
required: true,
default: Date.now,
},
},
{ timeStamps: true }
);
module.exports = mongoose.model("Question", questionSchema);
Here is my Controller method
exports.postReply = (req, res) => {
const reply = req.body.reply || "";
const userId = req.user._id || "";
const answerId = req.body.answerId || "";
Question.findByIdAndUpdate(
{ _id: answerId },
({ $push: { answers: { answer: { replies: { reply, userId } } } } },
{ new: true }),
(err, newReply) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
} else {
res.json({
msg: "Reply posted successfully",
newReply,
});
}
}
);
};
I feel I am going wrong on the findOneAndUpdate method. I am getting no error on the console but newReply comes null. Any help will be appreciated.
I would suggest you using the $addToSet instead of the $push operator as you are adding a document to the array. (see: https://docs.mongodb.com/manual/reference/operator/update/addToSet/).
If you want to add more than one document to the array, refer also to the $each operator together with $addToSet.
So your coding can look similiar to this (note: the variable 'yourDocument' is the document you want to add):
Question.findByIdAndUpdate(
{ _id: answerId },
{ $addToSet: { answers: yourDocument } },
{ new: true },
(err, newReply) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
} else {
res.json({
msg: "Reply posted successfully",
newReply,
});
}
}
);
};
The problem is clearly the parentesis around
({ $push: { answers: { answer: { replies: { reply, userId } } } } }, { new: true })
Doing this console.log( ({a:1}, {b:2}) ); will log {b: 2} which means you are doing this
Question.findByIdAndUpdate( { _id: answerId }, { new: true }, (err, newReply) => {
So remove the parentesis and you should be good
Question.findByIdAndUpdate(
{ _id: answerId },
{ $push: { answers: { answer: { replies: { reply, userId } } } } },
{ new: true },
(err, newReply) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
} else {
res.json({
msg: "Reply posted successfully",
newReply,
});
}
}
);
Here is course schema and I want to push content object, but I can't find what is a good way. help please.
import mongoose from "mongoose";
export const Schema = new mongoose.Schema({
course_en: { type: String },
course: { type: String },
module: [
{
_id: {
type: String
},
module_en: String,
module: String,
content: [
{
_id: { type: String }
content_en: String,
content: String,
}
],
}
],
});
const Course = mongoose.model("Course", Schema);
export default Course;
Here is my tried code but don't work, it only work to add module object
db.update(
{ _id: _id, "module._id": module_id },
{
$push: { content: data }
}
)
.then((result: any) => {
console.log(`Content Posted ${result}`);
response.send({ "status": "success" });
})
.catch((error: any) => {
console.log(`Error ${error}`);
response.send({ "status": "fail" });
});
db.collection.update(
{ _id: _id, "module._id": module_id },
{ "$push":
{"module.$.content":
{
"_id":"123",
"content_en": "hello",
"content": "hey"
}
}
})
Assuming data is the object you want to add to content array, you can try something like this:
db.update(
{ _id: _id, "module._id": module_id },
{
$push: { "module.$.content" : data }
}
)
I have a User schema, with a messages array. The message array is filled by conversations id and referenced to a Conversation schema.
I want to fetch all conversations from a user, sort them by unread and then most recent messages. Finally, I must only return an array of lastMessage object.
For the moment, I have only managed to populate the whole user object.
Here is the Conversation Schema:
const conversationSchema = new mongoose.Schema(
{
name: { type: String, required: true, unique: true },
messages: [{ message: { type: String }, authorId: { type: String } }],
lastMessage: {
authorId: { type: String },
snippet: { type: String },
read: { type: Boolean },
},
},
{ timestamps: true }
);
conversationSchema.index({ name: 1 });
module.exports = mongoose.model("Conversation", conversationSchema);
And here is my code:
router.get("/conversations", async (req, res) => {
try {
const { userId } = req.query;
const user = await User.findById({ _id: userId }).populate("messages");
.sort({ updatedAt: 1, "lastMessage.read": 1 });
return res.json({ messages: user.messages });
} catch (err) {
console.log("error", err);
return res.json({ errorType: "unread-messages-list" });
}
});
How to do this?
app.post("/admin/editAssignedTask/:id", (req, res) => {
Task.findById(req.params.id, (err, task) => {
task.title = req.body.title;
task.priority = req.body.priority;
task.date = new Date(req.body.date);
task.description = req.body.description;
if (req.body.assignTo) {
console.log(req.body.assignTo);
if (typeof req.body.assignTo == "string") {
task.assignedTo = [...task.assignedTo, req.body.assignTo];
Staff.find({ _id: req.body.assignTo }, (err, staff) => {
console.log(staff);
});
Staff.updateOne(
{ _id: req.body.assignTo },
{ $set: { $push: { todo: req.params.id } } },
(err, up) => {
console.log(up);
}
);
in the above code staff.find() returns
[ { todo: [ 5cc44b02abde080691893e41, 5cc46186db0195071f117808 ],
completed: [],
_id: 5cc34724ab9d2d231642f925,
name: 'gokul',
mailId: 'gokul',
__v: 12 } ]
but staff.updateOne() returns
{ ok: 0, n: 0, nModified: 0 }
here n is 0 which means no obects are matched.!
Why does this happen?
below is the Staff schema
var userSchema = new mongoose.Schema({
name: String,
mailId: String,
todo: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Task"
}
],
completed: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Task"
}
]
});
change
Staff.updateOne(
{ _id: req.body.assignTo },
{ $set: { $push: { todo: req.params.id } } },
(err, up) => {
console.log(up);
}
);
to
Staff.updateMany(
{ _id: { $in: req.body.assignTo } },
{ $push: { todo: mongoose.Types.ObjectId(req.params.id) } },
(err, up) => {
console.log(up);
}
);
1) don't use $set for pushing or pulling array elements.
2) since in the schema, attribute 'todo' is an array of objectIDs and not strings, we need to explicitly convert the string to objectIDs using mongoose.Types.ObjectId()
Why this won't insert 20 document for me?
for (let i = 0; i <= 20; i++) {
const [updated] = await Promise.all([
Job.findOneAndUpdate(
{ _id: id },
{
$set: {
status: 'something'
}
},
{ upsert: true, new: true }
),
User.findOneAndUpdate(
{ _id: userId },
{
$set: { assigned: true }
},
{ upsert: true, new: true }
)
])
}
or I should use reduce and Promise resolve? Above query worked just that it doesn't insert 20 documents, it inserted one document.
If you have array of id then you can use bulkWrite
const id = ['844646464', '45646546546', '45646546546']
Model.bulkWrite(
req.body.idArray((id) => {
return ({
updateOne: {
filter: { _id: id },
update: { $set: { status: 'something' } },
upsert: true
}
})
})
})
It will create a request something like this
bulkWrite([
{ updateOne: { filter: { _id: id }, update: { $set: { status: 'something' } } },
{ updateOne: { filter: { _id: id }, update: { $set: { status: 'something' } } },
{ updateOne: { filter: { _id: id }, update: { $set: { status: 'something' } } }
])
Which efficiently performs all updates in a single request to the server with a single response.