Automatically Delete a Token that was created after some milliseconds in Mongoose - mongodb

I have the following Mongoose Model that I wish to auto-delete after 2mins. Unfortunately, the auto-delete is not working. Note that, I wish to keep the created_at field as a Number in milliseconds not as a date. How do I go about getting the below code to work for me.
const mongoose = require("mongoose");
const TokenSchema = new mongoose.Schema(
{
_id: mongoose.Schema.Types.ObjectId,
token: String,
deleted: Boolean,
deleted_at: Number,
created_at: { type: Number, expires: '2m', default: new Date().getTime() },//Auto-Delete after 2minutes
updated_at: Number,
}
);
TokenSchema.pre('save', function (next) {
let shadow = this;
let now = new Date().getTime();
shadow.updated_at = now;
if (!shadow.created_at) {
shadow.created_at = now;
}
next();
});
Thank you

Mongoose uses MongoDB TTL Indexes for expiring documents, which only functions on fields containing either a Date or array of Date values.
If the indexed field for a document contains any other type, it will not be automatically expired, so to get auto-expiry working, you will need to have created_at store type: Date.
MongoDB internally stores dates as the number of milliseconds since epoch, which you can extract with the valueOf() method, and the mongo query language permits querying a date field by pass a number of milliseconds.

Related

MongoDB Mongoose storing same date and time

I have a Uploads Schema where I have stored the uploaded file's date like:
uploaded_date: {
type: Date,
default: Date.now(),
}
By saving date as such, mongoose stores the date and time of files exactly the same. eg:
uploaded_date: 2020-05-19T08:10:00.034+00:00
when I upload multiple files within a minute or so. Why is this occuring? Should I use timestamp for differenciating times?
Use Date.now instead of Date.now()
Mongoose will replace Date.now with the current datetime when creating a new record, so it will update for every record. But, if you would use Date.now() your default value will be set to a fixed time(the creation time of your schema).
uploaded_date: {
type: Date,
default: Date.now,
}

Mongoose deleting all documents at every one minute and not accepting time from `expires` and `expireAfterSeconds`

We are using express and mongoose, we are trying to remove the document every 1000 seconds in the background, but MongoDB removes at an unexpected time. how to solve it?. also would like to know the difference between expires and expireAfterSeconds.
MongoDB - v3.6.5,
mongoose - 5.4.3,
express - 4.16.4
Sample Model :
const mongoose = require('mongoose');
mongoose.set('useCreateIndex', true);
const forgotPassword = mongoose.Schema({
email: { type: String, required: [true, 'Email field is required']},
expiresAt: { type: Date, expires: '2m', default: Date.now }
}, { timestamps: true, versionKey: false, strict: false });
forgotPassword.index({ expiresAt: 1 }, { expireAfterSeconds : 1000 });
module.exports = mongoose.model('forgotpassword', forgotPassword);
Both expires and expireAfterSeconds uses TTL index:
The background task that removes expired documents runs every 60 seconds. As a result, documents may remain in a collection during the period between the expiration of the document and the running of the background task.
Your documents are expected to be removed between 2 and 3 min.
UPDATE:
Check if the collection has correct indexes. Mongoose do not update indexes if the collection already have it.
If expiration time was 0 when you first created the index the documents will be removed within a minute whatever changes you do in your js code until you drop the index, collection, or the whole database.
Use syncIndexes to update indexes on the database side, but be careful to ensure it doesn't happen often on production. It may be quite expensive on large collections.

How can I make a subdocument expire at a particular date in mongoose?

I have the following Schema for a virtual classroom in mongoose:
var classroomSchema = mongoose.Schema({
studentIds: [mongoose.Schema.Types.ObjectId],
teacherIds: [mongoose.Schema.Types.ObjectId],
teacherNames: [String],
createdAt: {
type: Date,
default: Date.now(),
},
lessons: [{
name: String,
startDate: {
type: Date,
min: Date.now(),
},
endDate: {
type: Date,
min: Date.now(),
},
**expiresAt: endDate,**
}],
});
I want each lesson to expire from the classroom after theer endDate has passed. How can I use TTLs in subdocuments in mongoose?
A part of the document cannot be deleted with ttl. I can think of two other options as a workaround:
Reference
Take out lesson to its own collection and place classroom_id in it as reference to classroom. This way you'll be able to remove the lesson alone with ttl.
Cronjob/Scheduler
Use a scheduler like cron to run a job every few minutes/hours to find in classrooms lessons with expiry dates passed and remove them from lesson array.
var CronJob = require('cron').CronJob;
var job = new CronJob({
cronTime: '00 */20 * * * *', //run every 20 minutes
onTick: function() {
//Find classrooms with lessons which have expiry date smaller than Date.now
//Remove those lessons from array and update the classrooms
},
start: false,
timeZone: 'America/Los_Angeles'
});
job.start();
For searching expiresAt within array of subdocument you can use $elemMatch operator, as shown in this example.
The only downside of method 2 is that depending on the cronjob interval you choose, lessons can persist passed their expiry dates for few extra minutes.

Using TTL in MongoDB [duplicate]

