Mongoose TTL for user saved tokens - node.js

I have this User schema.
const userSchema = new Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
tokens: [{
token: {
type: String,
required: true,
}
}], {timestamps: true});
I would like the token provided will expiresAt given time.
Any suggestions?

That is not possible with mongodb.
You can check this document: MongoDb Index TTL
TTL indexes are special single-field indexes that MongoDB can use to
automatically remove documents from a collection after a certain
amount of time or at a specific clock time. Data expiration is useful
for certain types of information like machine generated event data,
logs, and session information that only need to persist in a database
for a finite amount of time.
You can create a new collection for user tokens and relate them on model. I think this could be a better practice. Then you can use ttl index on token collection

Related

Mongoose aggregate and append

I have a Mongo DB (latest version) that I am accessing with Mongoose (v6.5.4)
The project is using a discriminator pattern to keep all documents in the same collection.
There are many instances where i need to join documents.
Set up:
// Models:
const UserSchema = new Schema<IUser>(
{
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
});
// There are other similar models to <Team>
const TeamSchema = new Schema<ITeam>(
{
name: {
type: String,
required: true,
},
userIds: {
type: [Schema.Types.ObjectId],
required: true,
ref: "User",
default: [],
},
});
Problem:
I can use populate to return collections of Teams and the userIds be an array of user objects.
Where I am stuck is querying getting an array of users with an added field of teams[].
I've been trying aggregate to no success, I can loop over the users collection and return a list of Teams but this feels wrong and expensive in terms of read units (production data base is on a pay as you go service)
As data models go there is not much going for it - but it is an existing solution
Can anyone advise?
I was being stupid. The from field in my look up was wrong.
Should have been 'teams' not 'Team' which is the model name.

Retweet schema in MongoDB

What is the best way to model retweet schema in MongoDB? It is important that I have createdAt times of both original message and the time when retweet occurred because of pagination, I use createdAt as cursor for GraphQL query.
I also need a flag weather the message itself is retweet or original, and id references to original message and original user and reposter user.
I came up with 2 solutions, first one is that I keep ids of reposters and createdAt in array in Message model. The downside is that I have to generate timeline every time and for subscription its not clear what message to push to client.
The second is that I treat retweet as message on its own, I have createdAt and reposterId in place but I have a lot of replication, if I were to add like to message i have to push in array of every single retweet.
I could use help with this what is the most efficient way to do it in MongoDB?
First way:
import mongoose from 'mongoose';
const messageSchema = new mongoose.Schema(
{
text: {
type: mongoose.Schema.Types.String,
required: true,
},
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
likesIds: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
reposts: [
{
reposterId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
createdAt: { type: Date, default: Date.now },
},
],
},
{
timestamps: true,
},
);
const Message = mongoose.model('Message', messageSchema);
Second way:
import mongoose from 'mongoose';
const messageSchema = new mongoose.Schema(
{
text: {
type: mongoose.Schema.Types.String,
required: true,
},
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
likesIds: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
isReposted: {
type: mongoose.Schema.Types.Boolean,
default: false,
},
repost: {
reposterId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
originalMessageId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Message',
},
},
},
{
timestamps: true,
},
);
const Message = mongoose.model('Message', messageSchema);
export default Message;
Option 2 is the better choice here. I'm operating with the assumption that this is a Twitter re-tweet or Facebook share like functionality. You refer to this functionality as both retweet and repost so I'll stick to "repost" here.
Option 1 creates an efficiency problem where, to find reposts for a user, the db needs to iterate over all of the repost arrays of all the messageSchema collections to ensure it found all of the reposterIds. Storing ids in mongo arrays in collection X referencing collection Y is great if you want to traverse from X to Y. It's not as nice if you want to traverse from Y to X.
With option 2, you can specify a more classic one-to-many relationship between messages and reposts that will be simpler and more efficient to query. Reposts and non-repost messages alike will ultimately be placed into messageSchema in the order the user made them, making organization easier. Option 2 also makes it easy to allow reposting users to add text of their own to the repost, where it can be displayed alongside the repost in the view this feeds into. This is popular on facebook where people add context to the things they share.
My one question is, why are three fields being used to track reposts in Option 2?
isReposted, repost.reposterId and repost.originalMessageId provide redundant data. All that you should need is an originalMessageId field that, if not null, contains a messageSchema key and, if null, signifies that the message is not itself a repost. If you really need it, the userId of the original message's creator can be found in that message when you query for it.
Hope this helps!

