is createdAt unique or not in mongoose timestamp? - node.js

Is createdAt created by the mongoose timestamps feature unique or not if we create multiple documents using mongoose?

No, there is no enforcement of uniqueness enforced at the DB level for these fields.
From a usage perspective we can look at mongoose source code to see how these fields are created:
const createdAt = handleTimestampOption(timestamps, 'createdAt');
const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
const currentTime = timestamps != null && timestamps.hasOwnProperty('currentTime') ?
timestamps.currentTime :
null;
const schemaAdditions = {};
schema.$timestamps = { createdAt: createdAt, updatedAt: updatedAt };
if (updatedAt && !schema.paths[updatedAt]) {
schemaAdditions[updatedAt] = Date;
}
So we don't need to follow the entire flow to understand this is created in memory. Basically when you create a new document the schema creates these two defaulted fields if you have timestamps enabled. Which means from a practical usage perspective it's unlikely for you to get 2 identical timestamps if you're only running a single app.
If you run multiple processes with multiple updates then this case becomes more likely.

Related

How to generate a unique 6 digits number to use as an ID for documents in a collection?

I have a collection of documents which are being added as a result of users' interactions.
Those docs already have an _id field, but I also wanna add a unique human readable ID for every existing and newly created object, in a form of D123456
What is the best way of adding such an ID and being sure that all those IDs are unique?
MongoDB doesn't have an auto-increment option like relational databases.
You can implement something yourself: before you save your document, generate an ID. First, create a database collection whose sole purpose is to hold a counter:
const Counter = mongoose.model('Counter', new mongoose.schema({
current: Number
}));
Second, before you save your object, find and increment the number in the collection:
const humanReadableDocumentId = await Counter.findOneAndUpdate(
// If you give this record a name, you can have multiple counters.
{ _id: 'humanReadableDocumentId' },
{ $inc: { current: 1 } },
// If no record exists, create one. Return the new value after updating.
{ upsert: true, returnDocument: 'after' }
);
const yourDocument.set('prettyId', format(humanReadableDocumentId.current));
function format(id) {
// Just an example.
return 'D' + id.toString().padStart(6, '0');
}
Note: I've tested the query in MongoDB (except for the 'returnDocument' option, which is Mongoose-specific, but this should work)
Formatting is up to you. If you have more than 999999 documents, the 'nice looking ID' in the example will just get longer and be 7+ characters.

MongoDB how to insert time format in the database

I am using mongodb in my application to store data through nodejs . I have created a schema for a collection (to make it here easy I will call that collection register ) .Below is the structure of the collection' s schema .
const register_schema = new Schema(
{
arrived_at: { type: String }, // time of arrival
gone_at: { type: String } // time of departure
}
);
I want just to save time in the arrived_at and gone_at fields in the format of 'HH-MM-SS'. Is it possible to store such data in mongodb?
There is no fine datatype for time handling or for adding specific time in mongodb schema except the timestamp. For adding time, just use string to specify the timing you want to add.

Maintain a custom order/sort of documents in MongoDB

In my web app XY I'm showing a classic list/table of data (documents) to the user. While all the sort functions provided by MongoDB ( and Mongoose, i'm using Mongoose) are pretty clear to me, I'm not interested in sorting by date or alphabetical order. In my case it would be important to let the user maintain a custom sort as in manually drag/drop items around to set a specific order of the documents (e.g. putting favourites in top of the list ). The UI to do this is a no-brainer but how to actually save the order in the database, brain-freeze.
Problem : How would I go about saving such a custom order of documents ?
What I use : NodeJS / Express / Mongoose (MongoDB)
Ideas
So far I could think of 2 ideas on how to do this.
A : Having an additional key (e.g. orderKey) in the mongoose Schema. Big con : I would need to keep constantly updating all documents orderKeys. Also I would need some sort of auto-increment for new documents.
const mySch = new Schema({
orderKey : { type : Number }
});
B : Creating one Schema/Model only for sorting, with an Array including all documents _ids for example. The order of the elements within the array would be used as reference for the custom order of the documents. Whenever the order changes, this Array would be changed as well.
conts orderSch = new Schema({
orderArray : { type : Array }
});
mongoose.model('Order', orderSch);
/* const customOrder = new Order({
orderArray : [ _id1, _id2, _id3, _id10, _id7, .. ]
}); */
Any more ideas or best practises are highly appreciated !

Does Mongoose upsert operation update/renew default schema values?

