Mongoose timestamp gives inacurate results - javascript

CreatedAt gives the time 3 hours ago. How can i make it display the current time?
The schema:
import mongoose from 'mongoose';
var articleSchema = new mongoose.Schema({
title: String,
text: String,
link: String,
description: String,
imgLink: String,
source: String,
} , { timestamps: true }
);
articleSchema.index({ createdAt: 1, expires: '60m', default: Date.now });
var Article = mongoose.model("Article", articleSchema);
export default Article;

You probably getting different time zone from mongoose.
Try installing npm i moment-timezone
And change your Date to the right timezone, here is the docs for that: https://momentjs.com/timezone/docs/#/using-timezones/parsing-in-zone/

Related

Mongoose and Next.js: Unhandled Runtime Error TypeError: Cannot read properties of undefined (reading 'Token')

I basically defined this Model, much like another which doesn't error out; So I am stumped as to why it's not working...
Here is a Minimal, Reproducible Example
Not working:
import mongoose from 'mongoose';
const TokenSchema = new mongoose.Schema({
_userId: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
token: { type: String, required: true },
createdAt: { type: Date, required: true, default: Date.now, expires: 43200 }
});
export default mongoose.models.Token || mongoose.model('Token', TokenSchema);
Working:
import mongoose from 'mongoose';
import emailValidator from 'email-validator'
import bcrypt from 'bcrypt'
import crypto from 'crypto'
const SALT_ROUNDS = 12;
const UserSchema = new mongoose.Schema(
{
username: {
type: String,
required: true,
trim: true,
lowercase: true,
index: { unique: true },
validate: {
validator: emailValidator.validate,
message: props => `${props.value} is not a valid email address!`
}
},
password: {
type: String,
required: true,
trim: true,
index: { unique: true },
minlength: 7,
maxlength: 11
},
roles: [{ type: 'String' }],
isVerified: { type: Boolean, default: false },
passwordResetToken: String,
resetPasswordExpires: Date
},
{
timestamps: true
}
);
UserSchema.pre('save', async function preSave(next) {
const user = this;
if (!user.isModified('password')) return next();
try {
const hash = await bcrypt.hash(user.password, SALT_ROUNDS);
user.password = hash;
return next();
} catch (err) {
return next(err);
}
});
UserSchema.methods.generatePasswordReset = function () {
this.resetPasswordToken = crypto
.randomBytes(20)
.toString('hex');
this.resetPasswordExpires = Date.now() + 3600000; // expires in an hour
};
UserSchema.methods.comparePassword = async function comparePassword(candidate) {
return bcrypt.compare(candidate, this.password);
};
export default mongoose.models.User || mongoose.model('User', UserSchema)
Also I'm following this example in the Next.js Examples repo.
Please help! :)
Apparently the TypeError: Cannot read properties of undefined (reading 'Token') is happening because the code is executing on the client side when it is intended to execute on the server side.
What is really happening is that mongoose has not initiated a connection to the database, and so mongoose.models is undefined. But the fix isn't to try to initiate a db connection:
I too was having the a similar issue, when I tried to define too many things in the same file, that is... In my case I'm trying to define Typescript interfaces that can be pulled into client-side code, but the mongoose model definitions end up executing as well...
I read that NextJS does a fair bit of work to split up code that gets sent to client-side and what stays on server-side... The developer should keep this in mind and try to split things that relate to client-side and server-side into different files.
In my case, I placed the Interface definitions and my custom Hooks in a different file from the Mongoose Schema definitions; and then imported only the Interfaces and Hooks when I need those, which made the errors go away.
Trying to keep everything in the same place sounds logical and neat, but doesn't work in this case.
I copied your code and it worked fine (went into the tokens collections versus token like expected possibly) one thing I noticed was the expires field on createdAt - was this a NextJS field? It's not a default field so just curious. Also, can you paste the exact error you are encountering, this will help someone track the issue down.
{
_userId: new ObjectId("5e1a0651741b255ddda996c4"),
token: 'abcd123',
createdAt: 2021-09-24T23:10:24.288Z,
_id: new ObjectId("614e5ae04c741f91ac062530"),
__v: 0
}
Also, consider using the timestamps options property when declaring the model as this will save you the headache of setting createdAt up (and you can also have updatedAt automatically update).
token: { type: String, required: true },
},
{
timestamps: true
}
);
I had the same error. Even though my schema file was correct. the issue was for some reason, I have been importing the model in react component file
import room from "../models/room";

