$inc has no effect on the document (does not work at all) [duplicate] - mongodb

This question already has answers here:
Update field in exact element array in MongoDB
(5 answers)
Closed 4 years ago.
I've got a simple poll trying to update the vote counter every time someone upvte it.
I tried using $inc but it has not effect. therefore it does return which supposed to be the updated poll/ poll after the vote counter is updated, but it just returns the same one without increasing anything at all.
What am i doing wrong?
app.patch('/voting/:id', (req, res) => {
let userVote = req.body.votes;
Poll_Schema_Model.findByIdAndUpdate({ "_id": '5b070f512a28d70eb0abaa51' }, { $inc: { "poll[0].votes":userVote } }, { new: true }, (err, newPoll) => {
res.status(200).send(newPoll);
})
.catch(() => {
res.status(400).send();
});
});
the newPoll results in :- (note that votes is defaulted to 0)
{
"_id": "5b070f512a28d70eb0abaa51",
"_creator": "5b04aba0ee81bb26182b2267",
"poll": [
{
"votes": 0,
"_id": "5b070f512a28d70eb0abaa52",
"option1": "FIRST OPTIONNN",
"option2": "SECOND OPTIONN"
}
],
"__v": 0
}
My schema :-
const Poll_Schema = new mongoose.Schema({
_creator: {
type: mongoose.Schema.Types.ObjectId
},
poll: [{
option1: {
type: String,
maxlength: 20,
minlength: 3
},
option2: {
type: String,
maxlength: 20,
minlength: 3
},
votes: {
type: Number,
minlength: 1,
default:0
}
}]
});

The syntax for referencing array item is different, you should specify position after dot like poll.0.votes instead of [0]. So your code should look like this:
app.patch('/voting/:id', (req, res) => {
let userVote = req.body.votes;
Poll_Schema_Model.findByIdAndUpdate({ "_id": '5b070f512a28d70eb0abaa51' }, { $inc: { "poll.0.votes":userVote } }, { new: true }, (err, newPoll) => {
res.status(200).send(newPoll);
})
.catch(() => {
res.status(400).send();
});
});

Related

Editing specific element of an array of a particular collection - mongoDb

I am new to mongoDb. I have created a collection named task that has comments field which is array along with other fields. I need to edit specific comment of the task. There is a edit button in each comment. Both task id and comment id are available. Now how to edit specific comment of the task?
Thanks in advance
task api
{
"status":true,
"task":[
{
"_id":"61dfef323a6ee474c4eba926",
"description":"hello there",
"title":"hello",
"comments":[
{
"comment_id":1,
"username":"test",
"comment":"abcd",
"status":true,
},
{
"comment_id":2,
"username":"test",
"comment":"abcdsdfsdf",
"status":true,
}
],
"createdAt":"2022-01-13T09:21:54.795Z",
"updatedAt":"2022-01-13T09:21:54.795Z",
"__v":0
}
]
}
Task model schema
const taskSchema = new Schema({
title: { type: String, required: true },
description: { type: String, required: true },
comments: [Object],
}, {
timestamps: true,
});
I tried using $set but I don't know how to use it in the inner array.
router.route('./comments/edit').post((req, res) => {
const commentId = req.body.commentId;
const taskId = req.body.postId;
const comment = req.body.editedComment;
const updatedAt = new Date();
Task.updateOne(
{ _id: taskId},
{
//what to do here?
// $set: { comments: [ {'comment_id': commentId} ]},
}
)
.then((response) => res.json({ status: true, msg: 'Comment Edited!' }))
.catch(err => res.json({ status: false, msg: err }));
});
Thanks in advance.
This is how to do best:
db.collection.update({
status: true
},
{
$set: {
"task.$[x].comments.$[y].username": "New Name"
}
},
{
arrayFilters: [
{
"x._id": "61dfef323a6ee474c4eba926"
},
{
"y.comment_id": 2
}
]
})
Explained:
Define x and y as arrayFIlters in the update statement.
In the $set statement provide the x & y filters to identify the specific comment for update.
In the example I update the username , but you can update any other value from the targeted array subelement addressed by x & y.
playground
And here is how to update two values at same time in the same nested array element.

Update using positional operator ($) in mongoose

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.

Mongoose update only fields available in request body

