Mongoose schema set min and max dates - node.js

I want to create an "event" object, events obviously need to happen on a date, I want users to:
not set a date in the past,
and not less than 1 day in the future (haven't tried to implement this)
and not more than 3 months in the future
Pretty sure I need a function, the code below obviously does not work.
const mongoose = require("mongoose");
const eventSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 1,
maxlength: 50,
unique: true,
},
date: {
type: Date,
required: true,
min: Date.now - 24 * 60 * 60 * 1000,
max: Date.now + 90 * 24 * 60 * 60 * 1000,
}

I managed to figure it out,
using javascript getTime() method to get a timestamp in milliseconds then comparing that to points in the future (also in milliseconds).
const mongoose = require("mongoose");
const eventSchema = new mongoose.Schema({
date: {
type: Date,
required: true,
validate: {
validator: function (v) {
return (
v && // check that there is a date object
v.getTime() > Date.now() + 24 * 60 * 60 * 1000 &&
v.getTime() < Date.now() + 90 * 24 * 60 * 60 * 1000
);
},
message:
"An event must be at least 1 day from now and not more than 90 days.",
}
}})

Related

Auto Delete MongoDB documents after 30d

I building a calendar app with React & Express.js & Mongo DB
the calendar has month view displays weekly events, and i want to delete these events automaticlly after 30 days
my schema.ts:
import { Calendar } from "../types";
const calendarSchema = new mongoose.Schema<Calendar>(
{
startDate: {
type: String,
required: true,
},
endDate: {
type: String,
required: true,
},
rRule: {
type: String,
},
title: {
type: String,
required: true,
},
notes: {
type: String,
// required: true,
},
type: {
type: String,
required: true,
},
expireAt: {
type: Number,
},
},
{
timestamps: true,
}
);
calendarSchema.index({ expireAt: 1 }, { expireAfterSeconds: 70 });
export default mongoose.model<Calendar>("Calendar", calendarSchema);
and i am creating a "expireAt" field in my front-end like:
const payload = await createAppointment({
...appointment,
expireAt: new Date().getTime() + 80,
});
now this deletes documents in 40 - 50 seconds, my question is how can i make it 30 or 31 days ?
thanks in advance .
Change expireAt to Date instead of number
And when creating an index assign 0 to expireAfterSeconds
calendarSchema.index({ expireAt: 1 }, { expireAfterSeconds: 0 });
Now you can define the exact date of when each Calendar will expire, from yearly or down back to seconds
For more info, check MongoDB's documentation
The issue is that you set expiry to be after 70 seconds:
calendarSchema.index({ expireAt: 1 }, { expireAfterSeconds: 70 });
Instead of 70 in the expireAfterSeconds just push the value you want, for example for 7 days:
60 * 60 * 24 * 7 = 604800

Mongoose - How to change the TTL based on another field?

How would I set the TTL based on another field in the schema? I want to set the the expireAt field to whatever value accountType is (the value can be a month / a week / a day).However, this code doesn't expire the document or change the expiry date. I've tried many variations of the TTL field but it doesn't seem to work.
import mongoose from 'mongoose'
import { generateKey } from '../lib/generateKey'
const KeySchema = new mongoose.Schema({
key: {
type: String,
default: generateKey,
required: true,
},
accountType: { /* The value can be a month, a week or a day*/
type: String,
required: true,
},
dailyLimit: {
type: Number,
default: 0,
required: true,
},
expireAt: {
type: Date,
default: Date.now,
index: {
expireAfterSeconds: 60
}
},
})
KeySchema.pre('save', function (next) {
switch (this.accountType) {
case 'month':
this.expireAt.index = {
expires: 60 * 60 * 24 * 30
}
break
case 'week':
this.expireAt.index = {
expires: 60 * 60 * 24 * 7
}
break
case 'day':
this.expireAt.index = {
expires: 60 * 60 * 24 * 1
}
break
}
next()
})
export default mongoose.models.Key || mongoose.model('Key', KeySchema)
I've tried
createdAt: { type: Date, expires: 600 },
KeySchema.index({ createdAt: 1 }, { expireAfterSeconds: 3600 })
expireAt: { type: Date, default: Date.now, index: { expires: '5s' }}
MongoDB handles TTL using an index
Mongoose creates the TTL index at the same time it create all of the other indexes, based on the schema, and the same index is used for all documents.
While it is possible to have partial indexes based on field values, MongoDB does not permit creating multiple indexes that contain the same keys but different options, like expiry.
Instead of trying to vary the index option expireAfterSeconds for each document, you might add another field to the scheme named deleteAt with expireAfterSeconds set to 0, then in the pre-save function set deletedAt based on the values of the expireAt and accountType fields.

Mongoose - Set TTL on MongoDB Document