How to store a datetime using mongoose?

I made this to store data of some meetings but I want the field "date" to store values as datetime like in a mysql base. How can I do this?
const mongoose= require('mongoose')
const Schema= mongoose.Schema
const Meets= mongoose.model('Meet', new Schema({
id1: String,
id2: String,
date: Date
}))
module.exports= Users
Your schema should be in below format for store Meeting time in DateTime format.
const mongoose= require('mongoose')
const Schema= mongoose.Schema
const Meets= mongoose.model('Meet', new Schema({
// First meeting recipient
id1: { type: Schema.Types.ObjectId, required: true },
// Second meeting recipient
id2: { type: Schema.Types.ObjectId, required: true },
// Date and Time of meeting
date: { type: Date, required: true }
}))
module.exports= Meets
Now, When you will make an entry in Meets collection, You will find below format of date key in your mongo database:
ISODate("2017-04-02T15:30:00Z")
This represents that the above is in ISO Date format which gives time into UTC format. To convert it in your local time format, you can do below step:
new Date(meet.date).toLocaleString();
Also, you can set property {timestamps:true} in your schema, which will additionally add createdAt and updatedAt field in your collection, which will automatically get updated at creation and each update.
Thanks
What kind of date format you want to store? If you want it to be stored in milliseconds, then you can set timestamps option to true like this:
const Meets= mongoose.model('Meet', new Schema({
id1: {String},
id2: {String},
}, {timestamps:true}))

Getting MongoDb document to expire at a certain time using mongoose [duplicate]

Below is the command that can be used via the mongo terminal to set an expiry time for collections (a TTL):
db.log.events.ensureIndex( { "status": 1 }, { expireAfterSeconds: 3600 } )
How do I do this from my code in Node.js using mongoose?
In Mongoose, you create a TTL index on a Date field via the expires property in the schema definition of that field:
// expire docs 3600 seconds after createdAt
new Schema({ createdAt: { type: Date, expires: 3600 }});
Note that:
MongoDB's data expiration task runs once a minute, so an expired doc might persist up to a minute past its expiration.
This feature requires MongoDB 2.2 or later.
It's up to you to set createdAt to the current time when creating docs, or add a default to do it for you as suggested here.
{ createdAt: { type: Date, expires: 3600, default: Date.now }}
this code is working for me.
may it help
let currentSchema = mongoose.Schema({
id: String,
name: String,
packageId: Number,
age: Number
}, {timestamps: true});
currentSchema.index({createdAt: 1},{expireAfterSeconds: 3600});
Providing a string to expires also works nicely with Mongoose if you do not want to deal with the expire time calculation and improve the overall readability of the schema.
For example here we are setting the expires to 2m (2 minutes) and mongoose would convert to 120 seconds for us:
var TestSchema = new mongoose.Schema({
name: String,
createdAt: { type: Date, expires: '2m', default: Date.now }
});
Mongoose would create an index in the background and auto set the expireAfterSeconds to in this case 120 seconds (specified by the 2m).
It is important to note that the TTL process runs once every 60 seconds so it is not perfectly on time always.
If you are working with Mongodb Atlas Replica Sets - try:
import * as mongoose from 'mongoose';
let currentSchema = new mongoose.Schema({
createdAt: { type: Date, expires: 10000, default: Date.now },
id: String,
name: String,
packageId: Number,
age: Number
});
currentSchema.index({"lastModifiedDate": 1 },{ expireAfterSeconds: 10000 });
new Scehma({
expireAt: {
type: Date,
expires: 11,
default: Date.now
}
)}
This is the solution that worked for me according to this in the current Mongoose docs.
There is a npm library - 'mongoose-ttl'.:
var schema = new Schema({..});
schema.plugin(ttl, { ttl: 5000 });
you can see all the options of this library:
https://www.npmjs.com/package/mongoose-ttl
const Schema = new mongoose.Schema({id: {
type: Number},
createdAt: {
type: Date, expires: '4h', index: true,
default: Date.now}});
You need to add index: true while creating you schema
9/2022 Working Solution using Mongoose 6.5.4
None of the answers here worked for me, but I was able to finally get it working using the latest version of Mongoose currently available, 6.5.4.
Say our Schema looks like this:
const MySchema = new mongoose.Schema({
id: { type: Number },
myCustomTTLField: { type: Date }
});
myCustomTTLField is the field you want to index and have control the expiration. To achieve this, we add the following under our schema definition:
MySchema.path('myCustomTTLField').index({ expires: 60 });
The argument in MySchema.path is the name of the field you want to index for TTL. The expires option should be the number of seconds that will elapse from the Date represented in myCustomTTLField before the document is deleted. In the example above, the document will be deleted 60 seconds after whatever date is saved in myCustomTTLField. The full example:
const MySchema = new mongoose.Schema({
id: { type: Number },
myCustomTTLField: { type: Date }
});
MySchema.path('myCustomTTLField').index({ expires: 60 });
Please let me know if this works for you, I hope this helps. Mongoose TTL has been a thorn in my side for a long time, as their docs are notoriously tough to navigate. I found this solution via a small example buried in the docs here.
IMPORTANT NOTE:
TTL is not guaranteed to happen at exactly the time specified by your date + expiration seconds. This is due to how MongoDB's background delete process works. It runs every 60 seconds, so you may theoretically wait up to 60 seconds past expected TTL before seeing your document deleted. More info on that from the MongoDB docs.
FWIW I could only get the expires feature to work on a field called expiresAt. Here's my interface, and schema for implementing this in Typescript.
import { model, Schema, Types } from 'mongoose';
export interface ISession {
sessionId: string;
userId: Types.ObjectId;
role: string;
expiresAt?: Date;
}
const sessionSchema = new Schema<ISession>({
sessionId: { type: String, required: true, indexes: { unique: true} },
userId: { type: Schema.Types.ObjectId, required: true, ref: 'users'},
role: { type: String, required: true, enum: [ 'ADMIN', 'BASIC_USER' ]},
expiresAt: { type: Date, expires: '1h', default: Date.now }
}, { versionKey: false });
Reading the Mongoose documentation it seems like all the other proposed solutions should work too. I don't know why they were not for me. You can read the official Mongoose docs on expiresAt here.

