What is the "__v" field in Mongoose - node.js

I'm using Mongoose version 3 with MongoDB version 2.2. I've noticed a __v field has started appearing in my MongoDB documents. Is it something to do with versioning? How is it used?

From here:
The versionKey is a property set on each document when first created
by Mongoose. This keys value contains the internal revision of the
document. The name of this document property is configurable. The
default is __v.
If this conflicts with your application you can configure as such:
new Schema({..}, { versionKey: '_somethingElse' })

Well, I can't see Tony's solution...so I have to handle it myself...
If you don't need version_key, you can just:
var UserSchema = new mongoose.Schema({
nickname: String,
reg_time: {type: Date, default: Date.now}
}, {
versionKey: false // You should be aware of the outcome after set to false
});
Setting the versionKey to false means the document is no longer versioned.
This is problematic if the document contains an array of subdocuments. One of the subdocuments could be deleted, reducing the size of the array. Later on, another operation could access the subdocument in the array at it's original position.
Since the array is now smaller, it may accidentally access the wrong subdocument in the array.
The versionKey solves this by associating the document with the a versionKey, used by mongoose internally to make sure it accesses the right collection version.
More information can be found at: http://aaronheckmann.blogspot.com/2012/06/mongoose-v3-part-1-versioning.html

For remove in NestJS need to add option to Schema() decorator
#Schema({ versionKey: false })

It is possible to disable the version key if you don't need it.
See this example:
var User = new mongoose.Schema({
FullName:{
type :String,
},
BirthDay:{
type :String,
},
Address:{
type :String,
},
Gender:{
type:String,
},
PhoneNumber:{
type:Number,
ref:'Account'
},
AccountID:{
type: Schema.Types.ObjectId,
ref: 'Account'
},
UserName:{
type:String,
ref:'Account'
}
},{collection:'User',
versionKey: false //here
});

It is the version key.It gets updated whenever a new update is made. I personally don't like to disable it .
Read this solution if you want to know more
[1]: Mongoose versioning: when is it safe to disable it?

the '__v' field in our 'document' serves 'optimisticConcurrency' concerns.
This term basically means in mongoose :
let, you grabed a document by 'findOne, findById' but not used save() method of mongoose yet. and what if at this interval, any other code grabed same document and used .save() method before the first document instance.
at this use case, if we want to (mongoose specific) throw a version error kinda thing, we use optimisticConcurrency: true option in schema.
and then mongoose will use '__v1' to compare these two document.
without optimisticConcurrency: true option. '__v' has no has no effect. and mongoose will not increase it by 1.
Note : in 'findOneAndUpdate' kinda operations, will not update '__v'. (only save() updates)

The __v field is called the version key. It describes the internal revision of a document. This __v field is used to track the revisions of a document. By default, its value is zero (__v:0).
If you don't want to use this version key you can use the versionKey: false as mongoose.Schema parameter.
You can follow this example...
const mongoose = require('mongoose');
const userSchema = mongoose.Schema(
{
name: {
type: String,
require: true
},
email: {
type: String,
unique: true
},
password: {
type: String,
}
},
{
timestamps: true,
versionKey: false, // Here You have to add.
}
)
module.exports = mongoose.model('tbl_user', userSchema)

We can use versionKey: false in Schema definition
'use strict';
const mongoose = require('mongoose');
export class Account extends mongoose.Schema {
constructor(manager) {
var trans = {
tran_date: Date,
particulars: String,
debit: Number,
credit: Number,
balance: Number
}
super({
account_number: Number,
account_name: String,
ifsc_code: String,
password: String,
currency: String,
balance: Number,
beneficiaries: Array,
transaction: [trans]
}, {
versionKey: false // set to false then it wont create in mongodb
});
this.pre('remove', function(next) {
manager
.getModel(BENEFICIARY_MODEL)
.remove({
_id: {
$in: this.beneficiaries
}
})
.exec();
next();
});
}
}

Related

mongoose schema unique by 2 fields

