Categories
MongoDB

Using MongoDB with Mongoose — String, Number, and Date Schema Types

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

String Schema Types

We can set various properties for strings schema types.

They include:

  • lowercase: boolean, whether to always call .toLowerCase() on the value
  • uppercase: boolean, whether to always call .toUpperCase() on the value
  • trim: boolean, whether to always call .trim() on the value
  • match: RegExp, creates a validator that checks if the value matches the given regular expression
  • enum: Array, creates a validator that checks if the value is in the given array.
  • minlength: Number, creates a validator that checks if the value length is not less than the given number
  • maxlength: Number, creates a validator that checks if the value length is not greater than the given number

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const schema = new mongoose.Schema({
  test: {
    type: String,
    enum: ['apple', 'orange']
  }
});

to make the test field an enum.

Number Schema Types

For number schema types, we can set the following properties for the field:

  • min: Number, creates a validator that checks if the value is greater than or equal to the given minimum.
  • max: Number, creates a validator that checks if the value is less than or equal to the given maximum.
  • enum: Array, creates a validator that checks if the value is strictly equal to one of the values in the given array.

Date Schema Types

For date schema types, we can set:

  • min: Date
  • max: Date

We can use the schema types by writing:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

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

const Person = mongoose.model('Person', personSchema);
const person = new Person({ name: { toString: () => 42 } });
person.save();
console.log(person.name);

We have the toString method that converts 42 into a string.

The field can also be rewritten as:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

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

const Person = mongoose.model('Person', personSchema);
const person = new Person({ name: { toString: () => 42 } });
person.save();
console.log(person.name);

The value is 'String' instead of the String constructor.

They do the same thing.

Dates

If we have date fields, we can call various methods to change its value:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

async function run() {
  const Assignment = mongoose.model('Assignment', { dueDate: Date });
  const assignment = new Assignment({ dueDate: new Date() });
  await assignment.save();
  Assignment.findOne((err, doc) => {
    doc.dueDate.setMonth(3);
    doc.save();
    doc.markModified('dueDate');
    doc.save();
  })
}
run();

We call the setMonth method to set the month.

Conclusion

We can set the schema types with various properties when we create the schema with Mongoose.

Categories
MongoDB

Using MongoDB with Mongoose — Connections

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

Connections

We can connect to a MongoDB database with Mongoose.

To do that, we write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

We connect to the server with the URL and the test collection name.

Operation Buffering

We can start using the models immediately without waiting fir Mongoose to establish a connection.

So we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

async function run() {
  const Assignment = mongoose.model('Assignment', { dueDate: Date });
  const assignment = new Assignment({ dueDate: new Date() });
  await assignment.save();
  Assignment.findOne((err, doc) => {
    doc.dueDate.setMonth(3);
    doc.save();
    doc.markModified('dueDate');
    doc.save();
  })
}
run();

We create our model and use it without waiting for the connect to complete since we have the database code outside the callback.

This is because Mongoose buffers the model function calls internally.

However, Mongoose won’t throw any errors if we use models without connecting.

We can disable buffering by setting bufferCommands to false :

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
mongoose.set('bufferCommands', false);
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

We can create collections with the createCollection method:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

async function run() {
  const schema = new mongoose.Schema({
    name: String
  }, {
    capped: { size: 1024 },
    bufferCommands: false,
    autoCreate: false
  });

  const Model = mongoose.model('Test', schema);
  await Model.createCollection();
}
run();

Now we’ll create the tests collection to create the collection.

Error Handling

We can catch any errors that are raised with the catch method or try-catch with async and await since mongoose.connect returns a promise.

For example, we can write:

const mongoose = require('mongoose');
async function run() {
  try {
    await mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true });
  } catch (error) {
    handleError(error);
  }
}
run();

or:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true }).
  catch(error => handleError(error));

to connect to a database.

We can also listen to the error event as we did in the example above.

Callback

The mongoose.connect method takes a callback to so we can check for errors.

For example, we can write:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true }, function (error) {
  console.log(error);
})

Connection String Options

We can add connection options to the connection string.

For example, we can write:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test?connectTimeoutMS=1000&bufferCommands=false')

We set the connection timeout with the connectTimeoutMS option is set to 1000ms to set.

And we also set bufferCommands option to false .

Conclusion

There are many things we can configure with connections.

Also, Mongoose buffers any commands queued before the connection is made by default so we don’t have to wait for a connection is done before running Mongoose methods.

Categories
MongoDB

Using MongoDB with Mongoose — SchemaTypes

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

SchemaType

A SchemaType is a configuration object for an individual property.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const kittySchema = new mongoose.Schema({
  name: String
});
console.log(kittySchema.path('name') instanceof mongoose.SchemaType);

to check that the name field in the kittySchema is an instance of mongoose.SchemaType .

It should return true , so we know that name is a SchemaType .

If we have nested fields in a schema, we also have to set the types for the nested fields:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const kittySchema = new mongoose.Schema({
  name: { type: String },
  nested: {
    firstName: { type: String },
    lastName: { type: String }
  }
});

Schema Types

With Mongoose, we can define a few schema types with our field.

