0% found this document useful (0 votes)
236 views22 pages

Mongoose

The document provides instructions on getting started with Mongoose, including installing Mongoose, connecting to a MongoDB database, defining schemas, creating models, adding documents, querying documents, and adding additional functionality like methods, indexes, and virtuals. Key aspects covered include defining a schema with Mongoose Schema types, compiling schemas into models, adding and saving documents to a collection, querying documents using the model, and adding custom functionality to schemas before creating models.

Uploaded by

sicario01
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
236 views22 pages

Mongoose

The document provides instructions on getting started with Mongoose, including installing Mongoose, connecting to a MongoDB database, defining schemas, creating models, adding documents, querying documents, and adding additional functionality like methods, indexes, and virtuals. Key aspects covered include defining a schema with Mongoose Schema types, compiling schemas into models, adding and saving documents to a collection, querying documents using the model, and adding custom functionality to schemas before creating models.

Uploaded by

sicario01
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 22

==============================================

Getting Started
----------------------------------------------

# install Mongoose
$ npm install mongoose --save

# include mongoose and open connection


// getting-started.js
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true,
useUnifiedTopology: true});

const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
// we're connected!
});

# everything is derived from a Schema.


const kittySchema = new mongoose.Schema({
name: String
});

# compiling schema into a Model.


const Kitten = mongoose.model('Kitten', kittySchema);
const silence = new Kitten({ name: 'Silence' });
console.log(silence.name); // 'Silence'

# add method to schema before compiling mongoose.model()


kittySchema.methods.speak = function () {
const greeting = this.name
? "Meow name is " + this.name
: "I don't have a name";
console.log(greeting);
}

const Kitten = mongoose.model('Kitten', kittySchema);


const fluffy = new Kitten({ name: 'fluffy' });
fluffy.speak(); // "Meow name is fluffy"

# save to db
fluffy.save(function (err, fluffy) {
if (err) return console.error(err);
fluffy.speak();
});

# access all documents through Kitten model.


Kitten.find(function (err, kittens) {
if (err) return console.error(err);
console.log(kittens);
})

# filter documents with a name property beginning with "fluff"


Kitten.find({ name: /^fluff/ }, callback);

==============================================
Schemas
----------------------------------------------

- Schema definition
- Creating a model
- Ids
- Instance methods
- Statics
- Query Helpers
- Indexes
- Virtuals
- Aliases
- Options

*** Schema definition ***


Everything in Mongoose starts with a Schema. Each schema maps to a MongoDB
collection and defines the shape of the documents within that collection.

import mongoose from 'mongoose';


const { Schema } = mongoose;

const blogSchema = new Schema({


title: String, // String is shorthand for {type: String}
author: String,
body: String,
comments: [{ body: String, date: Date }],
date: { type: Date, default: Date.now },
hidden: Boolean,
meta: {
votes: Number,
favs: Number
}
});

The permitted SchemaTypes are:

String
Number
Date
Buffer
Boolean
Mixed
ObjectId
Array
Decimal128
Map

*** Creating a model ***


To use our schema definition, we need to compile schema to Model:

const Blog = mongoose.model('Blog', blogSchema);


// ready to go!

*** Ids ***


By default, Mongoose adds an _id property to your schemas.

const schema = new Schema();


schema.path('_id'); // ObjectId { ... }

When create new document Mongoose add _id of type ObjectId to it.
const Model = mongoose.model('Test', schema);
const doc = new Model();
doc._id instanceof mongoose.Types.ObjectId; // true

# create custom _id property.

const schema = new Schema({ _id: Number });


const Model = mongoose.model('Test', schema);

const doc = new Model();


await doc.save(); // Throws "document must have an _id before saving"

doc._id = 1;
await doc.save(); // works

*** Instance methods ***


Add methods to shcema before compiling.

// define a schema
const animalSchema = new Schema({ name: String, type: String });

// assign a function to the "methods" object of our animalSchema


animalSchema.methods.findSimilarTypes = function(cb) {
return mongoose.model('Animal').find({ type: this.type }, cb);
};

const Animal = mongoose.model('Animal', animalSchema);


const dog = new Animal({ type: 'dog' });

dog.findSimilarTypes((err, dogs) => {


console.log(dogs); // woof
});

*** Add a function property to schema.statics ***


Call the Schema#static() function

// Assign a function to the "statics" object of our animalSchema


animalSchema.statics.findByName = function(name) {
return this.find({ name: new RegExp(name, 'i') });
};
// Or, equivalently, you can call `animalSchema.static()`.
animalSchema.static('findByBreed', function(breed) { return this.find({ breed });
});

const Animal = mongoose.model('Animal', animalSchema);


let animals = await Animal.findByName('fido');
animals = animals.concat(await Animal.findByBreed('Poodle'));

Do not declare statics using ES6 arrow functions (=>). Arrow functions explicitly
prevent binding this, so the above examples will not work because of the value of
this.

*** Query Helpers ***


Add query helper functions to schema before compiling.

animalSchema.query.byName = function(name) {
return this.where({ name: new RegExp(name, 'i') })
};
const Animal = mongoose.model('Animal', animalSchema);

Animal.find().byName('fido').exec((err, animals) => {


console.log(animals);
});

Animal.findOne().byName('fido').exec((err, animal) => {


console.log(animal);
});

*** Indexes ***


MongoDB supports secondary indexes. With mongoose, we define these indexes within
our Schema at the path level or the schema level. Defining indexes at the schema
level is necessary when creating compound indexes.

