Unable to update record in MongoDB using findOneAndUpdate - javascript

I am using Mongoose to update an Announcement record, with the following definition:
const { DateTime } = require('luxon');
const Schema = new mongoose.Schema({
title: String,
description: String,
date: {
type: Date,
set: (dt) => dt.toJSDate(),
get: (d) => DateTime.fromJSDate(d),
},
club: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Club',
},
});
I am performing the update operation in this function
exports.update = async (id, params) => {
console.log(params)
await Announcement.findOneAndUpdate({ _id: id }, params, {
upsert: true,
useFindAndModify: false,
});
return "exports.get(id)";
};
However, I get an error when running Announcement.findOneAndUpdate:
$set' is empty. You must specify a field like so: {$set: {<field>: ...}}
This is what the params look like:
{
title: 'Name!',
description: 'Description!',
date: DateTime {
ts: 1601524800000,
_zone: LocalZone {},
loc: Locale {
locale: 'en-US',
numberingSystem: null,
outputCalendar: null,
intl: 'en-US',
weekdaysCache: [Object],
monthsCache: [Object],
meridiemCache: null,
eraCache: {},
specifiedLocale: null,
fastNumbersCached: null
},
invalid: null,
weekData: null,
c: {
year: 2020,
month: 10,
day: 1,
hour: 0,
minute: 0,
second: 0,
millisecond: 0
},
o: -240,
isLuxonDateTime: true
},
club: '99cb91bdc3464f14678934ca'
}
I know that all the ObjectId's are valid because I have tested them from Mongo shell. However, I can't figure out why I am not able to run findOneAndUpdate on an Announcement record.

Related

How to filter by dayOfWeek in mongodb node.js

I'm new at this, I need to get all the persons from the db that matches the day of the week of the attribute createdAt (a Date) and a day of the week obtained through a parameter in the request. My data model is:
const personSchema = new mongoose.Schema({
firstName: {
required: true,
type: String
},
lastName: {
required: true,
type: String
},
age: {
required: true,
type: Number
},
email: {
type: String
},
createdAt: {
type: Date,
default: Date.now
}
})
I'm working with MongoDB and Node.js
I've tried this:
const people = await Person.aggregate(
[{
$addFields: {
dayyOfWeek: {
$dayOfWeek: "$createdAt"
}
}
}, {
$match: {
dayyOfWeek: {
$eq: req.params.weekday
}
}
}]
)
and tried too with $where and $function in the find function, but it goes wrong because that gives me an error, "MongoError: $where is not allowed in this atlas tier"

Mongoose `findById` not returning expected document

When I find a document using the following line:
const user = await User.findById(req.user.id).populate('input')
And then I console.log user, I can see the document that fulfills the query.
However, when I add .updateOne({ formResponded: '1' }); to the previous line like so:
const user = await User.findById(req.user.id).populate('input').updateOne({ formResponded: '1'});
and I console.log user I do not get any record but instead an object that looks like this:
{ n: 1,
nModified: 1,
opTime: {
ts: Timestamp { _bsontype: 'Timestamp', low_: 2, high_: 1635212112 },
t: 41
},
electionId: 7fffffff0000000000000029,
ok: 1,
'$clusterTime': {
clusterTime: Timestamp { _bsontype: 'Timestamp', low_: 2, high_: 1635212112 },
signature: { hash: [Binary], keyId: [Long] }
},
operationTime: Timestamp { _bsontype: 'Timestamp', low_: 2, high_: 1635212112 }
}
Why does this happen?
Fixed:
const user = await User.findByIdAndUpdate({ _id: req.user.id }, { formResponded: '1' }).populate('input')

Dynamically push, pull, and set on mongoose schema update

I am trying to setup my patch api so that I can create a dynamic query to push, pull, and set data in my mongoose schema. I have plenty of values that I would change using set, but I also have an array of objects which would require me to call push when I need to insert and pull when I need to remove an item. I'm trying to find the best way to combine this into a dynamic structure.
Schema:
const StepSchema = new Schema({
position: {
type: Number,
required: true
},
name: {
type: String,
required: true
},
due_date: {
type: Date
},
status: [{
label: {
type: String,
enum: ['Inactive', 'In Progress', 'Flagged', 'Complete'],
default: 'Inactive'
},
user: {
type: Schema.Types.ObjectId,
ref: 'users',
},
date: {
type: Date
}
}],
comments: [{
user: {
type: Schema.Types.ObjectId,
ref: 'users',
required: true
},
body: {
type: String,
required: true
},
date: {
type: Date,
required: true
},
}],
});
Api:
router.patch('/',
async (req, res) => {
let setQuery = req.body;
let pushQuery = {};
let pullQuery = {};
//remove id from set query
delete setQuery.id;
//if there is a comment
if(req.body.comment){
pushQuery.comments = req.body.comment
}
//if I need to remove a comment
if(req.body.remove_comment){
pullQuery.comments = {_id: req.body.remove_comment.id}
}
//Push new status into array
if(req.body.status) {
pushQuery.status = {
label: req.body.status,
user: req.user._id,
date: new Date()
};
delete setQuery.status;
}
//update step
await Step.findByIdAndUpdate(req.body.id, {$set: setQuery, $push: pushQuery, $pull: pushQuery})
.then(step => {
if(!step){
errors.noflow = "There was a problem updating the step";
return res.status(400).json(errors);
}
res.json(step)
})
.catch(err => {
console.log(err);
res.status(404).json(err);
});
});
I've been getting the following error when trying to push a new status into my document:
operationTime: Timestamp { bsontype: 'Timestamp', low: 1, high_:
1560978288 }, ok: 0, errmsg: 'Updating the path \'status\' would
create a conflict at \'status\'', code: 40, codeName:
'ConflictingUpdateOperators', '$clusterTime': { clusterTime:
Timestamp { bsontype: 'Timestamp', low: 1, high_: 1560978288 },
signature: { hash: [Object], keyId: [Object] } },
Oh, you're doing that $set and $push on a status. Your pushQuery is trying to have status be an array on the document, and your setQuery wants to set it to whatever it was on the actual body (I'm guessing the same object.
A quickfix would be to remove it from the set object:
delete setQuery.status
A reasonable and stable way to do this would be to actually only take the things from req.body which you really want for each of the stages. Example:
const { position, name, dueDate, status, comment, remove_comment } = req.body;
const setQuery = { position, name, dueDate };
const pushQuery = { status, comments: comment };
// ...
That way your queries are not conflicting in any way.