I am trying to update one document using findOneAndUpdate and $set but I clearly missing something very crucial here because the new request is overwriting old values.
My Device schema looks like this:
{
deviceId: {
type: String,
immutable: true,
required: true,
},
version: {
type: String,
required: true,
},
deviceStatus: {
sensors: [
{
sensorId: {
type: String,
enum: ['value1', 'value2', 'value3'],
},
status: { type: Number, min: -1, max: 2 },
},
],
},
}
And I am trying to update the document using this piece of code:
const deviceId = req.params.deviceId;
Device.findOneAndUpdate(
{ deviceId },
{ $set: req.body },
{},
(err, docs) => {
if (err) {
res.send(err);
} else {
res.send({ success: true });
}
}
);
And when I try to send a request from the postman with the body that contains one or multiple sensors, only the last request is saved in the database.
{
"deviceStatus": {
"sensors": [
{
"sensorId": "test",
"status": 1
}
]
}
}
I would like to be able to update values that are already in the database based on req.body or add new ones if needed. Any help will be appreciated.
The documentation said:
The $set operator replaces the value of a field with the specified
value.
You need the $push operator, it appends a specified value to an array.
Having this documents:
[
{
_id: 1,
"array": [
2,
4,
6
]
},
{
_id: 2,
"array": [
1,
3,
5
]
}
]
Using $set operator:
db.collection.update({
_id: 1
},
{
$set: {
array: 10
}
})
Result:
{
"_id": 1,
"array": 10
}
Using $push operator:
db.collection.update({
_id: 1
},
{
$push: {
array: 10
}
})
Result:
{
"_id": 1,
"array": [
2,
4,
6,
10
]
}
you want to using $push and $set in one findOneAndUpdate, that's impossible, I prefer use findById() and process and save() ,so just try
let result = await Device.findById(deviceId )
//implementation business logic on result
await result.save()
If you want to push new sensors every time you make request then update your code as shown below:
const deviceId = req.params.deviceId;
Device.findOneAndUpdate(
{ deviceId },
{
$push: {
"deviceStatus.sensors": { $each: req.body.sensors }
}
},
{},
(err, docs) => {
if (err) {
res.send(err);
} else {
res.send({ success: true });
}
}
);
Update to the old answer:
If you want to update sensors every time you make request then update your code as shown below:
const deviceId = req.params.deviceId;
Device.findOneAndUpdate(
{ "deviceId": deviceId },
{ "deviceStatus": req.body.sensors },
{ upsert: true },
(err, docs) => {
if (err) {
res.send(err);
} else {
res.send({ success: true });
}
}
);

MongoDB: How to $push an array consisting of objects having arrays, to an array

Heres what my mongoose schema looks like-
const GameLogSchema = new Schema({
_score: Number,
_playerData: {
x: Number,
y: Number,
},
_zoneData: [{ _x: Number, _height: Number }],
_pipeData: [{ _x: Number, _randomeHeightTop: Number }],
_gap: Number,
});
const PlayerSchema = new Schema({
/* other fields */
_gameLogs: {
type: [[GameLogSchema]],
},
});
This is what the data its supposed to deal with looks like -
Spreading one of those objects -
How to push an array of objects having arrays, into an array ? preferably in nodejs mongoose.
EDIT -
I tried to do something similar to whats given in Mongodb $push in nested array
I tried this with my schema -
const PlayerSchema = new Schema({
/* other fields */
_gameLogs: [
{
_logs: [{ _log: { _score: Number } }],
},
],
});
And heres my update function -
Player.findOneAndUpdate(
{ \* filter *\ },
{
$push: {
/* pushing something other, that works */
_gameLogs: {
'_gameLogs.$._logs': {
'_logs.$._log': { '_log.$._score': req.body.gameLogs.score },
},
},
},
$inc: { _totalGamesPlayed: 1, '_gameStreak._gamesPlayed': 1 },
},
{ safe: true, upsert: true, new: true, minimize: false },
(err, result) => {
console.log(result);
}
);
It outputs a list of { _logs: [], _id: 5f4f5979fba2d03c40d4aed7 }, among other things.

Mongodb Increment value inside nested array not working

I'm building a app where you can vote on user submitted polls. Here's the schema for my polls:
const pollSchema = new Schema({
userID: String,
pollQuestion: String,
pollOptions: [
{
name: String,
quantity: Number
}
]
})
This is the express route that handles incrementing the quantity of a poll option:
app.put('/vote', (req, res) => {
Poll.update(
{
id: req.body.id,
'pollOptions.name': req.body.selection
},
{ $inc: { 'pollOptions.$.quantity': 1 } }
).then(updatedPoll => {
console.log('poll', updatedPoll)
res.send(updatedPoll)
})
})
here req.body.selection is the option that the user has chosen to vote for.
I basically want the quantity of the user's chosen option to increment by one. This way of updating the database I derived from the answer to this question
When I send a request to this route I get some very unexpected behaviour. For one thing, the database doesn't get updated or changed in any way. The second thing is the updatedPoll is a really bizarre object. Here's what i get from the console.log:
poll { n: 0,
nModified: 0,
opTime:
{ ts: Timestamp { _bsontype: 'Timestamp', low_: 1, high_: 1510783687 },
t: 1 },
electionId: 7fffffff0000000000000001,
ok: 1 }
The one thing I've tried is include { new: true } as a third parameter after the object with the $inc operator. I doesn't change anything. Can someone please help with this issue?
Thanks to Neil Lunn in the comments, changing my route to the following worked for me:
poll.put('/vote', (req, res) => {
Poll.findOneAndUpdate(
{
_id: req.body.id,
pollOptions: {
$elemMatch: { name: req.body.selection }
}
},
{ $inc: { 'pollOptions.$.quantity': 1 } },
{ new: true }
).then(poll => {
res.send(poll)
})
})