const animalSchema = new Schema({


name: String,
type: String,
tags: { type: [String], index: true } // field level
});

animalSchema.index({ name: 1, type: -1 }); // schema level

When your application starts up, Mongoose automatically calls createIndex for each
defined index in your schema. Mongoose will call createIndex for each index
sequentially, and emit an 'index' event on the model when all the createIndex calls
succeeded or when there was an error. While nice for development, it is recommended
this behavior be disabled in production since index creation can cause a
significant performance impact. Disable the behavior by setting the autoIndex
option of your schema to false, or globally on the connection by setting the option
autoIndex to false.

mongoose.connect('mongodb://user:pass@localhost:port/database', { autoIndex:
false });
// or
mongoose.createConnection('mongodb://user:pass@localhost:port/database',
{ autoIndex: false });
// or
animalSchema.set('autoIndex', false);
// or
new Schema({..}, { autoIndex: false });

Mongoose will emit an index event on the model when indexes are done building or an
error occurred.

// Will cause an error because mongodb has an _id index by default that
// is not sparse
animalSchema.index({ _id: 1 }, { sparse: true });
const Animal = mongoose.model('Animal', animalSchema);

Animal.on('index', error => {


// "_id index cannot be sparse"
console.log(error.message);
});

*** Virtuals ***


Virtuals are document properties that you can get and set but that do not get
persisted to MongoDB. The getters are useful for formatting or combining fields,
while setters are useful for de-composing a single value into multiple values for
storage.

// define a schema
const personSchema = new Schema({
name: {
first: String,
last: String
}
});

// compile our model


const Person = mongoose.model('Person', personSchema);

// create a document
const person = new Person({
name: { first: 'Jhon', last: 'Doe' }
});

# concatenating properties
console.log(person.name.first + ' ' + person.name.last); // Jhon Doe
# perform with virtual method
personSchema.virtual('fullName').get(function() {
return this.name.first + ' ' + this.name.last;
});
console.log(axl.fullName); // Jhon Doe

# virtual setter method


personSchema.virtual('fullName').
get(function() {
return this.name.first + ' ' + this.name.last;
}).
set(function(v) {
this.name.first = v.substr(0, v.indexOf(' '));
this.name.last = v.substr(v.indexOf(' ') + 1);
});

person.fullName = 'Jane Doe'; // Now `person.name.first` is "Jane"

*** Aliases ***


Aliases are a particular type of virtual used for more readable properties names.

const personSchema = new Schema({


n: {
type: String,
// Now accessing `name` will get you the value of `n`, and setting `name` will
set the value of `n`
alias: 'name'
}
});

// Setting `name` will propagate to `n`


const person = new Person({ name: 'Val' });
console.log(person); // { n: 'Val' }
console.log(person.toObject({ virtuals: true })); // { n: 'Val', name: 'Val' }
console.log(person.name); // "Val"

person.name = 'Not Val';


console.log(person); // { n: 'Not Val' }
# declare aliases on nested paths.
const childSchema = new Schema({
n: {
type: String,
alias: 'name'
}
}, { _id: false });

const parentSchema = new Schema({


// If in a child schema, alias doesn't need to include the full nested path
c: childSchema,
name: {
f: {
type: String,
// Alias needs to include the full nested path if declared inline
alias: 'name.first'
}
}
});

*** Options ***


Schemas have configurable options for the constructor or to the set method:

new Schema({..}, options);

// or

const schema = new Schema({..});


schema.set(option, value);

# option: autoIndex
By default, Mongoose's init() function creates all the indexes defined in your
model's schema by calling Model.createIndexes() after you successfully connect to
MongoDB.

const schema = new Schema({..}, { autoIndex: false });


const Clock = mongoose.model('Clock', schema);
Clock.ensureIndexes(callback);

Change default value to false.


mongoose.set('autoIndex', false);

# option: autoCreate
Before Mongoose builds indexes, it calls Model.createCollection() to create the
underlying collection in MongoDB if autoCreate is set to true.

const schema = new Schema({..}, { autoCreate: true, capped: 1024 });


const Clock = mongoose.model('Clock', schema);
// Mongoose will create the capped collection for you.
Unlike autoIndex, autoCreate is false by default. You can change this default by
setting mongoose.set('autoCreate', true);

# option: bufferCommands
By default, mongoose buffers commands when the connection goes down until the
driver manages to reconnect. To disable buffering, set bufferCommands to false.

const schema = new Schema({..}, { bufferCommands: false });


The schema bufferCommands option overrides the global bufferCommands option.
mongoose.set('bufferCommands', true);
// Schema option below overrides the above, if the schema option is set.
const schema = new Schema({..}, { bufferCommands: false });
option: bufferTimeoutMS
If bufferCommands is on, this option sets the maximum amount of time Mongoose
buffering will wait before throwing an error. If not specified, Mongoose will use
10000 (10 seconds).

// If an operation is buffered for more than 1 second, throw an error.


const schema = new Schema({..}, { bufferTimeoutMS: 1000 });

# option: capped
Mongoose supports MongoDBs capped collections. To specify the underlying MongoDB
collection be capped, set the capped option to the maximum size of the collection
in bytes.

new Schema({..}, { capped: 1024 });


The capped option may also be set to an object if you want to pass additional
options like max or autoIndexId. In this case you must explicitly pass the size
option, which is required.

new Schema({..}, { capped: { size: 1024, max: 1000, autoIndexId: true } });

