Unable to update the data in mongodb - node.js

I need to update some fields
i am using mongoose driver and express js
schema:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ProfilesSchema = new Schema({
presentRound: {
type: Number,
default: 1
},
scheduleInterviewStatus: {
type: Boolean,
default: false
},
interviewStatus: String,
ratings: [{
round: Number,
rating: Number,
feedback: String,
interviewer: String,
roundStatus: String
}]
});
module.exports = mongoose.model('Profiles', ProfilesSchema);
so in these i need to update by id presentRound scheduleInterviewStatus interviewStatus and roundStatus(in ratings array by matching round number)
Before updating:
presentRound: 1,
scheduleInterviewStatus: true,
interviewStatus: "on-hold",
ratings: [{
round: 1,
rating: 3,
feedback: "good communication skills",
interviewer: "Vishal",
roundStatus: "second opinion"
}]
After Updating:
presentRound: 2,
scheduleInterviewStatus: false,
interviewStatus: "in-process",
ratings: [{
round: 1,
rating: 3,
feedback: "good communicationskills",
interviewer: "Vishal",
roundStatus: "selected"
}]
i have tried to run the query in robomongo first but getting error
Error: Fourth argument must be empty when specifying upsert and multi with an object.
Query:
db.getCollection('profiles').update({
"_id": ObjectId("57a9aa24e93864e02d91283c")
}, {
$set: {
"presentRound": 2,
"interviewStatus":"in process",
"scheduleInterviewStatus": false
}
},{
"ratings.$.round": 1
},{
$set: {
"ratings.roundStatus":"selected"
}
},
{ upsert: true },{multi:true})
I have no idea where i am going wrong
Please help.

Your update statement is incorrect, it has misplaced arguments - you are putting multiple $set operations and options as different parameters to the update method; they should be under separate designated update parameters. The correct Node.js syntax is:
update(selector, document, options, callback)
where selector is an object which is the selector/query for the update operation, document is also an object which is the update document and finally an optionsobject which by default is null and has the optional update settings.
Here you are doing
update(selector, document, selector, document, options, options, callback)
In which mongo is updating the collection using the first two parameters as correct and it naturally throws the error
Error: Fourth argument must be empty when specifying upsert and multi
with an object.
because you have too many incorrect parameters specified.
Also, you have incorrect usage of the positional operator. It should be part of the document to be updated, not in the query.
For the correct implementation, follow this update
db.getCollection('profiles').update(
/* selector */
{
"_id": ObjectId("57a9aa24e93864e02d91283c"),
"ratings.round": 1
},
/* update document */
{
"$set": {
"presentRound": 2,
"interviewStatus": "in process",
"scheduleInterviewStatus": false,
"ratings.$.roundStatus": "selected"
}
},
/* optional settings */
{ upsert: true, multi: true }
)

replace {upsert:true} -> {upsert:true,strict: false}

Related

Node JS, Mongoose How to query schema via aggregate?

I am new to using MongoDB and I am trying to update update my document using aggregate $set pipeline. However I have been trying this for a few days and there is still no success. I am trying to update by querying ObjectId and replacing matched result (singular) with the key value pair I specified. I tested the aggregate on Mongo Compass and it works. Does anyone know how to use aggregate for Node JS?
updateOne query I tried
let query = {"_id": ObjectId('theObjectIamUpdating')};
response = await newForm.updateOne(query, payload);
aggregate query I tried
response = await newForm.updateOne([
{$match: {"_id": ObjectId(update_id)}},
{$set: {
"data.velo": [
[1, 2, 3], [5, 7]
]
}}
]);
newForm Mongoose Schema
data: {
date: {
type: Array,
required: false,
trim: true,
},
speed: {
type: Array,
required: false,
trim: true,
},
velo: {
type: Array,
required: false,
trim: true,
}
},
calc: {
date: {
type: Array,
required: false,
trim: true,
},
speed: {
type: Array,
required: false,
trim: true,
},
velo: {
type: Array,
required: false,
trim: true,
}
}
UPDATE
I my updateOne() has succeeded, but my documents are not getting updated.
the result I got after logging response that I awaited
Data received {
acknowledged: true,
modifiedCount: 0,
upsertedId: null,
upsertedCount: 0,
matchedCount: 0
}
POST /api/compute/calculate 200 16 - 79.821 ms
Additionally, this is the MongoDB aggregate that worked when I tried on Mongo Compass
pipeline = $match > $set
**$match**
{
"_id": ObjectId('62e2295060280132dbcee4ae')
}
**$set**
{
"data.velo": [
[1, 2, 3], [5, 7]
]
}
where velo is one of the key value pairs in data, and the set result replaced only the data in data.velo.
As prasad_ mentioned in the comment section, I indeed found some mistakes with regards to my syntax of updateOne().
Correct Syntax for updateOne()
let response = await Form_csv_metadata2.updateOne({ '_id': [update_id] }, [
{//delete this line $match: {"_id": ObjectId(update_id)},
$set: {
"data.velo": [[1, 2, 3], [5, 7]]
}}
]);
As the official document has mentioned the parameters are: Query, UpdateData and Option. I made the mistake as my query was placed in my UpdateData() param (the $match). It should have been a param by itself as a query (query uses same syntax as find()). Another note is that if I were to use a pipeline aggregate, it should have been { $match: {...}, $set: {...} } instead of { {$match: {...}}, {$set: {...}} }