I am trying to set 2 fields to being unique to each other and not have duplicates.
The code is this :
const Connection = mongoose.model("Connection", new mongoose.Schema({
from_friend: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Friend'
},
to_friend: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Friend'
}
}))
exports.Connection = Connection;
You can do this using a unique index that includes both fields
const ConnectionSchema = mongoose.Schema({
from_friend: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Friend'
},
to_friend: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Friend'
}
});
ConnectionSchema.index({ from_friend: 1, to_friend: 1 }, { unique: true });
module.exports = mongoose.model('Connection', ConnectionSchema);
The unique Option is Not a Validator
A common gotcha for beginners is that the unique option for schemas is not a validator. It's a convenient helper for building MongoDB unique indexes. See the FAQ for more information.
From the FAQ:
Q. I declared a schema property as unique but I can still save duplicates. What gives?
A. Mongoose doesn't handle unique on its own: { name: { type: String, unique: true } } is just a shorthand for creating a MongoDB unique index on name. For example, if MongoDB doesn't already have a unique index on name, the below code will not error despite the fact that unique is true.
var schema = new mongoose.Schema({
name: { type: String, unique: true }
});
var Model = db.model('Test', schema);
Model.create([{ name: 'Val' }, { name: 'Val' }], function(err) {
console.log(err); // No error, unless index was already built
});
However, if you wait for the index to build using the Model.on('index') event, attempts to save duplicates will correctly error.
You will need to write your own custom validator.
If the built-in validators aren't enough, you can define custom validators to suit your needs.
Custom validation is declared by passing a validation function. You can find detailed instructions on how to do this in the SchemaType#validate() API docs.

Unique in mongoose not working as expected

I have my example mongoose schema as below
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const exampleSchema = new Schema ({
name:{
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
mobile:{
type: String,
required: true,
unique: true
}
})
module.exports ={Driver: mongoose.model('Driver', driverSchema)}
Now the thing is that unique in mobile is working fine, but in email it allows me to insert duplicate email address.
You had better to create your indexes in mongodb shell.
In the mongoose docs they state:
In a production environment, you should create your indexes
using the MongoDB shell rather than relying on mongoose to do it for
you. The unique option for schemas is convenient for development and
documentation, but mongoose is not an index management solution.
So I would remove unique options in schema, and can create the unique indexes in mongodb shell like this:
db.drivers.createIndex( { "email": 1 }, { unique: true } )
db.drivers.createIndex( { "mobile": 1 }, { unique: true } )
It was due to I have records in collection before giving unique: true.

Can a subdocument be required in mongoose?

Is it possible to have nested schemas in mongoose and have a required validator on the children? Something like this:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
}
});
const eventSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
host: {
type: userSchema,
required: true
}
});
I can't find anything in the documentation. Thanks.
Yes, your schema is correct.
The docs for mongoose nested schema (SubDocuments) can be found here
i suppose you'll update eventSchema with subdocuments of type user model.
you can use { runValidators: true} for update.
eventModel.update({ name: 'YOUR NAME' }, { $push: { host: user } }, { runValidators: true}, function(err) {
})
You can use the nested schema in mongoose.
It will also give you he Object Id on each sub schema values as well.
Doc: Here
Example: Here
required is a validator added to a schema or subschema in Mongoose (from docs)
so yes, you can set the required field to true ( it is false by default) for your subschema or subdocument in Mongoose.
The example schema you have created is correct.

Return Mongo document using Mongoose where subdocument does NOT exist?

Help! I'm losing my mind. I need to simply return a Mongo document, using Mongoose, IF a sub document does not exist.
My schemas:
var userSchema = new mongoose.Schema({
email: {type: String, unique: true, lowercase: true},
password: {type: String, select: false},
displayName: String,
picture: String,
facebook: String,
deactivation: deactiveSchema
});
var deactiveSchema = new mongoose.Schema({
when : { type: Date, default: Date.now, required: true },
who : { type: Schema.Types.ObjectId, required: true, ref: 'User' }
});
My goal is to lookup a user by their facebook ID if they have not been deactivated.
If they have been deactivated, then a deactivation subdocument will exist. Of course, to save space, if they are active then a deactivation will not exist.
On a side note, I'm also worried about how to properly construct the index on this logic.
I'd post snippets but every attempt has been wrong. =(
You can use $exists operator:
userSchema.find({deactivation:{$exists:false}}).exec(function(err,document){
});
or $ne:
userSchema.find({deactivation:{$ne:null}}).exec(function(err,document){
});
Since you are retiring data and not deleting, I'd go with one of two approaches:
Flag for retired (Recommended)
add to your schema:
retired: {
type: Boolean,
required: true
}
and add an index for this query:
userSchema.index({facebook: 1, retired: 1})
and query:
User.find({facebook: facebookId, retired: false}, callback)
Query for existence
User.find().exists("deactivation", false).exec(callback)
The latter will be slower, but if you really don't want to change anything, it will work. I'd recommend taking some time to read through the indexing section of the mongo docs.
Mongoose has many options for defining queries with conditions and a couple of styles for writing queries:
Condition object
var id = "somefacebookid";
var condition = {
facebook : id,
deactivation: { $exists : true}
};
user.findOne(condition, function (e, doc) {
// if not e, do something with doc
})
http://mongoosejs.com/docs/queries.html
Query builder
Alternatively, you may want to use the query builder syntax if you are looking for something closer to SQL. e.g.:
var id = "somefacebookid";
users
.find({ facebook : id }).
.where('deactivation').exists(false)
.limit(1)
.exec(callback);