# option: collection
Mongoose by default produces a collection name by passing the model name to the
utils.toCollectionName method. This method pluralizes the name. Set this option if
you need a different name for your collection.

const dataSchema = new Schema({..}, { collection: 'data' });


option: discriminatorKey

When you define a discriminator, Mongoose adds a path to your schema that stores
which discriminator a document is an instance of. By default, Mongoose adds an __t
path, but you can set discriminatorKey to overwrite this default.

const baseSchema = new Schema({}, { discriminatorKey: 'type' });


const BaseModel = mongoose.model('Test', baseSchema);

const personSchema = new Schema({ name: String });


const PersonModel = BaseModel.discriminator('Person', personSchema);

const doc = new PersonModel({ name: 'James T. Kirk' });


// Without `discriminatorKey`, Mongoose would store the discriminator
// key in `__t` instead of `type`
doc.type; // 'Person'

# option: id
Mongoose assigns each of your schemas an id virtual getter by default which returns
the document's _id field cast to a string, or in the case of ObjectIds, its
hexString. If you don't want an id getter added to your schema, you may disable it
by passing this option at schema construction time.

// default behavior
const schema = new Schema({ name: String });
const Page = mongoose.model('Page', schema);
const p = new Page({ name: 'mongodb.org' });
console.log(p.id); // '50341373e894ad16347efe01'
// disabled id
const schema = new Schema({ name: String }, { id: false });
const Page = mongoose.model('Page', schema);
const p = new Page({ name: 'mongodb.org' });
console.log(p.id); // undefined

*** option: _id ***


Mongoose assigns each of your schemas an _id field by default if one is not passed
into the Schema constructor. The type assigned is an ObjectId to coincide with
MongoDB's default behavior. If you don't want an _id added to your schema at all,
you may disable it using this option.

You can only use this option on subdocuments. Mongoose can't save a document
without knowing its id, so you will get an error if you try to save a document
without an _id.

// default behavior
const schema = new Schema({ name: String });
const Page = mongoose.model('Page', schema);
const p = new Page({ name: 'mongodb.org' });
console.log(p); // { _id: '50341373e894ad16347efe01', name: 'mongodb.org' }

// disabled _id
const childSchema = new Schema({ name: String }, { _id: false });
const parentSchema = new Schema({ children: [childSchema] });

const Model = mongoose.model('Model', parentSchema);

Model.create({ children: [{ name: 'Luke' }] }, (error, doc) => {


// doc.children[0]._id will be undefined
});

*** option: minimize ***


Mongoose by default, "minimize" schemas by removing empty objects.

const schema = new Schema({ name: String, inventory: {} });


const Character = mongoose.model('Character', schema);

// will store `inventory` field if it is not empty


const frodo = new Character({ name: 'Frodo', inventory: { ringOfPower: 1 }});
await frodo.save();
let doc = await Character.findOne({ name: 'Frodo' }).lean();
doc.inventory; // { ringOfPower: 1 }

// will not store `inventory` field if it is empty


const sam = new Character({ name: 'Sam', inventory: {}});
await sam.save();
doc = await Character.findOne({ name: 'Sam' }).lean();
doc.inventory; // undefined

This behavior can be overridden by setting minimize option to false. It will then
store empty objects.

const schema = new Schema({ name: String, inventory: {} }, { minimize: false });
const Character = mongoose.model('Character', schema);

// will store `inventory` if empty


const sam = new Character({ name: 'Sam', inventory: {} });
await sam.save();
doc = await Character.findOne({ name: 'Sam' }).lean();
doc.inventory; // {}
To check whether an object is empty, you can use the $isEmpty() helper:

const sam = new Character({ name: 'Sam', inventory: {} });


sam.$isEmpty('inventory'); // true

sam.inventory.barrowBlade = 1;
sam.$isEmpty('inventory'); // false

*** option: strict ***


The strict option, (enabled by default), ensures that values passed to our model
constructor that were not specified in our schema do not get saved to the db.

const thingSchema = new Schema({..})


const Thing = mongoose.model('Thing', thingSchema);
const thing = new Thing({ iAmNotInTheSchema: true });
thing.save(); // iAmNotInTheSchema is not saved to the db

// set to false..
const thingSchema = new Schema({..}, { strict: false });
const thing = new Thing({ iAmNotInTheSchema: true });
thing.save(); // iAmNotInTheSchema is now saved to the db!!
This also affects the use of doc.set() to set a property value.

const thingSchema = new Schema({..})


const Thing = mongoose.model('Thing', thingSchema);
const thing = new Thing;
thing.set('iAmNotInTheSchema', true);
thing.save(); // iAmNotInTheSchema is not saved to the db

This value can be overridden at the model instance level by passing a second
boolean argument:
const Thing = mongoose.model('Thing');
const thing = new Thing(doc, true); // enables strict mode
const thing = new Thing(doc, false); // disables strict mode

The strict option may also be set to "throw" which will cause errors to be produced
instead of dropping the bad data.

NOTE: Any key/val set on the instance that does not exist in your schema is always
ignored, regardless of schema option.

const thingSchema = new Schema({..})


const Thing = mongoose.model('Thing', thingSchema);
const thing = new Thing;
thing.iAmNotInTheSchema = true;
thing.save(); // iAmNotInTheSchema is never saved to the db

*** option: toJSON ***


Exactly the same as the toObject option but only applies when the document's toJSON
method is called.

const schema = new Schema({ name: String });


schema.path('name').get(function (v) {
return v + ' is my name';
});