I have a very certain thing i want to accomplish, and I wanted to make sure it is not possible in mongoose/mongoDB before I go and code the whole thing myself.
I checked mongoose-ttl for nodejs and several forums and didn't find quite what I need.
here it is:
I have a schema with a date field createDate. Now i wish to place a TTL on that field, so far so good, i can do it like so (expiration in 5000 seconds):
createDate: {type: Date, default: Date.now, expires: 5000}
but I would like my users to be able to "up vote" documents they like so those documents will get a longer period of time to live, without changing the other documents in my collection.
So, Can i change a TTL of a SINGLE document somehow once a user tells me he likes that document using mongoose or other existing npm related modules?
thank you
It has been more than a year, but this may be useful for others, so here is my answer:
I was trying accomplish this same thing, in order to allow a grace period after an entry deletion, so the user can cancel the operation afterwards.
As stated by Mike Bennett, you can use a TTL index making documents expire at a specific clock time.
Yo have to create an index, setting the expireAfterSeconds to zero:
db.yourCollection.createIndex({ "expireAt": 1 }, { expireAfterSeconds: 0 });
This will not affect any of the documents in your collection, unless you set expireAfterSeconds on a particular document like so:
db.log_events.insert( {
"expireAt": new Date('July 22, 2013 14:00:00'),
"logEvent": 2,
"logMessage": "Success!"
} )
Example in mongoose
Model
var BeerSchema = new Schema({
name: {
type: String,
unique: true,
required: true
},
description: String,
alcohol: Number,
price: Number,
createdAt: { type: Date, default: Date.now }
expireAt: { type: Date, default: undefined } // you don't need to set this default, but I like it there for semantic clearness
});
BeerSchema.index({ "expireAt": 1 }, { expireAfterSeconds: 0 });
Deletion with grace period
Uses moment for date manipulation
exports.deleteBeer = function(id) {
var deferred = q.defer();
Beer.update(id, { expireAt: moment().add(10, 'seconds') }, function(err, data) {
if(err) {
deferred.reject(err);
} else {
deferred.resolve(data);
}
});
return deferred.promise;
};
Revert deletion
Uses moment for date manipulation
exports.undeleteBeer = function(id) {
var deferred = q.defer();
// Set expireAt to undefined
Beer.update(id, { $unset: { expireAt: 1 }}, function(err, data) {
if(err) {
deferred.reject(err);
} else {
deferred.resolve(data);
}
});
return deferred.promise;
};
You could use the expire at clock time feature in mongodb. You will have to update the expire time each time you want to extend the expiration of a document.
http://docs.mongodb.org/manual/tutorial/expire-data/#expire-documents-at-a-certain-clock-time

How create a Date field with default value as the current timestamp in MongoDb?

How to create a date field with default value,the default value should be current timestamps whenever the insertion happened in the collection.
Thats pretty simple!
When you're using Mongoose for example, you can pass functions as a default value.
Mongoose then calls the function for every insertion.
So in your Schema you would do something like:
{
timestamp: { type: Date, default: Date.now},
...
}
Remember to only pass the function object itself Date.now and not the value of the function call Date.now()as this will only set the Date once to the value of when your Schema got created.
This solution applies to Mongoose & Node.Js and I hope that is your usecase because you did not specify that more precisely.
Use _id to get the timestamp.
For this particular purpose you don't really need to create an explicit field for saving timestamps. The object id i.e. "_id", that mongo creates by default can be used to serve the purpose thus, saving you an additional redundant space. I'm assuming that you are using node.js so you can do something like the following to get the time of particular document creation:
let ObjectId = require('mongodb').ObjectID
let docObjID = new ObjectId(<Your document _id>)
console.log(docObjID.getTimestamp())
And, if you are using something like mongoose, do it like this:
let mongoose = require('mongoose')
let docObjID = mongoose.Types.ObjectId(<Your document _id>)
console.log(docObjID.getTimestamp())
Read more about "_id" here.
When Creating Document, timestamps is one of few configurable options which can be passed to the constructor or set directly.
const exampleSchema = new Schema({...}, { timestamps: true });
After that, mongoose assigns createdAt and updatedAt fields to your schema, the type assigned is Date.
You would simply do this while inserting... for current timestamp.
collection.insert({ "date": datetime.now() }
Let's consider the user schema in which we are using created date, we can use the mongoose schema and pass the default value as Date.now
var UserSchema = new Schema({
name: {type: String, trim: true},
created: {type: Date, default: Date.now}
});
If we want to save timetamp instead of number then use Number isntead of number like that
var UserSchema = new Schema({
name: {type: String, trim: true},
created: {type: Number, default: Date.now}
});
Note:- When we use Date.now() in the default parameter then this will
only set the Date once to the value of when your Schema got created,
so you'll find the dates same as the that in the other document. It's better to use Date.now instead of Date.now().
Here's a command that doesn't set a default, but it inserts an object with the current timestamp:
db.foo.insert({date: new ISODate()});
These have the same effect:
db.foo.insert({date: ISODate()});
db.foo.insert({date: new Date()});
Be aware that Date() without new would be different - it doesn't return an ISODate object, but a string.
Also, these use the client's time, not the server's time, which may be different (since the time setting is never 100% precise).
I just wish to point out that in case you want the timestamp to be stored in the form of an integer instead of a date format, you can do this:
{
timestamp: { type: Number, default: Date.now},
...
}
Thanks friends ..
I found another way to get timestamp from _id field. objectid.gettimestamp() from this we can get it time stamp.
This is a little old, however I fount when using the Date.now() method, it doesn't get the current date and time, it gets stuck on the time that you started your node process running. Therefore all timestamps will be defaulted to the Date.now() of when you started your server.
One way I worked around this was to do the following:
ExampleSchema.pre('save', function (next) {
const instanceOfSchema = this;
if(!instanceOfSchema.created_at){
instanceOfSchema.created_at = Date.now();
}
instanceOfSchema.updated_at = Date.now();
next();
})
createdAt: {type: Date, default:Date.now},