Categories
MongoDB

Using MongoDB with Mongoose — Queries

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.

Queries

Mongoose comes with various query methods.

We can use the findOne method to return the first entry that matches the query.

For example, we can write:

async function run() {
  const { createConnection, Schema } = require('mongoose');
  const connection = createConnection('mongodb://localhost:27017/test');
  const schema = new Schema({
    name: {
      first: String,
      last: String
    },
    occupation: String
  });
  const Person = connection.model('Person', schema);
  const person = new Person({
    name: {
      first: 'james',
      last: 'smith'
    },
    occupation: 'waiter'
  })
  await person.save();
  const p = await Person.findOne({ 'name.last': 'smith' }, 'name occupation');
  console.log(p);
}
run();

We create the Person schema and save a document that’s created from the Person constructor.

Then we call findOne to find an entry.

The first argument is an object with the query. 'name.last' is the path to the nested field.

The 2nd argument is a string with the columns that we want to select.

Then we can get the result from the thenable object.

Even though we can use async and await , with the findOne method, it doesn’t return a promise.

Then then method is provided so that we can use the async and await syntax.

We should not mix promises and callbacks.

For example, we shouldn’t write code like:

async function run() {
  const { createConnection, Schema } = require('mongoose');
  const connection = createConnection('mongodb://localhost:27017/test');
  const schema = new Schema({
    name: {
      first: String,
      last: String
    },
    occupation: String
  });
  const Person = connection.model('Person', schema);
  const person = new Person({
    name: {
      first: 'james',
      last: 'smith'
    },
    occupation: 'waiter'
  })
  await person.save();
  const update = { name: { first: ['alex'] } };
  const p = await Person.updateOne({ 'name.first': 'james' }, update, (err, res) => {
    console.log(res);
  });
  console.log(p)
}
run();

Streaming

We can stream query results from MongoDB.

To do that, we call the cursor method:

async function run() {
  const { createConnection, Schema } = require('mongoose');
  const connection = createConnection('mongodb://localhost:27017/test');
  const schema = new Schema({
    name: {
      first: String,
      last: String
    },
    occupation: String
  });
  const Person = connection.model('Person', schema);
  const person = new Person({
    name: {
      first: 'james',
      last: 'smith'
    },
    occupation: 'host'
  })
  await person.save();
  const person2 = new Person({
    name: {
      first: 'jane',
      last: 'smith'
    },
    occupation: 'host'
  })
  await person2.save();
  const cursor = Person.find({ occupation: /host/ }).cursor();
  let doc;
  while (doc = await cursor.next()) {
    console.log(doc);
  }
}
run();

We created 2 Person documents.

Then we call find with the query to return the ones with the occupation field that matches the /host/ regex pattern.

Then we use the while loop to get the cursor.next() method to get the next item from the cursor object.

We can also use the for-await-of loop to do the same thing:

async function run() {
  const { createConnection, Schema } = require('mongoose');
  const connection = createConnection('mongodb://localhost:27017/test');
  const schema = new Schema({
    name: {
      first: String,
      last: String
    },
    occupation: String
  });
  const Person = connection.model('Person', schema);
  const person = new Person({
    name: {
      first: 'james',
      last: 'smith'
    },
    occupation: 'host'
  })
  await person.save();
  const person2 = new Person({
    name: {
      first: 'jane',
      last: 'smith'
    },
    occupation: 'host'
  })
  await person2.save();
  const query = Person.find({ occupation: /host/ });
  for await (const doc of query) {
    console.log(doc);
  }
}
run();

We don’t need the cursor method anymore since find and other query methods return the cursor when we use it with for-await-of .

Conclusion

We can make queries with Mongoose to get data.

Categories
MongoDB

Using MongoDB with Mongoose — Nested Documents

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.

Modify Nested Documents

We can modify nested documents by accessing the path and then set the value for it.