schema.set('toJSON', { getters: true, virtuals: false });


const M = mongoose.model('Person', schema);
const m = new M({ name: 'Max Headroom' });
console.log(m.toObject()); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom'
}
console.log(m.toJSON()); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom is
my name' }

// since we know toJSON is called whenever a js object is stringified:


console.log(JSON.stringify(m)); // { "_id": "504e0cd7dd992d9be2f20b6f", "name":
"Max Headroom is my name" }
To see all available toJSON/toObject options, read this.

*** option: toObject ***


Documents have a toObject method which converts the mongoose document into a plain
JavaScript object. This method accepts a few options. Instead of applying these
options on a per-document basis, we may declare the options at the schema level and
have them applied to all of the schema's documents by default.

To have all virtuals show up in your console.log output, set the toObject option to
{ getters: true }:

const schema = new Schema({ name: String });


schema.path('name').get(function(v) {
return v + ' is my name';
});

schema.set('toObject', { getters: true });


const M = mongoose.model('Person', schema);
const m = new M({ name: 'Max Headroom' });
console.log(m); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom is my name'
}
To see all available toObject options, read this.

*** option: validateBeforeSave ***


By default, documents are automatically validated before they are saved to the
database. This is to prevent saving an invalid document. If you want to handle
validation manually, and be able to save objects which don't pass validation, you
can set validateBeforeSave to false.

const schema = new Schema({ name: String });


schema.set('validateBeforeSave', false);
schema.path('name').validate(function (value) {
return value != null;
});

const M = mongoose.model('Person', schema);


const m = new M({ name: null });
m.validate(function(err) {
console.log(err); // Will tell you that null is not allowed.
});
m.save(); // Succeeds despite being invalid

*** option: versionKey ***


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 versionKey
option is a string that represents the path to use for versioning. The default is
__v. If this conflicts with your application you can configure as such:

const schema = new Schema({ name: 'string' });


const Thing = mongoose.model('Thing', schema);
const thing = new Thing({ name: 'mongoose v3' });
await thing.save(); // { __v: 0, name: 'mongoose v3' }

// customized versionKey
new Schema({..}, { versionKey: '_somethingElse' })
const Thing = mongoose.model('Thing', schema);
const thing = new Thing({ name: 'mongoose v3' });
thing.save(); // { _somethingElse: 0, name: 'mongoose v3' }
Note that Mongoose's default versioning is not a full optimistic concurrency
solution. Mongoose's default versioning only operates on arrays as shown below.

// 2 copies of the same document


const doc1 = await Model.findOne({ _id });
const doc2 = await Model.findOne({ _id });

// Delete first 3 comments from `doc1`


doc1.comments.splice(0, 3);
await doc1.save();

// The below `save()` will throw a VersionError, because you're trying to


// modify the comment at index 1, and the above `splice()` removed that
// comment.
doc2.set('comments.1.body', 'new comment');
await doc2.save();
If you need optimistic concurrency support for save(), you can set the
optimisticConcurrency option

Document versioning can also be disabled by setting the versionKey to false. DO NOT
disable versioning unless you know what you are doing.

new Schema({..}, { versionKey: false });


const Thing = mongoose.model('Thing', schema);
const thing = new Thing({ name: 'no versioning please' });
thing.save(); // { name: 'no versioning please' }

Mongoose only updates the version key when you use save(). If you use update(),
findOneAndUpdate(), etc. Mongoose will not update the version key. As a workaround,
you can use the below middleware.

schema.pre('findOneAndUpdate', function() {
const update = this.getUpdate();
if (update.__v != null) {
delete update.__v;
}
const keys = ['$set', '$setOnInsert'];
for (const key of keys) {
if (update[key] != null && update[key].__v != null) {
delete update[key].__v;
if (Object.keys(update[key]).length === 0) {
delete update[key];
}
}
}
update.$inc = update.$inc || {};
update.$inc.__v = 1;
});

*** option: optimisticConcurrency ***


Optimistic concurrency is a strategy to ensure the document you're updating didn't
change between when you loaded it using find() or findOne(), and when you update it
using save().

For example, suppose you have a House model that contains a list of photos, and a
status that represents whether this house shows up in searches. Suppose that a
house that has status 'APPROVED' must have at least two photos. You might implement
the logic of approving a house document as shown below:

async function markApproved(id) {


const house = await House.findOne({ _id });
if (house.photos.length < 2) {
throw new Error('House must have at least two photos!');
}

house.status = 'APPROVED';
await house.save();
}
The markApproved() function looks right in isolation, but there might be a
potential issue: what if another function removes the house's photos between the
findOne() call and the save() call? For example, the below code will succeed:

const house = await House.findOne({ _id });


if (house.photos.length < 2) {
throw new Error('House must have at least two photos!');
}

const house2 = await House.findOne({ _id });


house2.photos = [];
await house2.save();

// Marks the house as 'APPROVED' even though it has 0 photos!


house.status = 'APPROVED';
await house.save();
If you set the optimisticConcurrency option on the House model's schema, the above
script will throw an error.

const House = mongoose.model('House', Schema({


status: String,
photos: [String]
}, { optimisticConcurrency: true }));

const house = await House.findOne({ _id });


if (house.photos.length < 2) {
throw new Error('House must have at least two photos!');
}

const house2 = await House.findOne({ _id });


house2.photos = [];
await house2.save();

// Throws 'VersionError: No matching document found for id "..." version 0'