Update Mongoose Array

so basically I have this and I am trying to update the STATUS part of an array.
However, everything I try does nothing. I have tried findOneAndUpdate also. I am trying to identify the specific item in the array by the number then update the status part of that specific array
(Sorry for formatting, I have no idea how to do that on the site yet ...) (Full code can be found here: https://sourceb.in/0811b5f805)
Code
const ticketObj = {
number: placeholderNumber,
userID: message.author.id,
message: m.content,
status: 'unresolved'
}
let tnumber = parseInt(args[1])
let statuss = "In Progress"
await Mail.updateOne({
"number": tnumber
}, { $set: { "status": statuss } })
Schema
const mongoose = require('mongoose')
const mailSchema = new mongoose.Schema({
guildID: { type: String, required: true },
ticketCount: { type: Number, required: true },
tickets: { type: Array, default: [] }
}, { timestamps: true });
module.exports = mongoose.model('Mail', mailSchema)
You need to use something like Mail.updateOne({"guildID": message.guild.id}, {$set: {`tickets.${tnumber}.status`: statuss}})
or for all objects in array:
Mail.updateOne({"guildID": message.guild.id}, {$set: {'tickets.$[].status': statuss}})
Also, you need to create a schema for the tickets, as it is described in docs:
one important reason to use subdocuments is to create a path where there would otherwise not be one to allow for validation over a group of fields

MongoDB - update data in array of objects within object

I have a document in mongoDB structured like that
_id: ObjectId("generatedByMongo"),
name: {
required: true,
type: String,
trim: true
},
last: {
required: true,
type: String,
trim: true
},
grades: [{
grade: {
_id: ObjectId(""),
grade: Number,
date: date
}
}]
And to server I send array of objects containing 3 fields
[
{studentId}, {gradeId}, {newGrade}
]
What I'm trying to accomplish is I want to find in within that user collection grade with given gradeId and update it's value to newGrade. As far as I tried to do that I have done this
router.patch('/students/updateGrade',async(req,res) => {
const studentId = req.body.updateGradeArray[0].studentId;
const gradeId = req.body.updateGradeArray[0].gradeId;
const newGrade = req.body.updateGradeArray[0].newGrade;
try {
const student = await Student.find({_id: studentId})
.select({'grades': {$elemMatch: {_id: gradeId}}});
} catch(e) {
console.log(e);
}
}
);
If you intend to update just grade.grade(the number value), try this:
Student.updateOne(
// Find a document with _id matching the studentId
{ "_id": studentId },
// Update the student grade
{ $set: { "grades.$[selectedGrade].grade": newGrade } },
{ arrayFilters: [{ "selectedGrade._id": gradeId }] },
)
Why this should work:
Since you are trying to update a student document, you should be using one of MongoDB update methods not find. In the query above, I'm using the updateOne method. Inside the updateOne, I am using a combination of $set and $[identifier] update operators to update the student grade.
I hope this helps✌🏾

Mongoose: Model.find() returns an empty array when filtering by boolean

I have the following schema defined in my app (node.js w/ express):
const ArtistSchema = new mongoose.Schema({
name: {type: String, required: true},
year: {type: Number, required: true},
genres: {type: [String], required: true},
imageUrl: {type: String, required: false},
trending: {type: Boolean, required: false, default: false},
trendingDate: {type: Date, required: false}
});
and a route that is supposed to retrieve those entries, who have trending set to true:
// GET trending
router.get('/trending', (req, res) => {
artist.Artist.find({trending: true}).exec((err, trendingArtists) => {
if (err) {
console.error(err);
res.status(500).json({message: err.message});
return;
}
res.json(trendingArtists);
});
});
However, it always returns an empty array when i try to filter by trending field, even though there are items in my collection that have trending set to true. I have tried wrapping everything in single and double quotes and using 1 instead of true, but no query returns results. Filtering by other fields works just fine, not filtering at all returns all entries as expected.
The entries in mongo shell look like this:
> db.artists.find({},{name:1,trending:1}).pretty()
{
"_id" : ObjectId("5de942074a486e2c21246fb9"),
"name" : "Unwound",
"trending" : "true"
}
{
"_id" : ObjectId("5de942074a486e2c21246fba"),
"name" : "Ladytron",
"trending" : "true"
}
{
"_id" : ObjectId("5de942074a486e2c21246fbb"),
"name" : "Depeche Mode",
"trending" : "true"
}
console.loging the results in the app produces this:
[
{
genres: [ 'post-hardcore', 'noise rock', 'indie rock' ],
trending: true,
_id: 5de942074a486e2c21246fb9,
name: 'Unwound',
year: 1991,
imageUrl: '2a7f00a1f8e0ab37c647600f6abff67e.jpg',
trendingDate: 2019-12-20T18:48:53.000Z
},
{
genres: [ 'synthpop', 'electroclash', 'dream pop' ],
trending: true,
_id: 5de942074a486e2c21246fba,
name: 'Ladytron',
year: 1999,
imageUrl: 'f26cc1ae1fef371793622bd199b4bb52.jpg',
trendingDate: 2019-12-20T18:49:05.000Z
},
{
genres: [ 'synthpop', 'new wave' ],
trending: true,
_id: 5de942074a486e2c21246fbb,
name: 'Depeche Mode',
year: 1980,
imageUrl: 'e5328919dac971af86dd034781a2da71.jpg',
trendingDate: 2019-12-20T18:49:43.000Z
}
]
I am at my wits' end. What could cause filtering by a boolean field to break the query regardless of what i specify as the value (i have tried true, 'true', "true", 1, '1', as well as the falsy counterparts)?
edit: i tried a few things since:
1) filtering the results after the query was executed works fine (i.e just writing res.json(trendingArtists.filter(a => a.trending === true));), though it's obviously not the way i would like to deal with filtering my queries
2) the collection i'm querying was created and edited manually, and not via api that my app implements. If i insert a new entry using a POST request via api, that entry will be returned by the query, provided trending was set to true
3) editing existing entries with PATCH requests where i set trending to false and then back to true also works, though it messes up the trendingDate field that is set to current date each time trending is changed to true
4) if the query works for an entry, it works no matter what i put as the value for the filter, as long as it's truthy for mongoose. .find({trending: true}) works just as well as .find({trending: '1'})
I guess the problem is mongoose not recognizing my manually inserted values as truthy at all, even though theoretically they should be cast to true. My problem is solved, I guess? I do not plan to insert values manually in the future, this was just for testing, and those that few that are inserted can be fixed fairly easily, but i don't think it should matter for mongoose whether i edit the entries manually or via Model.save. It seems like a bug - should i close the question and open a bug report on their github?