For example, we can write:

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const childSchema = new mongoose.Schema({ name: 'string' });
  const parentSchema = new mongoose.Schema({
    children: [childSchema],
    child: childSchema
  });
  const Child = await connection.model('Child', childSchema);
  const Parent = await connection.model('Parent', parentSchema);
  const parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
  await parent.save();
  parent.children[0].name = 'Mary';
  await parent.save();
  console.log(parent);
}
run();

We get the children subarray’s first entry’s name property and set it to 'Mary' .

Then we call save on the parent to save everything.

We can also call the set method to do the same thing.

For example, we can write:

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const childSchema = new mongoose.Schema({ name: 'string' });
  const parentSchema = new mongoose.Schema({
    children: [childSchema],
    child: childSchema
  });
  const Child = await connection.model('Child', childSchema);
  const Parent = await connection.model('Parent', parentSchema);
  const parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
  await parent.save();
  parent.set({ children: [{ name: 'Mary' }] });
  await parent.save();
  console.log(parent);
}
run();

We replaced the children array with the given entry.

Then we save the parent and child entries with the save method.

Adding Subdocuments to Arrays

We can add subdocuments to arrays.

For example, we can write:

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const childSchema = new mongoose.Schema({ name: 'string' });
  const parentSchema = new mongoose.Schema({
    children: [childSchema],
    child: childSchema
  });
  const Child = await connection.model('Child', childSchema);
  const Parent = await connection.model('Parent', parentSchema);
  const parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
  parent.children.push({ name: 'Jane' });
  await parent.save();
  console.log(parent);
}
run();

We call push on the children array to append an entry to the children subarray.

Removing Subdocuments

To remove subdocuments, we can call the remove method.

For example, we can write:

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const childSchema = new mongoose.Schema({ name: 'string' });
  const parentSchema = new mongoose.Schema({
    children: [childSchema],
    child: childSchema
  });
  const Child = await connection.model('Child', childSchema);
  const Parent = await connection.model('Parent', parentSchema);
  const parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
  await parent.children[0].remove();
  await parent.save();
  console.log(parent);
}
run();

to remove a document from the children array.

To remove an object, we can call the remove method on the object:

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const childSchema = new mongoose.Schema({ name: 'string' });
  const parentSchema = new mongoose.Schema({
    children: [childSchema],
    child: childSchema
  });
  const Child = await connection.model('Child', childSchema);
  const Parent = await connection.model('Parent', parentSchema);
  const parent = new Parent({ child: { name: 'Matt' } })
  await parent.child.remove();
  await parent.save();
  console.log(parent);
}
run();

Conclusion

We can work with nested MongoDB documents easily with Mongoose.

Categories
MongoDB

Using MongoDB with Mongoose — Updating Data

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.

Updating

We can update the first with the given key and value with the updateOne method.

For example, we can write:

const mongoose = require('mongoose');
const connection = mongoose.createConnection('mongodb://localhost:27017/test');
const schema = new mongoose.Schema({ name: 'string', size: 'string' });
const Tank = connection.model('Tank', schema);
Tank.updateOne({ size: 'small' }, { name: 'small tank' }, (err) => {
  if (err) {
    return console.log(err);
  }
});

The first argument is the query for the document.

The 2nd argument is the key-value pair we want to update the document with.

The 3rd argument is the callback that’s called after the operation is done.

Documents

Mongoose documents are one to one mapping to documents as stored in MongoDB.

Each document is an instance of its model.

For example, if we have:

const mongoose = require('mongoose');
const connection = mongoose.createConnection('mongodb://localhost:27017/test');
const schema = new mongoose.Schema({ name: 'string', size: 'string' });
const Tank = connection.model('Tank', schema);
const tank = new Tank({ name: 'big tank' });
console.log(tank instanceof Tank);
console.log(tank instanceof mongoose.Model);
console.log(tank instanceof mongoose.Document);

then we can see all the console logs are true .

The tank document is an instance of the Tank constructor.

It’s also an instance of the Model constructor and the Document constructor.

Retrieving

When we retrieve a document with findOne , it returns a promise that resolves to a document.

For example, if we have:

const { runInContext } = require('vm');

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const schema = new mongoose.Schema({ name: 'string', size: 'string' });
  const Tank = connection.model('Tank', schema);
  const tank = new Tank({ name: 'big tank' });
  await tank.save();
  const doc = await Tank.findOne();
  console.log(doc instanceof Tank);
  console.log(doc instanceof mongoose.Model);
  console.log(doc instanceof mongoose.Document);
}
run();

We see again that all 3 console logs are true , so we know that documents are retrieved with findOne .

Updating

We can modify a document and call save to update a document.

For example, we can write:

const { runInContext } = require('vm');

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const schema = new mongoose.Schema({ name: 'string', size: 'string' });
  const Tank = connection.model('Tank', schema);
  const tank = new Tank({ name: 'big tank' });
  await tank.save();
  const doc = await Tank.findOne();
  doc.name = 'foo';
  await doc.save();
}
run();

We get the document we want to change with the findOne method.

Then we set the property to the given value and the call save to save the changes.

If the document with the given _id isn’t found, then we’ll get a DocumentNotFoundError raised:

const { runInContext } = require('vm');

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const schema = new mongoose.Schema({ name: 'string', size: 'string' });
  const Tank = connection.model('Tank', schema);
  const tank = new Tank({ name: 'big tank' });
  tank.save();
  const doc = await Tank.findOne();
  try {
    await Tank.deleteOne({ _id: doc._id });
    doc.name = 'foo';
    await doc.save();
  } catch (error) {
    console.log(error)
  }
}
run();

We can also update all documents with the updateMany method:

const { runInContext } = require('vm');

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const schema = new mongoose.Schema({ name: 'string', size: 'string' });
  const Tank = connection.model('Tank', schema);
  const tank = new Tank({ name: 'big tank' });
  tank.save();
  await Tank.updateMany({}, { $set: { name: 'foo' } });
}
run();

We update all documents in the tanks collection by calling the updateMany method with the 2nd argument being what we want to set.

This is indicated by the $set property.

The first argument of updateMany is the query object.

Conclusion

There are various ways to update MongoDB documents with Mongoose.

Categories
MongoDB

Using MongoDB with Mongoose — Document Validation

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.

Validation

We can validate documents before saving by using the validate method.

For example, we can write:

const { runInContext } = require('vm');

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const schema = new mongoose.Schema({ name: 'string', size: 'string' });
  const Tank = connection.model('Tank', schema);
  let t = new Tank({ name: 'foo', size: 'small' });
  await t.validate();
  let t2 = new Tank({ name: 'foo', size: -1 });
  await t2.validate();
}
run();

validate is run internally so we don’t have to run it ourselves.

We can set the runValidators property explicitly to control whether we want to run the validator:

const { runInContext } = require('vm');

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const schema = new mongoose.Schema({ name: 'string', size: 'string' });
  const Tank = connection.model('Tank', schema);
  Tank.updateOne({}, { size: -1 }, { runValidators: true });
}
run();

Overwriting

We can overwrite a document.

For example, we can write:

const { runInContext } = require('vm');

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const schema = new mongoose.Schema({ name: 'string', size: 'string' });
  const Tank = connection.model('Tank', schema);
  const doc = await Tank.findOne({ });
  doc.overwrite({ name: 'James' });
  await doc.save();
}
run();

to call the overwrite method to change the first tanks document with the name field set to 'James' .

Subdocuments

Subdocuments are documents that are embedded in other documents.

We can nest schemas in other schemas.

For example, we can write:

const { runInContext } = require('vm');

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const childSchema = new mongoose.Schema({ name: 'string' });
  const parentSchema = new mongoose.Schema({
    children: [childSchema],
    child: childSchema
  });
  const Child = await connection.model('Child', childSchema);
  const Parent = await connection.model('Parent', parentSchema);
  const parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
  await parent.save();
}
run();

We created the Child and Parent schemas with the Child schema embedded in the Parent schema.

The children property has an array of documents that fie the Child schema.

When we call save on the parent , everything embedded inside will also be saved.

We can watch the validate and save events for each schema.