house.status = 'APPROVED';
await house.save();
option: collation
Sets a default collation for every query and aggregation. Here's a beginner-
friendly overview of collations.

const schema = new Schema({


name: String
}, { collation: { locale: 'en_US', strength: 1 } });

const MyModel = db.model('MyModel', schema);

MyModel.create([{ name: 'val' }, { name: 'Val' }]).


then(() => {
return MyModel.find({ name: 'val' });
}).
then((docs) => {
// `docs` will contain both docs, because `strength: 1` means
// MongoDB will ignore case when matching.
});

*** option: timestamps ***

The timestamps option tells mongoose to assign createdAt and updatedAt fields to
your schema. The type assigned is Date.

By default, the names of the fields are createdAt and updatedAt. Customize the
field names by setting timestamps.createdAt and timestamps.updatedAt.

const thingSchema = new Schema({..}, { timestamps: { createdAt: 'created_at' } });


const Thing = mongoose.model('Thing', thingSchema);
const thing = new Thing();
await thing.save(); // `created_at` & `updatedAt` will be included

// With updates, Mongoose will add `updatedAt` to `$set`


await Thing.updateOne({}, { $set: { name: 'Test' } });

// If you set upsert: true, Mongoose will add `created_at` to `$setOnInsert` as


well
await Thing.findOneAndUpdate({}, { $set: { name: 'Test2' } });

// Mongoose also adds timestamps to bulkWrite() operations


// See https://mongoosejs.com/docs/api.html#model_Model.bulkWrite
await Thing.bulkWrite([
insertOne: {
document: {
name: 'Jean-Luc Picard',
ship: 'USS Stargazer'
// Mongoose will add `created_at` and `updatedAt`
}
},
updateOne: {
filter: { name: 'Jean-Luc Picard' },
update: {
$set: {
ship: 'USS Enterprise'
// Mongoose will add `updatedAt`
}
}
}
]);

By default, Mongoose uses new Date() to get the current time. If you want to
overwrite the function Mongoose uses to get the current time, you can set the
timestamps.currentTime option. Mongoose will call the timestamps.currentTime
function whenever it needs to get the current time.
const schema = Schema({
createdAt: Number,
updatedAt: Number,
name: String
}, {
// Make Mongoose use Unix time (seconds since Jan 1, 1970)
timestamps: { currentTime: () => Math.floor(Date.now() / 1000) }
});

==============================================
Connection
----------------------------------------------

See the mongodb connection string spec for more detail.

Buffering
Error Handling
Options
Connection String Options
Connection Events
A note about keepAlive
Server Selection
Replica Set Connections
Replica Set Host Names
Multi-mongos support
Multiple connections
Connection Pools
Option Changes in v5.x

*** Operation Buffering ***


Mongoose lets you start using your models immediately, without waiting for mongoose
to establish a connection to MongoDB.

mongoose.connect('mongodb://localhost:27017/myapp', {useNewUrlParser: true});


const MyModel = mongoose.model('Test', new Schema({ name: String }));

// Works
MyModel.findOne(function(error, result) { /* ... */ });

That's because mongoose buffers model function calls internally. This buffering is
convenient, but also a common source of confusion. Mongoose will not throw any
errors by default if you use a model without connecting.

const MyModel = mongoose.model('Test', new Schema({ name: String }));


// Will just hang until mongoose successfully connects
MyModel.findOne(function(error, result) { /* ... */ });

setTimeout(function() {
mongoose.connect('mongodb://localhost:27017/myapp', {useNewUrlParser: true});
}, 60000);

To disable buffering, turn off the bufferCommands option on your schema. If you
have bufferCommands on and your connection is hanging, try turning bufferCommands
off to see if you haven't opened a connection properly. You can also disable
bufferCommands globally:

mongoose.set('bufferCommands', false);
Note that buffering is also responsible for waiting until Mongoose creates
collections if you use the autoCreate option. If you disable buffering, you should
also disable the autoCreate option and use createCollection() to create capped
collections or collections with collations.

const schema = new Schema({


name: String
}, {
capped: { size: 1024 },
bufferCommands: false,
autoCreate: false // disable `autoCreate` since `bufferCommands` is false
});

const Model = mongoose.model('Test', schema);


// Explicitly create the collection before using it
// so the collection is capped.
await Model.createCollection();
Error Handling
There are two classes of errors that can occur with a Mongoose connection.

Error on initial connection. If initial connection fails, Mongoose will emit an


'error' event and the promise mongoose.connect() returns will reject. However,
Mongoose will not automatically try to reconnect.
Error after initial connection was established. Mongoose will attempt to reconnect,
and it will emit an 'error' event.
To handle initial connection errors, you should use .catch() or try/catch with
async/await.

mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true }).


catch(error => handleError(error));

// Or:
try {
await mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser:
true });
} catch (error) {
handleError(error);
}
To handle errors after initial connection was established, you should listen for
error events on the connection. However, you still need to handle initial
connection errors as shown above.

mongoose.connection.on('error', err => {


logError(err);
});
Note that Mongoose does not necessarily emit an 'error' event if it loses
connectivity to MongoDB. You should listen to the disconnected event to report when
Mongoose is disconnected from MongoDB.

Options
The connect method also accepts an options object which will be passed on to the
underlying MongoDB driver.

mongoose.connect(uri, options);
A full list of options can be found on the MongoDB Node.js driver docs for
connect(). Mongoose passes options to the driver without modification, modulo a few
exceptions that are explained below.