Mongoose - How to prevent mongoose from creating _id on model instantiation

I want MongoDB itself to add an _id upon insertion so I can track the insertion time using the ObjectID but when I do new MyModel(...), moongose will add the id field.
How do I prevent this so the db itself adds the id?
Alternatively how do I create a field which will be set to the INSERTION time by the db?
Edit: I see that this is not technically possible with mongoose, so would it be possible to add a field that is set by MongoDB when the insertion is done?
My model (if relevant):
{
timestamp: {
type: Date,
required: true
},
signaler: {
type: String,
required: true,
trim: true
},
source: {
type: String,
required: false,
trim: true
},
category: {
type: String,
required: false,
trim: true
},
key: {
type: String,
required: true,
trim: true
},
level: {
type: String,
required: false,
trim: true,
uppercase: true,
enum: ['ALARM', 'WARNING', 'NORMAL']
},
payload: {
type: Schema.Types.Mixed,
required: true
}
}
It's an interesting use case you have. Mongoose really does create that _id on when you call a Model constructor!
I see three paths forward:
Don't use mongoose. The lower level mongo driver doesn't create _ids util you insert into a collection.
Pass around a plain javascript object until you are ready to save
it, then use the Model.create method.
Finally, you can use the pre save middleware to update the _id, by manually generating a new one (with mongoose.Types.ObjectId()) that will have more accurate time info.
If you want to introduce a createdAt field that is updated when the document is inserted, then you are also going handle the pre save middleware. That's the way this popular plugin does it: https://github.com/drudge/mongoose-timestamp/blob/master/index.js

MongoDB reusing a collection for every user

I'm not sure the best way to set this up in MongoDB.
I have two collections User and Skill. The collection for Skill has a list of skills that every user should have.
var SkillSchema = new Schema({
name: { type: String, required: true, trim: true },
category: { type: String, required: true, trim: true }
});
mongoose.model('Skill', SkillSchema);
var UserSchema = new Schema({
_id: { type: String, required: true, trim: true },
first_name: { type: String, required: true, trim: true },
last_name: { type: String, required: true, trim: true },
email_address: { type: String, required: true, trim: true },
skills: [{
skill: { type: ObjectId, ref: 'Skill' },
count: { type: Number, default: 0 },
last_performed: { type: Date }
}]
});
mongoose.model('User', UserSchema);
What I am trying to do is have a list of skills, and each user has a count property that shows how many times they have performed the skill, and a last_performed property that has the date they last performed it.
My problem is, I want the list of skills to be same for each user, but I can update their count and last_performed properties uniquely for each user.
The way I have got it currently is referencing the skill id, and then having the count/date in the user schema. The problem with this, is if I add another skill to the Skills schema, the user's skills array won't be updated with the new skill. I figured updating every user every time I add/remove a skill, to reflect the new skills list, wouldn't be the optimal way to do this.
Can you sync the user's skills array to match the Skills schema?
Would it be better to just add each user's count/date to the skill schema directly? The only problem with this is if there are thousands of users, would this have any performance problems, and would it be easy enough to sort the skills by count/date for each user, and query the user's skills individually without returning the counts for every user?
Cheers,
Ben
skills : [{ type: ObjectId, ref: 'Skill' }]. Just push the Ids to the array if you want to add skills when you do a save or an update.
you can populate the array of skills and you can count the skills array. That will give you the count.
If you have last_performed in the Skill model. then you will get access to it after you populate

How to check access to document based on related ones?

I have the following document schemas:
var CalendarModel = mongoose.model('Calendar', mongoose.Schema({
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
rooms: [{
_id: {type: mongoose.Schema.Types.ObjectId},
title: {type: String}
}]
}));
var EventModel = mongoose.model('Event', mongoose.Schema({
room: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
startTime: {
type: Date
},
endTime: {
type: Date
}
}));
Each event related to specific room by their id. I have the following cases:
User could create/update/delete/edit event
User could edit room (only title at now)
When user issues query to perform on of the cases I need to check access to perform operation.
Let's say user can delete some event by _id and he could if he is owner of calendar where event relates to specific room.
What is the best practice to check writes when user tries to delete event?
EDIT: basically I'm looking for best practices for storing permissions for each user's calendar and make fast checking for access. I know that I may query mongo every time that user makes operation (read/edit and etc) but it's overhead. So I need better solution to store application state

Resources