How to update MongoDB createdAt date - node.js

I am trying to update the automatically added createdAt date on a record to right now.
Here the code I am using:
Document.findOneAndUpdate({_id: docId}, {createdAt: new Date()}, function(err, updated) {});
It works on my dev environment, but not the server. It does not return an error, but does not update the record, just returns the unchanged record.
I tried formatting it with new Date().toISOnew Date().toISOString() or .toGMTString() but both still only work on the dev environment. Both have node 6.10, but the server has mongo 3.4.4 and the dev has mongo 3.2.10.
If I add another field to be updated (second arugment), that field gets updated fine, but createdAt remains unchanged.

Automatic createdAt and updatedAt fields are populated by mongoose using the timestamps option as
const schema = new Schema({
// Your schema...
}, {
timestamps: { createdAt: true, updatedAt: false }
})
If you take a look at the source code you'll see that createdAt is excluded from updates. It's fairly easy though to modify your schema accordingly.
const schema = mongoose.Schema({
name: String,
createdAt: { type: Date, default: Date.now }
});
const Test = mongoose.model('test', schema);
const john = await Test.create({name: 'John Doe'});
console.log(john);
const updatedJohn = await Test.findOneAndUpdate({name: 'John Doe'}, { createdAt: Date.now() });
console.log(updatedJohn);

I do not know if it works for update, but for create, I first make sure have the createdAt field abled in my DB and then set the createdAt field like this:
{createdAt: moment().toISOString()}
because createdAt and updatedAt are ISO strings.
Edit: Just like Nigel mentioned, I do use moment library but new Date().toISOString() gives you the same result.

Related

Why wouldn’t mongoDB update the “Date” field of my user entry?

I created a User schema in my React App as follows:
const userSchema = new Schema(
{
profileId: String,
expirationDate: { type: Date, default: new Date() },
credits: { type: Number, default: 0 },
},
{ timestamps: { createdAt: "created_at" } }
);
When the user pays me, I want to reset/update two fields: the expirationDate and credits via a post method. Here’s the code I use on my expressjs backend server to update the database entry on MongoDB Atlas:
req.user.expirationDate = new Date(
req.user.expirationDate.setDate(req.user.expirationDate.getDate() + 30)
);
req.user.credits += 1;
const user = await req.user.save();
res.send(user);
Once the operation succeeded, I can see the field of “credits” gets updated (increased by 1). However, the “expirationDate” field remains unchanged. What’s more curious is that when I send the updated user object to my frontend server with “res.send(user)”, I can see the updated expirationDate in the console.
Successfully updated user model as designed/intended: seen from frontend console
But below is what I saw in my mongoDB:
Updated user entry in MongoDB: the Date field "expirationDate" is not updated; but, the "credits" field is.
What is going on here? How to fix it?
I was having a similar issue recently and haven't figured out the actual reason behind this, but as a workaround try telling mongoose explicitly that the expirationDate-field was changed:
req.user.expirationDate = new Date(
req.user.expirationDate.setDate(req.user.expirationDate.getDate() + 30)
);
req.user.markModified('expirationDate');
await req.user.save();
EDIT:
Just debugged it again and I think the reason behind this behaviour is your default value for expirationDate. Try passing it the Date.now function instead of immediately setting it to a new date:
expirationDate: {type: Date, default: Date.now},
This fixed it for me without having to use markModified().
Although we still have some unsolved issues on why the same set of codes works differently. I have decided not to deal with it for the moment. Here's my solution to the original problem: change the datatype from Date to String. Here's the new set of codes:
Creating User schema at the frontend:
const userSchema = new Schema(
{
profileId: String,
expirationDate: { type: String, default: new Date().toDateString() },
credits: { type: Number, default: 0 },
},
{ timestamps: { createdAt: "created_at" } }
);
Updating user at the backend to MongoDB Atlas::
d = new Date(req.user.expirationDate);
req.user.expirationDate = new Date(
d.setDate(d.getDate() + 30)
).toDateString();
req.user.credits += 1;
const user = await req.user.save();
console.log(typeof req.user.expirationDate);//Checking the datatype of "expirationDate"

How can I set correct timezone for a date field in Mongoose?