bufferCommands - This is a mongoose-specific option (not passed to the MongoDB


driver) that disables Mongoose's buffering mechanism
user/pass - The username and password for authentication. These options are
Mongoose-specific, they are equivalent to the MongoDB driver's auth.user and
auth.password options.
autoIndex - By default, mongoose will automatically build indexes defined in your
schema when it connects. This is great for development, but not ideal for large
production deployments, because index builds can cause performance degradation. If
you set autoIndex to false, mongoose will not automatically build indexes for any
model associated with this connection.
dbName - Specifies which database to connect to and overrides any database
specified in the connection string. This is useful if you are unable to specify a
default database in the connection string like with some mongodb+srv syntax
connections.
Below are some of the options that are important for tuning Mongoose.

useNewUrlParser - The underlying MongoDB driver has deprecated their current


connection string parser. Because this is a major change, they added the
useNewUrlParser flag to allow users to fall back to the old parser if they find a
bug in the new parser. You should set useNewUrlParser: true unless that prevents
you from connecting. Note that if you specify useNewUrlParser: true, you must
specify a port in your connection string, like mongodb://localhost:27017/dbname.
The new url parser does not support connection strings that do not have a port,
like mongodb://localhost/dbname.
useCreateIndex - False by default. Set to true to make Mongoose's default index
build use createIndex() instead of ensureIndex() to avoid deprecation warnings from
the MongoDB driver.
useFindAndModify - True by default. Set to false to make findOneAndUpdate() and
findOneAndRemove() use native findOneAndUpdate() rather than findAndModify().
useUnifiedTopology- False by default. Set to true to opt in to using the MongoDB
driver's new connection management engine. You should set this option to true,
except for the unlikely case that it prevents you from maintaining a stable
connection.
promiseLibrary - Sets the underlying driver's promise library.
poolSize - The maximum number of sockets the MongoDB driver will keep open for this
connection. By default, poolSize is 5. Keep in mind that, as of MongoDB 3.4,
MongoDB only allows one operation per socket at a time, so you may want to increase
this if you find you have a few slow queries that are blocking faster queries from
proceeding. See Slow Trains in MongoDB and Node.js.
socketTimeoutMS - How long the MongoDB driver will wait before killing a socket due
to inactivity after initial connection. A socket may be inactive because of either
no activity or a long-running operation. This is set to 30000 by default, you
should set this to 2-3x your longest running operation if you expect some of your
database operations to run longer than 20 seconds. This option is passed to Node.js
socket#setTimeout() function after the MongoDB driver successfully completes.
family - Whether to connect using IPv4 or IPv6. This option passed to Node.js'
dns.lookup() function. If you don't specify this option, the MongoDB driver will
try IPv6 first and then IPv4 if IPv6 fails. If your mongoose.connect(uri) call
takes a long time, try mongoose.connect(uri, { family: 4 })
authSource - The database to use when authenticating with user and pass. In
MongoDB, users are scoped to a database. If you are getting an unexpected login
failure, you may need to set this option.
The following options are important for tuning Mongoose only if you are running
without the useUnifiedTopology option:

autoReconnect - The underlying MongoDB driver will automatically try to reconnect


when it loses connection to MongoDB. Unless you are an extremely advanced user that
wants to manage their own connection pool, do not set this option to false.
reconnectTries - If you're connected to a single server or mongos proxy (as opposed
to a replica set), the MongoDB driver will try to reconnect every reconnectInterval
milliseconds for reconnectTries times, and give up afterward. When the driver gives
up, the mongoose connection emits a reconnectFailed event. This option does nothing
for replica set connections.
reconnectInterval - See reconnectTries
bufferMaxEntries - The MongoDB driver also has its own buffering mechanism that
kicks in when the driver is disconnected. Set this option to 0 and set
bufferCommands to false on your schemas if you want your database operations to
fail immediately when the driver is not connected, as opposed to waiting for
reconnection.
connectTimeoutMS - How long the MongoDB driver will wait before killing a socket
due to inactivity during initial connection. Defaults to 30000. This option is
passed transparently to Node.js' socket#setTimeout() function.
The following options are important for tuning Mongoose only if you are running
with the useUnifiedTopology option:

serverSelectionTimeoutMS - With useUnifiedTopology, the MongoDB driver will try to


find a server to send any given operation to, and keep retrying for
serverSelectionTimeoutMS milliseconds. If not set, the MongoDB driver defaults to
using 30000 (30 seconds).
heartbeatFrequencyMS - With useUnifiedTopology, the MongoDB driver sends a
heartbeat every heartbeatFrequencyMS to check on the status of the connection. A
heartbeat is subject to serverSelectionTimeoutMS, so the MongoDB driver will retry
failed heartbeats for up to 30 seconds by default. Mongoose only emits a
'disconnected' event after a heartbeat has failed, so you may want to decrease this
setting to reduce the time between when your server goes down and when Mongoose
emits 'disconnected'. We recommend you do not set this setting below 1000, too many
heartbeats can lead to performance degradation.
The serverSelectionTimeoutMS option also handles how long mongoose.connect() will
retry initial connection before erroring out. With useUnifiedTopology,
mongoose.connect() will retry for 30 seconds by default (default
serverSelectionTimeoutMS) before erroring out. To get faster feedback on failed
operations, you can reduce serverSelectionTimeoutMS to 5000 as shown below.

Example:

const options = {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
autoIndex: false, // Don't build indexes
poolSize: 10, // Maintain up to 10 socket connections
serverSelectionTimeoutMS: 5000, // Keep trying to send operations for 5 seconds
socketTimeoutMS: 45000, // Close sockets after 45 seconds of inactivity
family: 4 // Use IPv4, skip trying IPv6
};
mongoose.connect(uri, options);
See this page for more information about connectTimeoutMS and socketTimeoutMS

Callback
The connect() function also accepts a callback parameter and returns a promise.

mongoose.connect(uri, options, function(error) {


// Check error in initial connection. There is no 2nd param to the callback.
});

// Or using promises
mongoose.connect(uri, options).then(
() => { /** ready to use. The `mongoose.connect()` promise resolves to mongoose
instance. */ },
err => { /** handle initial connection error */ }
);
Connection String Options
You can also specify driver options in your connection string as parameters in the
query string portion of the URI. This only applies to options passed to the MongoDB
driver. You can't set Mongoose-specific options like bufferCommands in the query
string.

mongoose.connect('mongodb://localhost:27017/test?
connectTimeoutMS=1000&bufferCommands=false&authSource=otherdb');
// The above is equivalent to:
mongoose.connect('mongodb://localhost:27017/test', {
connectTimeoutMS: 1000
// Note that mongoose will **not** pull `bufferCommands` from the query string
});
The disadvantage of putting options in the query string is that query string
options are harder to read. The advantage is that you only need a single
configuration option, the URI, rather than separate options for socketTimeoutMS,
connectTimeoutMS, etc. Best practice is to put options that likely differ between
development and production, like replicaSet or ssl, in the connection string, and
options that should remain constant, like connectTimeoutMS or poolSize, in the
options object.

The MongoDB docs have a full list of supported connection string options. Below are
some options that are often useful to set in the connection string because they are
closely associated with the hostname and authentication information.

authSource - The database to use when authenticating with user and pass. In
MongoDB, users are scoped to a database. If you are getting an unexpected login
failure, you may need to set this option.
family - Whether to connect using IPv4 or IPv6. This option passed to Node.js'
dns.lookup() function. If you don't specify this option, the MongoDB driver will
try IPv6 first and then IPv4 if IPv6 fails. If your mongoose.connect(uri) call
takes a long time, try mongoose.connect(uri, { family: 4 })
Connection Events
Connections inherit from Node.js' EventEmitter class, and emit events when
something happens to the connection, like losing connectivity to the MongoDB
server. Below is a list of events that a connection may emit.

connecting: Emitted when Mongoose starts making its initial connection to the
MongoDB server
connected: Emitted when Mongoose successfully makes its initial connection to the
MongoDB server, or when Mongoose reconnects after losing connectivity.
open: Equivalent to connected
disconnecting: Your app called Connection#close() to disconnect from MongoDB
disconnected: Emitted when Mongoose lost connection to the MongoDB server. This
event may be due to your code explicitly closing the connection, the database
server crashing, or network connectivity issues.
close: Emitted after Connection#close() successfully closes the connection. If you
call conn.close(), you'll get both a 'disconnected' event and a 'close' event.
reconnected: Emitted if Mongoose lost connectivity to MongoDB and successfully
reconnected. Mongoose attempts to automatically reconnect when it loses connection
to the database.
error: Emitted if an error occurs on a connection, like a parseError due to
malformed data or a payload larger than 16MB.
fullsetup: Emitted when you're connecting to a replica set and Mongoose has
successfully connected to the primary and at least one secondary.
all: Emitted when you're connecting to a replica set and Mongoose has successfully
connected to all servers specified in your connection string.
reconnectFailed: Emitted when you're connected to a standalone server and Mongoose
has run out of reconnectTries. The MongoDB driver will no longer attempt to
reconnect after this event is emitted. This event will never be emitted if you're
connected to a replica set.
When you're connecting to a single MongoDB server (a "standalone"), Mongoose will
emit 'disconnected' if it gets disconnected from the standalone server, and
'connected' if it successfully connects to the standalone. In a replica set with
useUnifiedTopology = true, Mongoose will emit 'disconnected' if it loses
connectivity to every server in the replica set, and 'connected' if it manages to
reconnect to at least one server in the replica set.

A note about keepAlive


For long running applications, it is often prudent to enable keepAlive with a
number of milliseconds. Without it, after some period of time you may start to see
"connection closed" errors for what seems like no reason. If so, after reading
this, you may decide to enable keepAlive:

mongoose.connect(uri, { keepAlive: true, keepAliveInitialDelay: 300000 });


keepAliveInitialDelay is the number of milliseconds to wait before initiating
keepAlive on the socket. keepAlive is true by default since mongoose 5.2.0.

Replica Set Connections


To connect to a replica set you pass a comma delimited list of hosts to connect to
rather than a single host.

mongoose.connect('mongodb://[username:password@]host1[:port1][,host2[:port2],...
[,hostN[:portN]]][/[database][?options]]' [, options]);
For example:

mongoose.connect('mongodb://user:pw@host1.com:27017,host2.com:27017,host3.com:27017
/testdb');
To connect to a single node replica set, specify the replicaSet option.

mongoose.connect('mongodb://host1:port1/?replicaSet=rsName');
Server Selection
If you enable the useUnifiedTopology option, the underlying MongoDB driver will use
server selection to connect to MongoDB and send operations to MongoDB. If the
MongoDB driver can't find a server to send an operation to after
serverSelectionTimeoutMS, you'll get the below error:

MongoTimeoutError: Server selection timed out after 30000 ms


You can configure the timeout using the serverSelectionTimeoutMS option to
mongoose.connect():

mongoose.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
serverSelectionTimeoutMS: 5000 // Timeout after 5s instead of 30s
});
A MongoTimeoutError has a reason property that explains why server selection timed
out. For example, if you're connecting to a standalone server with an incorrect
password, reason will contain an "Authentication failed" error.

