Is there a way to selectively apply timestamps in mongoose schema? - mongodb

I'm currently designing a mongoose Schema. The schema is for blog comments, it looks like this:
new mongoose.Schema({
commentedOn: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
author: {
type: String,
required: true
},
contents:{
type: String
},
points: {
type: Number,
default:0
},
timestamps: true
})
The points field is to record the votes of one comment. I don't want to change the timestamp every time when users vote the comment. Is there a way to achieve this? Or should I move the points field out of this schema?

I believe timestamps should be passed in the second argument of the schema.
Regarding your question, the only way I can think of doing this is to not use timestamps and explicitly declare your timestamp fields e.g. createdAt and updatedAt in your schema. Whenever you save or update, you would explicitly set the updatedAt field (or not) depending on the situation.
new mongoose.Schema({
commentedOn: { type: mongoose.Schema.Types.ObjectId, required: true },
author: { type: String, required: true },
contents: String,
points: { Number, default: 0 },
createdAt: { type: Date, default: Date.now },
updatedAt: Date
});

Related

How to add timestamps to mongoose schema subfields?

I'm trying to add createdAt and updatedAt timestamps in subfields of otp in generate: {} and verify:{}
I know that using { timestamps: true } will add the createdAt and updatedAt timestamps to the whole schema.`
const userSchema = new mongoose.Schema({
email: { type: String, unique: true },
name: { type: String },
mobileNumber: {
isVerified: {type: Boolean, default: false},
otp: {
generate: {
attempts: {type: Number, default: 0},
total: {type: Number, default: 0},
createdAt: {type: Date},
updatedAt: {type: Date}
},
verify: {
attempts: {type: Number, default: 0},
total: {type: Number, default: 0},
createdAt: {type: Date},
updatedAt: {type: Date}
}
}
}
}, { timestamps: true });
What is correct solution to add individual timestamps to subfields? Is it correct to do the same by adding {timestamps: true} to the subfields?
generate: {
attempts: {type: Number, default: 0},
total: {type: Number, default: 0},
{timestamps: true}
},
verify: {
attempts: {type: Number, default: 0},
total: {type: Number, default: 0},
{timestamps: true}
}
You will have do define a separate schema for your subfields, then use that as the type of your subfields.
const otpSchema = new mongoose.Schema({
attempts: { type: Number, default: 0 },
total: { type: Number, default: 0 }
}, {
_id: false, // omit _id fields for subfields
timestamps: true // timestamps options for subfields
});
const userSchema = new mongoose.Schema({
email: { type: String, unique: true },
name: { type: String },
mobileNumber: {
isVerified: { type: Boolean, default: false },
otp: {
generate: otpSchema, // use the defined schema
verify: otpSchema
}
}
}, { timestamps: true });
Okay, it seems like this answer became more popular, so a will extended it with full coverage.
What does {timesamps: true} do and how it does what it does?
The original {timesamps: true} code, from mongoose.js can be found here # line:1150
How exactly timestamps: true} knows when and how it should update updatedAt field, and don't update createdAt ?
By this code:
this.pre('save', function(next) {
/**
* SKIP CODE A BIT
*/
if (!skipUpdatedAt && updatedAt && (this.isNew || this.isModified())) {
let ts = defaultTimestamp;
if (this.isNew) {
if (createdAt != null) {
ts = this.$__getValue(createdAt);
} else if (auto_id) {
ts = this._id.getTimestamp();
}
}
this.set(updatedAt, ts);
}
next();
});
So each time when mongoose driver triggers .save on MongooseDocument, this code got executed (if timestamps set to true, of course)
There is a big difference between MongooseDocument (object) and js-Object/JSON/result of find({}).lean()
You could cast various methods on MongooseDocument, like .isNew (this is exactly how mongoose understood that updatedAt field should be updated, and createdAt should not). or convert it .toObject() or .toJSON() Full list of methods can be found here.
Just to be sure: when you are using .find without .lean() option, you are dealing with MongooseDocument(s), but if enable it, you will receive plain JavaScript objects.
How to create you own implementation of {timestamps: true} for your own schema?
It's easy to achieve the same results, via default values and with using setters in your schema:
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now, set: v => v.Date.now()}
You could read more about setters here.
Also, it could be any function you want, for example you could modify value each time on any insert || modify operation (and update, too)
..Or you could avoid setters and update updatedAt field manually in code, every time, via: model.findAndUpdate({your_field: value}, {your_field: value, updatedAt: Date.now()) each time.
So, in the end, using setters (or manual query update), will gave you the same result as timestamps: true option, but you could apply it to every sub-document in your schema.
I don't think such feature fall in the scope of the database capabilities to provide, nor mongoose to enable.
You may want to create two other entities - Attribute and AttributeValue with OneToMany relationship, to track values changes timestamp.
Well, that's how we tackled the issue on my main current project.
I ran into the same problem and mine was a pretty straightforward solution.
const userSchema = mongoose.Schema({ email: String }, { timestamps: true });
my result with createdAt and updatedAt automatically added:
{
"_id" : ObjectId("60ba2cb3bca3572f6ca1b525"),
"title" : "First post with timestamp",
"content" : "Test post timestamp",
"createdAt" : ISODate("2021-06-04T13:37:55.025Z"),
"updatedAt" : ISODate("2021-06-04T13:37:55.025Z"),
"__v" : 0
}

How to give iDs to dynamic fields in React-Redux?

I created a simple dynamic fields in React-Redux with a plus button to add as many field as I want (hobbies) of an already existing form. I'm using mongodb as a database and so I have this error that tells me that my fields/data don't have iDs.
so how can I generate iDs for my data?
this below is my model with featherJs. as you can see this is how I added my hobbies array in the existing model called myService. I can see that my hobbies are created in mongo (using Robo 3T) which is great but i'm having difficulty reusing them (hobbies) in an other component in Redux. I'm not sure if I should give IDs to this fields or create a new service just for them. I never coded something in backend so I'm confused. what's the rule for this kind of situations.
Any other suggestions would be helpful.
warning in Redux: Each child in a list should have a unique "key" prop.
error in api : Cast to ObjectId failed for value at path "_id" for model "
const { Schema } = mongooseClient;
const myService = new Schema({
type: { type: String, enum: VALID_TYPES, required: true },
user: {
type: mongooseClient.Schema.Types.ObjectId,
ref: 'user',
required: true
},
comment: String,
hobbies: [{
type: mongooseClient.Schema.Types.ObjectId,
ref: 'hobbies',
default: [],
required: false }],
date: {
begin: { type: Date, default: Date.now },
current: { type: Date, default: Date.now },
end: { type: Date, required: true },
},
}, {
timestamps: true
});
return mongooseClient.model('myService', myService);
};

MongoDB does not store date accurately while adding new items repeatedly

If I add new data to MongoDB repeatedly, for example adding 3 data in 2-3 seconds, it stores "createdAt" field same.
I actually does not know much about MongoDB. I don't know what else I should try.
FactSchema
const FactSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
text: {
type: String,
required: true,
},
vote: {
type: Number,
default: 0,
},
createdAt: {
type: Date,
default: Date.now(),
},
updatedAt: {
type: Date,
default: Date.now(),
},
});
These 3 documents show same createdAt yet they shouldn't be. There should be 2-3 seconds different between them.
_id: 5d6a8d6863a5d51af80eec87
createdAt: 2019-08-31T15:08:18.190+00:00
_id: 5d6a8d6f63a5d51af80eec88
createdAt: 2019-08-31T15:08:18.190+00:00
_id: 5d6a8d7263a5d51af80eec89
createdAt: 2019-08-31T15:08:18.190+00:00
Use Date.now instead of Date.now().
Update:
Date.now sends the function itself to mongoose which will be evaluated at the time of "document creation",
Whereas Date.now() is the evaluated value which is created when it is run for the first time, that is, at the time of "schema creation".

database design for a market

I want to design database for a market with simple and few objects for selling using NodeJS, MongoDB and Mongoose. Because I'm new to MongoDB and NoSQL designs, I need a guide for designing it.
My implementation is here:
var orderSchema = new Schema({
orderId: Schema.Types.ObjectId,
orderType: {
type: String, enum: ['OBJEC1',
'OBJECT2',
//other objects
], default: 'ALBUM'
},
price: { type: String, enum: ['PRICE1', 'PRICE2', 'PRICE3'] },
coverPhoto: { type: String, default: '' },
photos: [{
address: { type: String, default: 'media/uploads' },
}],
orderQuantity: { type: Number, default: 1 },
isChecked: { type: Boolean, default: true },
date: { type: Date, default: Date.now }
});
Besides, I'll save reference of each order to its related user. Am I right, or not? Thanks a lot.
The way you designed your schema based on the logic seems good. One thing is you have used default in all the fields.
First, you should understand that default is optional and default is used only when you want to populate some value during the data is created.
Example: you have default for date field, here it is good to have. You don't want to manually assign a date during processing the data. So only unless your field should have common default value when creation then you go ahead otherwise remove the default field and make sure the data is inserted properly.
you can use required attribute in case some field is mandatory to create a document in the collection. I guess orderType a mandatory field so don't miss it ever during insertion so make it as required: true.
var orderSchema = new Schema({
orderId: {
type: Schema.Types.ObjectId
},
orderType: {
type: String,
enum: ['OBJEC1','OBJECT2']
},
price: {
type: String,
enum: ['PRICE1', 'PRICE2', 'PRICE3']
},
coverPhoto: {
type: String
},
photos: [{
address: {
type: String
}
}],
orderQuantity: {
type: Number
},
isChecked: {
type: Boolean,
default: true
},
date: {
type: Date,
default: Date.now
}
});

How to model a pending trade in MongoDB?

I'm wondering what the "Mongo Way" is for modeling a pending trade of an item between two users.
I have a user collection and I have a book collection. In my app, the users will be able to propose trades to one another. Until the trade proposal is accepted, the trade needs to be stored as a pending trade in the database.
It seems to me that the best option is to have a 'trades' property on each book document modeled like this (using Mongoose):
const booksSchema = new Schema({
title: { type: String, required: true },
createdAt: { type: Date, 'default': Date.now },
updatedAt: { type: Date, 'default': Date.now },
author: { type: String, required: false},
imageUrl: { type: String, required: false},
ownerUser: { type: Schema.ObjectId, required: true },
trades: [{
fromUser: { type: Schema.ObjectId, required: true },
bookOffered: { type: Schema.ObjectId, required: true }
}]
});
The problem I see with this is that it will involve updating two documents when the trade is accepted. Assuming that the trade is accepted, the ownerUser on each document will need to be changed and the trades array will need to be cleared out.
It seems that to do this you'd want the changes to be in some sort of "Transaction" so that if one didn't update for some reason, then the other wouldn't either.
Is this a typical way to model this type of situation? What to do about the "Transaction" part of the situation?
There is no way to do a transaction including multiple documents in MongoDB.
You might consider a separate Trade collection with documents like:
{
book: ...,
ownerUser: ...,
buyerUser: ...,
status: 'pending'
dateSold: null
}
When the trade is approved you can change this document first, then update any related documents next. Should something else fail, this document would decide whether the transaction had actually happened.