How to store history of the documents in Mongoose/MongoDB?

I have the following Schema -
const leadSchema = new Schema(
{
emails: [{ type: Email, default: null }],
name: { type: String },
country: { type: String },
city: { type: String, index: true },
source: {
type: Number,
min: 1,
max: leadConfig.sources.length,
required: true
},
course: { type: Schema.Types.ObjectId, ref: 'courses',required: true},
gender: { type: String, enum: leadConfig.gender },
status: {type: Schema.Types.ObjectId, ref: 'status' },
dob: Date,
parent_name: String,
counselor: { type: Schema.Types.ObjectId, ref: 'users', default: null },
consultant_amount: { type: Number, min: 0, default: 0 },
consultant_amount_paid: { type: Number, min: 0, default: 0 },
loan: { type: Boolean, default: false },
reported: { type: Boolean, default: false },
scholarship: { type: Number, default: 0 },
student_id: { type: Number, default: null },
next_interection_deadline: { type: Date, default: null },
session: { type: Schema.Types.ObjectId, ref: 'session' }
},
{ timestamps: true }
);
module.exports = mongoose.model('leads', leadSchema);
I want to store the update history of all the documents of this collection.
For Example -
If I change the name field of a lead from 'John' to 'Jane' then a record should be saved in a history table with the following schema -
{
_id:(ObjectId),
collectionName:"lead"
column_name:"name"
oldValue - 'John',
newValue - 'Jane'
updateAt - Date()
}
I googled some plugins like mongoose-diff-history and it serves the purpose well but the only drawback was that it only worked with .save() method and not with mongodb updates methods.
I have been working on this problem for so many days but couldn't find a correct and efficient solution. Any solutions to this problem will be very much appreciated.
Have you looked into the midldeware hooks? Usually what you want could be handled there. For example look into Mongoose hooks: http://mongoosejs.com/docs/middleware.html
You have basically "events" which allow you do intercept records just before "save" etc and do something (like in your case store/log somewhere).
Here is an example from their docs:
var schema = new Schema(..);
schema.pre('save', function(next) {
// do stuff
next();
})
Here is one for the 'update':
schema.pre('update', function() {
this.update({},{ $set: { updatedAt: new Date() } });
});

Mongoose/Mongodb: Index Already Exists With Different Options

I am using Mongoose 5.1.7 and attempting to create a compound index across multiple text indexes in my defined schema. Here is my schema definition:
const mongoose = require('mongoose');
const alumniSchema = mongoose.Schema({
firstName: {
type: [String],
required: true
},
lastName: {
type: [String],
required: true
},
classYear: {
type: Number,
required: true
},
photoURL: {
type: String,
},
education: [
{
school: {
type: String,
required: true
},
gradYear: {
type: Number,
required: true
},
degreeType: String,
degreeSubject: String,
}
],
jobs: [
{
employer: {
type: String,
required: true
},
position: String,
startDate: Date,
endDate: Date,
isCurrent: Boolean
}
],
contactInfo: {
phoneNumber: {
type: String,
},
email: {
type: String,
}
},
})
alumniSchema.index({ firstName: 'text', lastName : 'text', email: 'text' });
module.exports = mongoose.model('Alumni', alumniSchema);
When I boot up the server, I receive the following error:
MongoError: Index: { v: 2, key: { _fts: "text", _ftsx: 1 }, name: "firstName_text_lastName_text_email_text", ns: "5b3be578c0c6e317f7c1bc2b_test.alumnis", background: true, weights: { email: 1, firstName: 1, lastName: 1 }, default_language: "english", language_override: "language", textIndexVersion: 3 } already exists with different options: { v: 2, key: { _fts: "text", _ftsx: 1 }, name: "firstName_text_lastName_text_classYear_text_education.school_text", background: true, weights: { classYear: 1, education.school: 1, firstName: 1, lastName: 1 }, default_language: "english", language_override: "language", ns: "5b3be578c0c6e317f7c1bc2b_test.alumnis", textIndexVersion: 3 }
I have been messing around with this for a while and evidently previously created an index. When I use the mongo shell to check the indexes that I currently have set up, however, I cannot find the index "firstName_text_lastName_text_classYear_text_education.school_text" referenced by the error message:
> db
test
> db.collection.getIndexes()
[ ]
I am at an impasse--I'm not sure if I've incorrectly created the index, or if I am supposed to drop the index (it doesn't look like Mongoose natively supports a dropIndex() function).
Has anyone else dealt with this issue? Thanks!
Looks like Mongoose dynamically creates the index at runtime. The trick for me was adding:
var Alumni = mongoose.model('Alumni', alumniSchema);
Alumni.collection.dropIndex('firstName_text_lastName_text_classYear_text_education.school_text', function(err, result) {
if (err) {
console.log('Error dropping index!', err);
}
});
and then restarting the server.
THEN I was able to change the index to whatever I wanted. Note that I still needed to add the above code segment and restart the server every time I wanted to update the index.

Categories