Reputation: 5046
I'm using Mongoose's timestamps
schema option to make a createdAt
and updatedAt
property on a user schema (calling them "applicants"). The only reason I need this is to figure out how long it takes a user to get through the signup process. When they click the "Let's get started" button, it creates the user on the backend and sets the createdAt
and updatedAt
properties (at this point, they are equal). Then when they finish the signup process, I want to send another call to the db and force the document to save again, thus updating the updatedAt
property. Once that occurs, I want to find the time difference between the two timestamp properties to see how long it took them to finish the process, then save that value to a timeTaken
property.
Problem is, the only way I'm seeing to do this is to trigger the save, then in the promise that's returned, save the document again after a "post save" hook runs to update the applicant's document.
Probably will make more sense with the code:
var applicantSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
lowercase: true
},
timeTaken: String
}, {timestamps: true});
applicantSchema.post("save", function () {
this.setTimeTaken();
});
applicantSchema.methods.setTimeTaken = function () {
var applicant = this;
var ms = this.updatedAt - this.createdAt;
var x = ms / 1000;
var seconds = Math.floor(x % 60);
x /= 60;
var minutes = Math.floor(x % 60);
x /= 60;
var hours = Math.floor(x % 24);
applicant.timeTaken = hours + "h:" + minutes + "m:" + seconds + "s";
};
applicantRouter.put("/:applicantId", function (req, res) {
Applicant.findById(req.params.applicantId, function (err, applicant) {
applicant.save().then(function () {
applicant.save(function (err, applicant) {
//console.log(applicant.timeTaken);
if (err) {
res.status(500).send(err)
} else {
res.send({success: true, timeTaken: applicant.timeTaken})
}
});
});
});
});
PUT
request comes in, mongoose searches mongodb for the document with the given id, then triggers a "save" event. It's caught by the schema's "post save" hook, which calls the document's setTimeTaken
method. This method updates the property on the document, but since this happens post save, I need to save it again to get it to persist.
I can't use a pre-save hook, because it won't have updated the updatedAt
property yet.
The code above actually works, but I kinda hate myself for writing it this way. I feel like an infomercial actor: "There's got to be a better way!"
Or maybe there isn't? Someone who knows this stuff better ? Thanks in advance
Upvotes: 4
Views: 9280
Reputation: 5046
After stepping away from the computer for a few hours I realized I was adding too much complexity and trying too hard to use the built-in timestamps
option. I just changed my math to include Date.now() - this.createdAt
, changed the post save
to a pre save
, and got rid of the extra applicant.save()
:
var applicantSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
lowercase: true
},
timeTaken: String
}, {timestamps: true});
applicantSchema.pre("save", function (next) {
this.setTimeTaken();
next();
});
applicantSchema.methods.setTimeTaken = function () {
var applicant = this;
var ms = Date.now() - this.createdAt;
var x = ms / 1000;
var seconds = Math.floor(x % 60);
x /= 60;
var minutes = Math.floor(x % 60);
x /= 60;
var hours = Math.floor(x % 24);
applicant.timeTaken = hours + "h:" + minutes + "m:" + seconds + "s";
};
applicantRouter.put("/:applicantId", function (req, res) {
Applicant.findById(req.params.applicantId, function (err, applicant) {
applicant.save(function (err, applicant) {
if (err) {
res.status(500).send(err)
} else {
res.send({success: true, timeTaken: applicant.timeTaken})
}
});
});
});
Works like a charm.
Upvotes: 2