I am trying to set a TTL via mongoose when a document is created in MongoDB, but I'm not having any luck with any of my attempts. Latest version of mongoose is being used in my project and from what I can tell I've tried the most common answers here on SO and elsewhere online.
My Schema
const mongoose = require('mongoose');
const jobSchema = new mongoose.Schema(
{
positionTitle: {
type: String,
},
description: {
type: String,
}
});
const Jobs = mongoose.model('job', jobSchema);
module.exports = Jobs;
I have tried adding a createdAt with expires based on this question answer:
const jobSchema = new mongoose.Schema({
positionTitle: {
type: String,
},
description: {
type: String,
},
createdAt: { type: Date, expires: 3600 },
});
Along with this option that's also in the same question to have createdAt be created automatically via timestamps:
const jobSchema = new mongoose.Schema(
{
positionTitle: {
type: String,
},
description: {
type: String,
},
},
{ timestamps: true }
);
Trying variations of the following to set an index with timestamps defined:
jobSchema.index({ createdAt: 1 }, { expires: 86400 });
jobSchema.index({ createdAt: 1 }, { expires: '1 day' });
jobSchema.index({ createdAt: 1 }, { expireAfterSeconds: 3600 });
Regardless of which option I try, the document is removed after MongoDB's 60-second cycle when a createdAt field is set on the document. Would really love to know what I'm doing wrong.
After trying all the solutions in the thread you mentioned, none of them worked. In the end this code did the trick. It involves setting the expireAt field to the actual time that you want it deleted, which makes sense really.
const Schema = mongoose.Schema;
const YourSchema = new Schema({
expireAt: {
type: Date,
default: Date.now() + 10 * 60 * 1000 // expires in 10 minutes
},
});
This is the only thing that worked, all the other solutions I tried always deleted after 1min, no matter the amount of time I added.
I've been having issues with this as well. I found this thread here https://github.com/Automattic/mongoose/issues/2459 and it worked for me. Translated into your code would look like this.
const mongoose = require('mongoose');
const jobSchema = new mongoose.Schema(
{
positionTitle: {
type: String,
},
description: {
type: String,
},
expireAt: {
type: Date,
default: Date.now,
index: { expires: '5s' }
}
});
const Jobs = mongoose.model('job', jobSchema);
module.exports = Jobs;
On the link I added, it is the very last solution. I'm not exactly sure what this is doing but here is the mongo link of what it should be doing for anyone else with this issue. https://docs.mongodb.com/manual/tutorial/expire-data/. To change the amount of time that you need the document just change the expires. It accepts '#s' and '#d' for sure. Also if you want your document to be deleted at a specific time then you can do this.
expireAt: {
type: Date,
default: new Date('July 22, 2013 14:00:00'),
index: { expires: '0s' }
}
This will delete the document 0 seconds after the specified date.
Problem in TTL, Reason behind Document does not delete after some / few seconds, how to expire document in MongoDB / Mongoose using schema. Solution expireAfterSeconds / expires / index.
NOTE: - 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.
NOTE :- Below code is working fine and the document will delete after 5 minutes.
const verficationSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true,
validate(email) {
if (!validator.isEmail(email)) {
throw new Error("Email is not valid!");
}
},
},
otp: {
type: Number,
required : true
},
expireAt : {
type: Date,
default: Date,
expires : 300 // means 300 seconds = 5 minutes
}
});
NOTE :- Upper code is working fine, But document will delete after 1 minutes, because MongoDB check expiration procedure after every 1 minutes.
const verficationSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true,
validate(email) {
if (!validator.isEmail(email)) {
throw new Error("Email is not valid!");
}
},
},
otp: {
type: Number,
required : true
},
expireAt : {
type: Date,
default: Date,
expires : 8 // means 8 seconds
}
});

Handle date in the insertion, mongodb

My question is simple how to insert a date in my Schema that will be 1 year from the present date ?
const testSchema = new mongoose.Schema({
User_id: {
type: String,
required:true
},
inscription_date: {
type: Date,
default: Date.now
}
end_inscription_date: {
type: Date,
default: xxxxx // => Date.now + 1 year
}
});
For example:
Submit inscription 2019/07/24, the end date must be 2020/07/24.
You can create a function to calculate the one year later date and use that as your default value.
Try this :
var oneYear = 365 * 24 * 60 * 60 * 1000;
var oneYearLater = function() { return new Date(Date.now() + oneYear);};
Use function oneYearLater in your schema.
const testSchema = new mongoose.Schema({
User_id: {
type: String,
required:true
},
inscription_date: {
type: Date,
default: Date.now
}
end_inscription_date: {
type: Date,
default: oneYearLater// => Date.now + 1 year
}
});
Or simply try to add the milli seconds in Date.now.
end_inscription_date: {
type: Date,
default: Date.now() + 365*24*60*60*1000
}

Person Age Virtual Property in Mongoose

I would like to know if there is a simple way in Mongoose of getting a person's age from the associated birthdate in the schema.
// Define the User schema
const Schema = mongoose.Schema;
const UserSchema = new Schema({
id: { type: Number, required: true, unique: true },
first_name: { type: String, required: true, max: [15, 'Too long name'] },
last_name: { type: String, required: true, max: [15, 'Too long surname'] },
gender: { type: String, required: true, enum: ['M', 'F'] },
image: { data: Buffer, contentType: String }
birthdate: { type: Date, required: true }
}, {
collection: 'Users'
});
How to implement this?
// Virtual for user's age
UserSchema
.virtual('age')
.get(function() {
...
});
You can manipulate the date like bellow :
Short answer :
const birthdate = new Date(687882497000);
// replace birthdate by this.birthdate
const nbYearRounded = Math.floor((Date.now() - birthdate.getTime()) / (1000 * 3600 * 24 * 365));
console.log(nbYearRounded);
Exxplaination :
const date = new Date(687882497000);
const timeInMs = date.getTime();
const diffInMs = Date.now() - timeInMs;
const nbDay = diffInMs / 1000 / 3600 / 24;
const nbYear = nbDay / 365;
const nbYearRounded = Math.floor(nbYear);
console.log(date);
console.log(timeInMs);
console.log(diffInMs);
console.log(nbDay);
console.log(nbYear);
console.log(nbYearRounded);
Here is the documentation about VirtualType.prototype.get().
Given example :
var virtual = schema.virtual('fullname');
virtual.get(function () {
return this.name.first + ' ' + this.name.last;
});
This should work
UserSchema.virtual('age').get(function(){
return Math.floor((Date.now() - this.birthdate.getTime()) / (1000 * 3600 * 24 * 365));
});

Resources