They include:

  • required: boolean or function, if true adds a required validator for this property
  • default: Any or function, sets a default value for the path. If the value is a function, the return value of the function is used as the default.
  • select: boolean, specifies default projections for queries
  • validate: function, adds a validator function for this property
  • get: function, defines a custom getter for this property using Object.defineProperty().
  • set: function, defines a custom setter for this property using Object.defineProperty().
  • alias: string, available with Mongoose >= 4.10.0 only. Defines a virtual with the given name that gets/sets this path.
  • immutable: boolean, defines path as immutable. Mongoose prevents you from changing immutable paths unless the parent document has isNew: true.
  • transform: function, Mongoose calls this function when you call Document#toJSON() function, including when you JSON.stringify() a document.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const schema = new mongoose.Schema({
  integerOnly: {
    type: Number,
    get: v => Math.round(v),
    set: v => Math.round(v),
    alias: 'i'
  }
});

to create a schema with the integerOnly field.

We control how the value is get and set with the get and set methods respectively.

And we added an alias property to define another name we can access it with.

Indexes

We can add indexes for fields. For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const schema = new mongoose.Schema({
  test: {
    type: String,
    index: true,
    unique: true
  }
});

to add the test schema with the index property set to true to add an index for the test field.

The unique property set to true will add a unique index.

Conclusion

We can specify different schema types to set the fields for the schema.

Categories
MongoDB

Using MongoDB with Mongoose — Maps and Getters

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

Array Mixed Type

If we specify an empty array as the type, then the type is considered a mixed type.

For instance, if we have:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const Empty1 = new mongoose.Schema({ any: [] });
const Empty2 = new mongoose.Schema({ any: Array });
const Empty3 = new mongoose.Schema({ any: [mongoose.Schema.Types.Mixed] });
const Empty4 = new mongoose.Schema({ any: [{}] });

then they all have a any field with the mixed type.

Maps

Mongoose version 5.1.0 or later has the Map type to store key-value pairs.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const userSchema = new mongoose.Schema({
  socialMediaHandles: {
    type: Map,
    of: String
  }
});

const User = mongoose.model('User', userSchema);
console.log(new User({
  socialMediaHandles: {
    github: 'abc',
    twitter: '@abc'
  }
}).socialMediaHandles);

We created the userSchema with the Map type.

The of property sets the type of the value.

Then we can pass in the object to specify the key-value pairs for the map.

We can also write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const userSchema = new mongoose.Schema({
  socialMediaHandles: {
    type: Map,
    of: String
  }
});

const User = mongoose.model('User', userSchema);
const user = new User({
  socialMediaHandles: {}
});
user.socialMediaHandles.set('github', 'abc');
user.set('socialMediaHandles.twitter', '@abc');
console.log(user.socialMediaHandles.get('github'));
console.log(user.get('socialMediaHandles.twitter'));
user.save();

We can access the socialMediaHandles property or put the property name in the string in the first argument of the set method.

And we can get the value by the key with the get method.

Getters

We can add getters to our schema fields.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const root = 'https://s3.amazonaws.com/mybucket';

const userSchema = new mongoose.Schema({
  name: String,
  picture: {
    type: String,
    get: v => `${root}${v}`
  }
});

const User = mongoose.model('User', userSchema);

const doc = new User({ name: 'Val', picture: '/123.png' });
console.log(doc.picture);
console.log(doc.toObject({ getters: false }).picture);

to add a getter to the picture field with the get method.

By default the picture property has the return value of the getter.

But we can also set the getters property to false to get the picture field’s value.

Conclusion

We can add array mixed types, getters, and maps to a schema field.

Categories
MongoDB

Using MongoDB with Mongoose — Buffer, Boolean, ObjectId and Array Types

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

Buffer

We can declare the Buffer type with the Buffer constructor:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const Binary = mongoose.model('binary', { binData: Buffer });

Mixed

We can also add a mixed type to let us add anything as the value for a field:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const Mixed = mongoose.model('mixed', { any: mongoose.Mixed });

We can’t autodetect and save changes when we use this type since it’s a schema-less type.

ObjectIds

We can specify the ObjectId type to store object IDs.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const carSchema = new mongoose.Schema({ driver: mongoose.ObjectId });
const Car = mongoose.model('Car', carSchema);
const car = new Car();
car.driver = new mongoose.Types.ObjectId();
console.log(typeof car.driver)
console.log(car.driver instanceof mongoose.Types.ObjectId);
car.driver.toString();

We created a carSchema to store data about cars.

Inside the schema, we have the driver field, which is of type mongoose.ObjectId .

We can create the object ID to assign with the mongoose.Types.ObjectId() method.

Then we can check the types of car.driver . We should get the type is 'object' .

The 2nd console log should be true since driver is of type ObjectId .

Boolean

Another type that we set to fields is the boolean type.

The following values are cast to true :

  • true
  • 'true'
  • 1
  • '1'
  • 'yes'

And these values are cast to false :

  • false
  • 'false'
  • 0
  • '0'
  • 'no'

For example, if we write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const M = mongoose.model('Test', new mongoose.Schema({ b: Boolean }));
console.log(new M({ b: 'nay' }).b);
console.log(mongoose.Schema.Types.Boolean.convertToFalse);
mongoose.Schema.Types.Boolean.convertToFalse.add('nay');
console.log(new M({ b: 'nay' }).b);

We called mongoose.Schema.Types.Boolean.convertToFalse to let us cast 'nay' to false .

Arrays

Mongoose has an array type that lets us specify arrays.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const ToySchema = new mongoose.Schema({ name: String });
const ToyBoxSchema = new mongoose.Schema({
  toys: [ToySchema],
  strings: [String],
});

We have the ToySchema Mongoose schema, which we specify as the type of the toys array field.

Each entry of the toys array must conform to the ToySchema .

Conclusion

We can specify various types with Mongoose to build our schema.