MongoDB: executionTimeMillis increased when adding index

I am pretty confused to why making the searched fields to be "index" is making the query "theoretically" slower.
I have a not very big collection of items (6240) and all of them have the following structure.
const SomeSchema = new mongoose.Schema({
data: String,
from: {
type: Number,
},
to: {
type: Number,
},
timeStamp: {
type: Date,
default: new Date()
}
})
SomeSchema.set('toJSON', {
getters: true,
transform: (doc, ret) => {
delete ret.from
delete ret.to
return sanitizeSensitiveProperties(ret)
}
})
export const Some = mongoose.model('Some', SomeSchema, 'somethings')
The strange thing came when after trying to improve the query I changed the schema to be
...
from: {
type: Number,
index: true
},
to: {
type: Number,
index: true
},
...
With this schema I run the following query
db.rolls.find({from: {$lte: 1560858984}, to: {$gte: 1560858984}}).explain("executionStats")
This are the results NOTE THAT THE 1st ONE is the one without index
"executionTimeMillis" : 6,
"totalKeysExamined" : 0,
"totalDocsExamined" : 6240,
"executionTimeMillis" : 15,
"totalKeysExamined" : 2895,
"totalDocsExamined" : 2895,
Does this result make any sense, or is just the mongo .explain() function messing around?
As you can see I am using the Mongoose Driver in the version ^5.5.13 and I am using Mongo in the version 4.0.5

Resources