Mongoose Schema:
new Schema({
...
createDate: { type: Date, default: Date.now },
updateDate: { type: Date, default: Date.now }
});
Upsert operation:
const upsertDoc = {
...
}
Model.update({ key: 123 }, upsertDoc, { upsert: true })
when I upsert with update or findOneAndUpdate the default schema values createDate and updateDate are always renewed no matter document is inserted or updated. It's same when I use $set (in which of course I don't pass dates).
I don't seem to find anything to tell if it's an expected behavior. I expect dates to be added only on insert and not update, unless explicitly set.
If you are looking for "proof" of the expected behavior, then look no further than the source code itself. Particularly within the schema.js main definition:
updates.$setOnInsert = {};
updates.$setOnInsert[createdAt] = now;
}
return updates;
};
this.methods.initializeTimestamps = function() {
if (createdAt && !this.get(createdAt)) {
this.set(createdAt, new Date());
}
if (updatedAt && !this.get(updatedAt)) {
this.set(updatedAt, new Date());
}
return this;
};
this.pre('findOneAndUpdate', _setTimestampsOnUpdate);
this.pre('update', _setTimestampsOnUpdate);
this.pre('updateOne', _setTimestampsOnUpdate);
this.pre('updateMany', _setTimestampsOnUpdate);
}
function _setTimestampsOnUpdate(next) {
var overwrite = this.options.overwrite;
this.update({}, genUpdates(this.getUpdate(), overwrite), {
overwrite: overwrite
});
applyTimestampsToChildren(this);
next();
}
So there you can see all the 'pre' middleware handlers being registered for each of the "update" method variants and to the same functional code. These all essentially modify the $set operator in any "update" you issue to include the updatedAt field, or whatever name you mapped to that key in the schema options.
The actual statement sent with "upsert" actions uses $setOnInsert for the createdAt field or mapped option name ( see the top of the listing ). This action only applies when an "upsert" actually occurs, so documents that exist and are merely matches for any of the "update" methods are never actually touched by this value.
Those operators are part of how MongoDB works and not really to do with mongoose, but the code shown here shows how mongoose "adjusts" your "update" actions in order to include these additional operations.
For reference the whole main function in schema.js which works out what to apply currently begins at Line #798 for the genUpdates() function as called in the bottom part of the listing shown here yet the top part is the last few lines of that function where the keys of $setOnInsert get defined.
So in summary, YES every "update" action is intentional that the updatedAt mapped field has the current Date value assigned, and also that the "updates" are modified to include the $setOnInsert action which only applies when a new document is created as the result of an "upsert" action for the createdAt mapped field.
Well, I'd always recommend to use the provided and recommended way to manage createdAt and updatedAt by mongoose. Simply by passing timeStamp: true as schema options.
This is always a best practice and lets you not to be worried about such behaviors.
I use it and I never see a problem with timestamps using update or findOneAndUpdate.
Here is how you use it
new Schema({
... //Your schema
},{ timestamps: true})

Saving Schema-less Records with Mongoose?

So I've been trying to save CSP reports into Mongoose with a Mixed schema and have ran into a snag of sorts.
If I try to save anything using the "schema-less" way, it only saves the default _v and _id fields
ViolationSchema = new Schema({});
Violation = mongoose.model('CSPViolation', ViolationSchema);
... wait for POST ...
new Violation( req.body ).save( callback );
// { _id : <some_id>, _v : <some_hash> }
If I set a field in the schema to be Mixed and add a .markModified() to the field, it will save.
ViolationSchema = new Schema({ report : { type : Mixed } });
Violation = mongoose.model('CSPViolation', ViolationSchema);
... wait for POST ...
var v = new Violation( { report : req.body } );
v.markModified('report');
v.save( callback );
// report saved under v.report.<actual_report>
I thought about using native MongoDB-style collection.insert, however it doesn't look like the model has an insert method (nor the schema for that matter).
I suppose I could also go over each key in the report I'm saving and manually mark it as modified, but I'd like to avoid that just to store a report such as this.
Any ideas how I can blindly save a mixed schema type using Mongoose?
It looks like this can be done by setting { strict : false } on the schema. This ensures that Mongoose will save any fields that weren't declared in the original schema.
Normally this isn't something you would enable on 95% of your data, it just fits perfectly with what I'm trying to do currently.
Example
ViolationSchema = new Schema({ type: Mixed }, { strict : false });
Violation = mongoose.model('CSPViolation', ViolationSchema);
... wait for POST ...
new Violation( req.body ).save( callback );
// Saves with full data

Resources