const mongoose = require('mongoose');

const uri = 'mongodb+srv://username:badpw@cluster0-OMITTED.mongodb.net/' +


'test?retryWrites=true&w=majority';
// Prints "MongoError: bad auth Authentication failed."
mongoose.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
serverSelectionTimeoutMS: 5000
}).catch(err => console.log(err.reason));
Replica Set Host Names
MongoDB replica sets rely on being able to reliably figure out the domain name for
each member. On Linux and OSX, the MongoDB server uses the output of the hostname
command to figure out the domain name to report to the replica set. This can cause
confusing errors if you're connecting to a remote MongoDB replica set running on a
machine that reports its hostname as localhost:

// Can get this error even if your connection string doesn't include
// `localhost` if `rs.conf()` reports that one replica set member has
// `localhost` as its host name.
failed to connect to server [localhost:27017] on first connect
If you're experiencing a similar error, connect to the replica set using the mongo
shell and run the rs.conf() command to check the host names of each replica set
member. Follow this page's instructions to change a replica set member's host name.

Multi-mongos support
You can also connect to multiple mongos instances for high availability in a
sharded cluster. You do not need to pass any special options to connect to multiple
mongos in mongoose 5.x.

// Connect to 2 mongos servers


mongoose.connect('mongodb://mongosA:27501,mongosB:27501', cb);
Multiple connections
So far we've seen how to connect to MongoDB using Mongoose's default connection.
Mongoose creates a default connection when you call mongoose.connect(). You can
access the default connection using mongoose.connection.

You may need multiple connections to MongoDB for several reasons. One reason is if
you have multiple databases or multiple MongoDB clusters. Another reason is to work
around slow trains. The mongoose.createConnection() function takes the same
arguments as mongoose.connect() and returns a new connection.

const conn = mongoose.createConnection('mongodb://[username:password@]host1[:port1]


[,host2[:port2],...[,hostN[:portN]]][/[database][?options]]', options);
This connection object is then used to create and retrieve models. Models are
always scoped to a single connection.

const UserModel = conn.model('User', userSchema);


If you use multiple connections, you should make sure you export schemas, not
models. Exporting a model from a file is called the export model pattern. The
export model pattern is limited because you can only use one connection.

const userSchema = new Schema({ name: String, email: String });

// The alternative to the export model pattern is the export schema pattern.
module.exports = userSchema;

// Because if you export a model as shown below, the model will be scoped
// to Mongoose's default connection.
// module.exports = mongoose.model('User', userSchema);
If you use the export schema pattern, you still need to create models somewhere.
There are two common patterns. First is to export a connection and register the
models on the connection in the file:
// connections/fast.js
const mongoose = require('mongoose');

const conn = mongoose.createConnection(process.env.MONGODB_URI);


conn.model('User', require('../schemas/user'));

module.exports = conn;

// connections/slow.js
const mongoose = require('mongoose');

const conn = mongoose.createConnection(process.env.MONGODB_URI);


conn.model('User', require('../schemas/user'));
conn.model('PageView', require('../schemas/pageView'));

module.exports = conn;
Another alternative is to register connections with a dependency injector or
another inversion of control (IOC) pattern.

const mongoose = require('mongoose');

module.exports = function connectionFactory() {


const conn = mongoose.createConnection(process.env.MONGODB_URI);

conn.model('User', require('../schemas/user'));
conn.model('PageView', require('../schemas/pageView'));

return conn;
};
Connection Pools
Each connection, whether created with mongoose.connect or mongoose.createConnection
are all backed by an internal configurable connection pool defaulting to a maximum
size of 5. Adjust the pool size using your connection options:

// With object options


mongoose.createConnection(uri, { poolSize: 4 });

const uri = 'mongodb://localhost:27017/test?poolSize=4';


mongoose.createConnection(uri);
Option Changes in v5.x

==============================================
Schemas and Models
----------------------------------------------

Schemas Data Types:


String
Number
Date
Boolean
Buffer
ObjectId
Mixed
Array

# Schema example:
schema --> compile to --> model

# example:
var mongoose = require( 'mongoose' ),
dbURI = 'mongodb://localhost/MongoosePM';
mongoose.connect(dbURI);
// Connection events snipped out for brevity

/* ********************************************
USER SCHEMA
******************************************** */
var userSchema = new mongoose.Schema({
name: String,
email: {type: String, unique:true},
createdOn: { type: Date, default: Date.now },
modifiedOn: Date,
lastLogin: Date
});

// Build the User model


mongoose.model( 'User', userSchema );
connect_object.model( 'User', userSchema );

# instances
var user = new User({ name: 'Simon' });
console.log(user.name);

user.save(function (err, user) {


if (err) return handleError(err);

if(!err){
console.log('Saved user name: ' + user.name);
console.log('_id of saved user: ' + user._id);
}
});

==============================================
QueryBuilder
----------------------------------------------

var myQuery = User.find({'name' : 'Simon Holmes'});


myQuery.where('age').gt(18);
myQuery.sort('-lastLogin');
myQuery.select('_id name email');
myQuery.exec(function (err, users){
if (!err){
console.log(users); // output array of users found
}
});

You might also like