Timestamp from Node is 3 minutes behind when converted to Python datetime

I have a Node/Express API that sets a timestamp on a Mongoose model:
var NotificationSchema = new Schema({
created_on: {
type: Number,
default: new Date().getTime()
},
. . .
});
When I convert this into a Python datetime in Django:
created_on = datetime.fromtimestamp(notification['created_on'] / 1000.0)
it's exactly 3 minutes behind. Both applications are running on the same server. What's going on with this timestamp?
Got this figured out. It was as simple as specifying the default value as:
var NotificationSchema = new Schema({
created_on: {
type: Number,
default: Date.now
},
. . .
});
Now each time a model gets saved, Date.now is called properly.

MEAN.JS MissingSchemaError

I'm playing with MEAN.JS to see how I like it and I got an error that I can usually solve, but this time I can't seem to figure out what I may be doing wrong.
I'm trying to populate a child object using mongooses populate method, but I'm now getting this error: MissingSchemaError: Schema hasn't been registered for model "topic" This makes sense ... makes sure the "topic" model schema is loaded. I thought it should be loaded according to the loading order in MEAN.js
moment.server.model.js
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
/**
* Moment Schema
*/
var MomentSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill Moment name',
trim: true
},
content: {
type: String,
default: '',
trim: true
},
created: {
type: Date,
default: Date.now
},
topic: {
type: Schema.ObjectId,
ref: 'Topic'
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Moment', MomentSchema);
topic.server.model.js
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
moment = require('moment-timezone');
/**
* Topic Schema
*/
var TopicSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill Topic name',
trim: true
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
},
moments: [
{
type: Schema.ObjectId,
ref: 'Moment'
}
]
});
mongoose.model('Topic', TopicSchema);
The Query that causes the error:
Moment.find().sort('-created').populate('user', 'displayName', 'topic').exec(function(err, moments) { ... }
What might be causing this error and how can I fix it? I've solved this before in other node systems but in meanjs I think I'm missing something fundamental.
Figured it out. I was ignoring how to use populate correctly. To fix, I simply chained another populate call to the other dbRef value as such:
Moment.find()
.sort('-created')
.populate('user', 'displayName')
.populate('topic')
.exec(function(err, moments) {
// do stuff with results
});
Now the topic as well as the username is populated.
Just wrote a sticky note to my self entitled: RTFM.

Categories