I'm using moment for generating time and date:
const moment = require('moment-timezone');
const emailModel = require('./api/models/emails');
sentTime=moment().tz('America/Los_Angeles').format();
console.log(sentTime); //console log shows correct time
emailModel.findOneAndUpdate({ _id: emailInfo._id }, {sentTime: sentTime }, { upsert: true },function (err, doc) {
if (err)
console.log(err);
});
And this is Schema that I'm using mongoose :
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const EmailSchema = new Schema({
.
.
.
sentTime: {
type: Date,
trim: true
}
.
.
.
});
Problem is:
Console log shows correct time 2020-01-07T12:23:00-08:00 BUT mongoose saved incorrect timezone in DB : 2020-01-07T20:23:01.000+00:00
Currently the default behavior of Mongodb is to: (From the docs)
MongoDB stores times in UTC by default, and will convert any local
time representations into this form.
As a solution (and rightly so) what they recommend is:
Applications that must operate or report on some unmodified local time
value may store the time zone alongside the UTC timestamp, and compute
the original local time in their application logic.
Update:
Since you are already using moment-timezone a simple way I would go about this is:
Change the EmailSchema to have a timezone field and create a Mongoose virtual field on that schema to get adjusted time.
const schemaOpts = { toJSON: { virtuals: true } };
const EmailSchema = new Schema(
{
sentTime: {
type: Date,
trim: true
},
timeZone: {
type: String
}
},
schemaOpts
);
EmailSchema.virtual("adjustedTime").get(function() {
return moment.tz(this.sentTime, this.timeZone).format();
});
//fetching data
const result = await EmailSchema.findOne({}).exec();
console.info("result::", result.toJSON());
//note that if using .lean() for performance which has a caveat on using .toJSON()
trick for this case is before save, you need to add time with date. Ex: 2021/01/02 ==> 2021/01/02 15:00:00, ofcouse hour is always equal or greater than 04:00:00. Becase without time, date will be 00:00:00 and mongo will convert it to default timezone and substract hour with 4.

How do I set the updatedAt timestamp using mongoose during a data migration

I am doing a data migration from MS SQL to MongoDB. I am using mongoose and in my schema I set the timestamp property to true.
{
timestamps: true
}
I then try and set the values of the createdAt and updatedAt fields. When inserting a record. The createdAt field saves correctly, however, the updatedAt field is set to whatever the createdAt field is.
Is this the standard behaviour or am I doing something wrong?
The timestamps option is really cool, without doubt, but i'm still doing it "old school":
'use strict';
/**
* Module dependencies
*/
const mongoose = require('mongoose');
var DataSchema = new mongoose.Schema({
name: {
type: String,
required: true,
lowercase: true
},
created: {
type: Date,
default: Date.now
},
updated: {
type: Date,
default: Date.now
}
});
DataSchema.pre('save', function(next) {
this.updated = Date.now();
return next();
});
mongoose.model('Data', DataSchema);
During your migration, when you set the timestamps on your new records, pass the timestamps: false option along with your save() or create().
await newRecord.save({timestamps: false});
This will override the timestamps: true setting on the model for this particular save allowing you to use timestamps from your old data without interference.
From the documentation for this option:
[options.timestamps=true] «Boolean» if false and timestamps are enabled, skip timestamps for this save().

how to change date timezone in mongoose?