Which SchemaType in Mongoose is Best for Timestamp?

I'm using Mongoose, MongoDB, and Node.
I would like to define a schema where one of its fields is a date\timestamp.
I would like to use this field in order to return all of the records that have been updated in the last 5 minutes.
Due to the fact that in Mongoose I can't use the Timestamp() method I understand that my only option is to use the following Javascript method:
time : { type: Number, default: (new Date()).getTime() }
It's probably not the most efficient way for querying a humongous DB.
I would really appreciate it if someone could share a more efficient way of implementing this.
Is there any way to implement this with Mongoose and be able to use a MongoDB timestamp?
Edit - 20 March 2016
Mongoose now support timestamps for collections.
Please consider the answer of #bobbyz below. Maybe this is what you are looking for.
Original answer
Mongoose supports a Date type (which is basically a timestamp):
time : { type : Date, default: Date.now }
With the above field definition, any time you save a document with an unset time field, Mongoose will fill in this field with the current time.
Source: http://mongoosejs.com/docs/guide.html
The current version of Mongoose (v4.x) has time stamping as a built-in option to a schema:
var mySchema = new mongoose.Schema( {name: String}, {timestamps: true} );
This option adds createdAt and updatedAt properties that are timestamped with a Date, and which does all the work for you. Any time you update the document, it updates the updatedAt property. Schema Timestamps Docs.
In case you want custom names for your createdAt and updatedAt
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schemaOptions = {
timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' },
};
const mySchema = new Schema({ name: String }, schemaOptions);
var ItemSchema = new Schema({
name : { type: String }
});
ItemSchema.set('timestamps', true); // this will add createdAt and updatedAt timestamps
Docs: https://mongoosejs.com/docs/guide.html#timestamps
Mongoose now supports the timestamps in schema.
const item = new Schema(
{
id: {
type: String,
required: true,
},
{ timestamps: true },
);
This will add the createdAt and updatedAt fields on each record create.
Timestamp interface has fields
interface SchemaTimestampsConfig {
createdAt?: boolean | string;
updatedAt?: boolean | string;
currentTime?: () => (Date | number);
}
This would help us to choose which fields we want and overwrite the date format.
new mongoose.Schema({
description: {
type: String,
required: true,
trim: true
},
completed: {
type: Boolean,
default: false
},
owner: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
}
}, {
timestamps: true
});
I would like to use this field in order to return all the records that have been updated in the last 5 minutes.
This means you need to update the date to "now" every time you save the object. Maybe you'll find this useful: Moongoose create-modified plugin
You can use timestamps:true along with toDateString to get created and updated date.
const SampleSchema = new mongoose.Schema({
accountId: {
type: String,
required: true
}
}, {
timestamps: true,
get: time => time.toDateString()
});
Sample doc in Mongo DB
First : npm install mongoose-timestamp
Next: let Timestamps = require('mongoose-timestamp')
Next: let MySchema = new Schema
Next: MySchema.plugin(Timestamps)
Next : const Collection = mongoose.model('Collection',MySchema)
Then you can use the Collection.createdAt or Collection.updatedAt anywhere your want.
Created on: Date Of The Week Month Date Year 00:00:00 GMT
Time is in this format.

Resources