For example, we can write:

const { runInContext } = require('vm');

async function run() {
  const mongoose = require('mongoose');
  const connection = mongoose.createConnection('mongodb://localhost:27017/test');
  const childSchema = new mongoose.Schema({ name: 'string' });
  const parentSchema = new mongoose.Schema({
    children: [childSchema],
    child: childSchema
  });

  childSchema.pre('validate', function (next) {
    console.log('2');
    next();
  });

  childSchema.pre('save', function (next) {
    console.log('3');
    next();
  });

  parentSchema.pre('validate', function (next) {
    console.log('1');
    next();
  });

  parentSchema.pre('save', function (next) {
    console.log('4');
    next();
  });
  const Child = await connection.model('Child', childSchema);
  const Parent = await connection.model('Parent', parentSchema);
  const parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
  await parent.save();
}
run();

We see that they have the number logged.

They should run in the order in the same order the numbers are in.

So first the parent is validated, then the children are validated.

The children documents are saved, and then the parent is saved.

Conclusion

Mongoose comes with document validation built-in.

Categories
MongoDB

Using MongoDB with Mongoose — Data Manipulation with Models

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.

Models

We can create models so we can use them as templates to create a document in the MongoDB database.

For example, we can write:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test')
const schema = new mongoose.Schema({ name: 'string', size: 'string' });
const Tank = mongoose.model('Tank', schema);
const small = new Tank({ size: 'small' });
small.save((err) => {
  if (err) {
    return console.log(err);
  }
});

We called mongoose.Schema that has the name and size string fields.

Then we create the model with the schema with the mongoose.model method.

Next, we use the model class to create the document with the data we want.

Then we call save to save the document.

The tank collection will be created if it’s not already created.

Then the document will be created.

The callback we pass into the save method has the err parameter that will be defined if there’s an error.

We can also add the create static method to create the document:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test')
const schema = new mongoose.Schema({ name: 'string', size: 'string' });
const Tank = mongoose.model('Tank', schema);
Tank.create({ size: 'small' }, (err, small) => {
  if (err) {
    return console.log(err);
  }
  console.log(small);
});

The first argument is the document we want to create.

And the 2nd argument is the callback that’s called when the result is computed.

Also, we can call the insertMany static method on the Tank model:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test')
const schema = new mongoose.Schema({ name: 'string', size: 'string' });
const Tank = mongoose.model('Tank', schema);
Tank.insertMany([{ size: 'small' }], (err) => {
  if (err) {
    console.log(err);
  }
});

If we create a custom collection, then we can use the model function to create the model:

const mongoose = require('mongoose');
const connection = mongoose.createConnection('mongodb://localhost:27017/test');
const schema = new mongoose.Schema({ name: 'string', size: 'string' });
const Tank = connection.model('Tank', schema);
Tank.insertMany([{ size: 'small' }], (err) => {
  if (err) {
    console.log(err);
  }
});

Querying

We can query documents with the find method.

For example, we can write:

const mongoose = require('mongoose');
const connection = mongoose.createConnection('mongodb://localhost:27017/test');
const schema = new mongoose.Schema({ name: 'string', size: 'string' });
const Tank = connection.model('Tank', schema);
Tank.find({ size: 'small' }).where('createdDate').gt(365).exec((err, tanks) => {
  if (err) {
    return console.log(err)
  }
  console.log(tanks);
});

We call find with the key and value we’re looking for.

The where method has the field that we want to search for.

gt searches for something that’s greater than.

exec runs the query, and the callback has the results.

Deleting

We can delete items with the deleteOne method:

const mongoose = require('mongoose');
const connection = mongoose.createConnection('mongodb://localhost:27017/test');
const schema = new mongoose.Schema({ name: 'string', size: 'string' });
const Tank = connection.model('Tank', schema);
Tank.deleteOne({ size: 'large' }, (err) => {
  if (err) {
    return console.log(err);
  }
});

We delete the first item that has the size field equal to 'large' .

Conclusion

We can manipulate MongoDB documents via models in with Mongoose.