This is my user model with watched and watchLater arrays:
const UserSchema = new mongoose.Schema(
{
email: { type: String, required: true, unique: true },
watched: [{ type: Number }],
watchLater: [{ type: Number }],
},
{ timestamps: true },
)
I have this function where I want to remove id from watchLater and add to watched:
async addToWatched(id) {
const _id = this.getUserId()
return await this.store.User.findByIdAndUpdate(
{ _id },
// remove id from watchLater and add to watched,
{ new: true },
)
}
How do I do that?
Been reading docs a bit. This seems to work:
return await this.store.User.findByIdAndUpdate(
{ _id },
// remove id from watchLater and add to watched,
{ $pull: { watchLater: id }, $addToSet: { watched: id } },
{ new: true },
)
Related
I am using findOneAndUpdate, where I want
to return updated document
i dont want to return the entire document but only the following:
one object out of an array + a virtual property in the document.
const notifications = {
to:
messages: [
{_id: "23452", title:"hello"}, {_id: "23452", title:"bye"}
]
...
}
so for example I would want to only return the object {_id: "23452", title:"bye"} AND unreadCount virtual field prop.
my code works so far as I am returning updated document and only the message I want, but I dont know how to return also the unreadCount prop.
schema:
const notificationSchema = new mongoose.Schema({
to: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
messages: [{
title: {
type: String,
required: true
},
isRead: {
type: Boolean,
default: false
},
createdAt: {
type: Date,
default: new Date()
}
}, ]
},
{timestamps: true, toObject: {virtuals: true}
});
notificationSchema.virtual('unreadCount').get(function() {
... return count;...
})
updateRead: async (userId, id) => {
const notification = await Notification.findOneAndUpdate({to: userId, 'messages._id': id}, {
$set: { "messages.$.isRead": true} },
{ select: {
messages: {
$elemMatch: {_id: id}
}
}, new: true});
}
I am currently trying to setup a schema for custom Discord guild commands:
const GuildCommandsSchema = new mongoose.Schema({
_id: String,
commands: [
{
name: {
type: String,
unique: true,
required: true,
},
action: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
},
],
});
Is this ok, performancewise, or could I improve it?
I feel like Mongo would need to look through all commands, since it can't index any commands inside 'commands' even though 'name' is unique.
If that's fine, how can I access the values inside commands?
I would need to find the right command via 'name' if it exists, otherwise create it and add/update 'action' + 'author'.
I tried something like this:
const updatedCommand = await GuildCommands.findOneAndUpdate(
{ _id },
{
$set: {
[`commands.$[outer].name`]: name,
[`commands.$[outer].action`]: action,
[`commands.$[outer].author`]: author,
},
},
{
arrayFilters: [{ 'outer.name': name }],
}
);
Unfortunately that does not create commands if they don't exist.
Thanks for your help
aggregate
db.collection.update({},
{
$set: {
"commands.$[c].name": "1",
"commands.$[c].author": "1",
"commands.$[c].action": "1"
}
},
{
arrayFilters: [
{
"c.author": "34"
}
],
multi: true
})
mongoplayground
To answer my own question:
I changed my Schema to use Maps instead of Arrays for performance improvments and also better model management.
const GuildCommandsSchema = new mongoose.Schema(
{
_id: String,
commands: {
type: Map,
of: {
_id: false,
name: {
type: String,
required: true,
},
action: {
type: String,
required: true,
},
active: {
type: Boolean,
required: true,
default: true,
},
author: {
type: String,
required: true,
},
},
},
},
{ versionKey: false }
);
The new query to find and update/create a command is also better imo:
const findCommand = await GuildCommands.findOne({ _id });
if (!action) {
const getCommand = findCommand.commands.get(name);
if (getCommand) {
message.reply(getCommand.action);
} else {
message.reply(`Cannot find ${name}`);
}
} else {
findCommand.commands.set(name, {
name,
action,
author,
});
findCommand.save();
}
I have a document containing an array of objects. I wanted to update a particular element in the array. Tried using MongoDB shell, it works fine. But when I use in Mongoose in NodeJs, it is not working. The command is same in both the cases.
NodeJs code
const updateAttendance = await classModel.updateOne(
{
_id: item.classId,
'studentAttendance.studentId': item.studentId,
},
{ $set: { 'studentAtendance.$.present': true } }
)
Schema defination
const mongoose = require('mongoose')
const moment = require('moment')
const student = mongoose.Schema({
studentId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
unique: true,
},
present: {
type: Boolean,
default: false,
},
})
const classes = mongoose.Schema({
date: {
type: String,
required: true,
default: moment().format('DD/MM/YYYY'),
validate: {
validator: (value) => {
return moment(value, 'DD/MM/YYYY', true).isValid()
},
message: 'Provide a valid date in the format of DD/MM/YYYY',
},
},
courseId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Course',
},
studentAttendance: [
{
type: student,
},
],
})
module.exports = mongoose.model('Class', classes)
Sample data
{
"date": "20/06/2021",
"_id": "60cf5446970dc063e40356d3",
"courseId": "60ce2c3aca275c868089ac48",
"studentAttendance": [
{
"present": false,
"_id": "60cf5446970dc063e40356d4",
"studentId": "60ce315f9f83a24544414705"
},
{
"present": false,
"_id": "60cf5446970dc063e40356d5",
"studentId": "60ce31ba9f83a2454441470a"
},
{
"present": false,
"_id": "60cf5446970dc063e40356d6",
"studentId": "60ce38e49f83a24544414712"
}
],
"__v": 0
}
What am I doing wrong or where is the problem?
Without looking at the schema def, just taking a punt in the dark that you dont explicitly say its an ObjectId.
Easy solve, just wrap "item.studentId" in mongoose.Types.ObjectId().
So your new code would be like
const updateAttendance = await classModel.updateOne({
_id: mongoose.Types.ObjectId(item.classId),
'studentAttendance.studentId': mongoose.Types.ObjectId(item.studentId),
},
{ $set: { 'studentAtendance.$.present': true } }
)
Don't forget const mongoose = require('mongoose');
Based on the update your update statement needs 'updating'. try fixing the spelling of studentAttendance vs studentAtendance in the $set statement.
This is what I have so far. This is my AnswerSchema with a comments array nested within that I am trying to update.
const AnswerSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'user',
},
question: {
type: Schema.Types.ObjectId,
ref: 'question',
},
text: {
type: String,
required: true,
},
name: {
type: String,
},
avatar: {
type: String,
},
views: {
type: Number,
},
date: {
type: Date,
default: Date.now,
},
answerLikes: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'user',
},
},
],
comments: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'user',
},
text: {
type: String,
required: true,
},
name: {
type: String,
},
avatar: {
type: String,
},
commentLikes: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'user',
},
},
],
date: {
type: Date,
default: Date.now,
},
},
],
})
and here is my update route that I am trying to use to update the comments array text field
try {
const updatedAnswer = await Answer.findOneAndUpdate(
{ _id: req.params.answer_id },
{
$set: { 'comments.$[comment].text': formattedAnswer },
},
{
arrayFilters: [{'comment._id': req.params.comment_id }],
},
{ new: true }
)
res.json(updatedAnswer)
I keep getting the error 'Callback must be a function, got [object Object]' and cant figure out a fix.
Any ideas?
Thanks!
The problem in your code is that you are passing 4 parameters to the findOneAndUpdate function.
The 4th argument is a callback which accepts a function:
(err /* an error if occurred */, doc /* the updated document */) => {}
In order to solve that you need to combine your last 2 arguments into one object like:
{
arrayFilters: [{'comment._id': req.params.comment_id }],
new: true
}
Final query:
const updatedAnswer = await Answer.findOneAndUpdate(
{ _id: req.params.answer_id },
{
$set: { 'comments.$[comment].text': formattedAnswer },
},
{
arrayFilters: [{'comment._id': req.params.comment_id }],
new: true
}
)
The 4th argument in findOneAndUpdate function takes in a callback function that was where your error was.
Try this
try{
const updatedAnswer = await Answer.findOneAndUpdate(
{ _id: req.params.answer_id },
{
$set: { 'comments.$[comment].text': formattedAnswer },
},
{
arrayFilters: [{'comment._id': req.params.comment_id }],
new: true
}
);
res.json(updatedAnswer);
}catch(err){
//console.log(err)
}
I'm trying to update an object but I´m getting problems to update it. Please check the scheme:
const personSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
teachers: [
{
name: {
type: String,
},
information: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'info',
}],
},
],
I need to update an information inside this scheme Im trying to do (created is the object created):
const person = await ctx.models.person
.findOneAndUpdate({
teachers: {
$elemMatch: {
teachers: {
_id: input.processId,
},
},
},
},
{
$push: {
teachers: {
information: created,
},
},
}, {
new: true,
});
First of all, as recommended here by MongoDb official docs, you don't need $elemMatch:
If you specify only a single condition in the $elemMatch expression, you do not need to use $elemMatch.
Besides that, I recommend use the dot notation to search. Your code will be like that:
const person = await ctx.models.person
.findOneAndUpdate({
"teachers._id" : input.processId
},
{
$push: {
teachers: {
information: created,
},
},
}, {
new: true,
});