In model schema,
Using
updated: {
type: Date,
default: Date.now
In server.js
put(function(req, res) {
var query = {name: req.params.name};
// use our bear model to find the bear we want
Domain.find(query, function(err, domains) {
if (err)
res.send(err);
var domain = domains[0];
domain.password = req.body.password; // update the bears info
domain.updated = new Date();
// save the bear
domain.save(function(err, data) {
if (err)
res.send(err);
res.json({ status: 'success', message: 'domain updated!' }, data);
});
});
});
However,
In db side it shows,
"updated": "2016-02-27T16:20:42.941Z"
But, my timezone is UTC+02.00
So it should be like 18:20:42
What I'm doing wrong?
I'm using moment-timezone
npm install moment-timezone
const moment = require('moment-timezone');
const dateThailand = moment.tz(Date.now(), "Asia/Bangkok");
console.log(dateThailand); // "2018-08-20T16:35:14.033+07:00"
*** Asia/Bangkok +07:00
Schema in the mongoose.
const categorySchema = new Schema(
{
_id: {type: mongoose.Schema.Types.ObjectId, auto: true},
c_name: String,
created_by: String,
created_date: {type: Date, default: dateThailand},
updated_by: String,
updated_date: {type: Date, default: dateThailand}
}, {_id: false}
);
See up that created_date, updated_date: {type: Date, default: dateThailand }
Read more: http://momentjs.com/timezone/docs/
*If you using Robo 3T tool.
You can set "Display Dates In..."
Options > Display Dates In... > Local Timezone
:) Work for me.
The timestamps are timezone agnostic, stored as a unix timestamp. This timestamp will work across timezones, and node interprets it using current timezone of the server. The date you've shown is correctly stored. As soon as you'll retrieve it, if your server's timezone is UTC+2, it will show you correct time.
There is nothing wrong in your code. MongoDb saves date in UTC format no matter in whichever timezone you try to insert your date.
If you log domain.updated before saving in DB, result will be UTC+2 (your local time)
If you see updated column in DB, result will be in UTC
If you fetch updated column value from DB, then again result will be in UTC+2 (your local time)
I changed this,
var utc = new Date();
utc.setHours( utc.getHours() + 2);
domain.updated = utc;
Now it works.
You can create a Date Object from a specific UTC time:
new Date(Date.UTC(year, month, day, hour, minute, second))
Remember that no matter what you use to set time in mongoose schema, mongoose will always use UTC time, hence you need to dynamically allocate the UTC timestamp inside the Schema. Here it goes :-
var current = new Date();
const timeStamp = new Date(Date.UTC(current.getFullYear(),
current.getMonth(),current.getDate(),current.getHours(),
current.getMinutes(),current.getSeconds(), current.getMilliseconds()));
//Here goes your schema
const auditSchema = mongoose.Schema({
dateTime : { type: Date, default : timeStamp }
})
Using moment.js it is as easy as:
var moment = require('moment');
var utcDate = moment.utc().toDate();
Enjoy!

How do I update a property with the current date in a Mongoose schema on every save?

In my database collections, I want to update a 'lastChanged' field every time the record is updated with the current datetime. I want it to be in the same format as mongoose's default date like:
ISODate("2011-10-06T14: 01: 31.106Z")
Any words of wisdom?
If you just want an ISO String use:
new Date().toISOString()
One way of accomplishing this is to use Mongoose Middleware and update the field pre-save.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
//schema
var SomethingSchema = new Schema({
text: {type: String},
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now}
});
//middle ware in serial
SomethingSchema.pre('save', function preSave(next){
var something = this;
something.updatedAt(Date.now());
next();
});
It seems, however, that the middleware is not always invoked:
Notes on findAndUpdate()
pre and post are not called for update operations executed directly on the database, including Model.update,.findByIdAndUpdate,.findOneAndUpdate, .findOneAndRemove,and .findByIdAndRemove.order to utilize pre or post middleware, you should find() the document, and call the init, validate, save, or remove functions on the document. See explanation.
Update: See this question "add created_at and updated_at fields to mongoose schemas"
In a few days Mongo is going to announce new 2.6 version (currently you can download experimental 2.5.x version). Among many other features you can use $currentDate which is going to do exactly the thing you want:
db.users.update(
<criteria>,
{
$currentDate: { yourField: true},
}
)
The middleware function is a good approach, however, it should be
SomethingSchema.pre('save', function preSave(next){
var something = this;
something.updatedAt = Date.now();
next();
});
Since something.updateAt is not a function.
I added updated: new Date to fix a similar problem. Here is how I used it.
update: (req, res) => {
let userId = req.params.id;
let userParams = {
name: {
first: req.body.first,
last: req.body.last
},
email: req.body.email,
password: req.body.password,
updated: new Date
};
db.User.findOneAndUpdate(userId, { $set: userParams })
.then(upUser => res.json(`Profile Updated for: ${upUser.fullName}`))
.catch(err => res.status(422).json(err));
}

Resources