MongoDB and Mongoose Note

Posted on: September 09, 2023

Find more details in Mongoose Documents.

Set up

  1. Follow this tutorial to set up a hosted database on MongoDB Atlas.
  2. Write your MongoDB Atlas database URI inside .env file
  3. Import Mongoose in your file e.g. App.js as mongoose
  4. Connect database using the following syntax: mongoose.connect(<Your URI>, { useNewUrlParser: true, useUnifiedTopology: true });
  5. Replace <Your URI> with process.env.<uri_name>

Schema and Model

Each schema maps to a MongoDB collection (i.e. a table in relational database), it defines the shape of the documents (i.e. instances of the objects) within that collection. We use schema to create models, which is like a class object, via mongoose.model(<name>, <schema>).

Example

js
const Schema = mongoose.Schema;
const personSchema = new Schema({
name: { type: String, required: true },
age: Number,
favoriteFoods: [String],
});
let Person = mongoose.model("Person", personSchema);

In the example above, personSchema has three fields: required string field name, a optional number field age and a optional array of strings favoriteFoods. personSchema is the created schema defining format of the model and Person is the model.

When building a schema, there are three options for name validation:

js
name: String
name: {type: String}
name: {type: String, required: true} //preferred

Detailed introduction can be found in this article.

done()

The done() function is a callback that tells us that we can proceed after completing an asynchronous operation such as inserting, searching, updating, or deleting. It's following the Node convention, and should be called as done(null, data) on success, or done(err) on error.

js
const someFunc = function(done) {
//... do something (risky) ...
if (error) return done(error);
done(null, result);
};

Create Record

To create and save one record of a model

Create a document/record of a model is like create a object, then save the record using document.save().

js
const createAndSavePerson = (done) => {
const p1 = new Person({
name: "p1",
age: 29,
favoriteFoods: ["Pizza", "Sushi"],
});
p1.save((err, data) => {
if (err) return done(err);
done(null, data);
});
};

In the example above, p1 is the document.

To create many records

To create many instances of a model, use Model.create() that takes an array of objects, then saves them in db.

js
const createManyPeople = (arrayOfPeople, done) => {
Person.create(arrayOfPeople, (err, data) => {
if (err) return done(err);
done(null, data);
});
};

Find Record

Find all matching documents

Model.find() takes a query document (a JSON object), and returns an array of matching documents.

js
const findPeopleByName = (personName, done) => {
Person.find({ name: personName }, (err, data) => {
if (err) return done(err);
done(null, data);
});
};

Find a single matching document

Model.findOne() also takes a query, but only returns a single matching document, even there are multiple matching items. This is useful when searching by unique properties.

js
const findOneByFood = (food, done) => {
Person.findOne({ favoriteFoods: food }, (err, data) => {
if (err) return done(err);
done(null, data);
});
};

Find document by ID

When saving a document, MongoDB automatically adds the field _id, and set it to a unique alphanumeric key. Model.findById(<id>) is almost equivalent to Model.findOne({_id: <id>}).

js
const findPersonById = (personId, done) => {
Person.findById(personId, (err, data) => {
if (err) return done(err);
done(null, data);
});
};

Chain Search Query Helpers to Narrow Search Results

You can store the query in a variable for later use. This kind of object enables you to build up a query using chaining syntax. The actual db search is executed when you finally chain the method .exec().

js
// This finds people who like burrito, and will sort them by name, limited the results to two documents, and hide their age.
const queryChain = (done) => {
const foodToSearch = "burrito";
Person.find({ favoriteFoods: foodToSearch })
.sort({ name: 1 })
.limit(2)
.select({ age: 0 })
.exec((err, data) => {
if (err) return done(err);
done(null, data);
});
};

Update Record

Find and edit then save

Can use any find method listed above to find the document, then make changes and save the document. This is the classic update method.

js
const findEditThenSave = (personId, done) => {
const foodToAdd = "hamburger";
Person.findById({ _id: personId }, (err, person) => {
if (err) return done(err);
person.favoriteFoods.push(foodToAdd);
person.save((err, updatedPerson) => {
if (err) return done(err);
done(null, updatedPerson);
});
});
};

Find and update

Use Model.findOneAndUpdate() to find and update document, it takes a query as first arugument to find the document, then update using query in second argument. The third aurgument is for passing options. By default these methods return the unmodified object, so adding { new: true } as the option will return the updated document.

js
const findAndUpdate = (personName, done) => {
const ageToSet = 20;
Person.findOneAndUpdate(
{ name: personName },
{ age: ageToSet },
{ new: true },
(err, data) => {
if (err) return done(err);
done(null, data);
}
);
};

Remove Record

Delete one document

Both Model.findByIdAndRemove() and Model.findOneAndRemove find one matching document and remove it from the database, behaves like the previous update methods.

js
const removeById = (personId, done) => {
Person.findByIdAndRemove(personId, (err, data) => {
done(null, data);
});
};

Delete many documents

Model.remove() is useful to delete all the documents matching given criteria. This method doesn’t return the deleted document, but a JSON object containing the outcome of the operation, and the number of items affected.

js
// This removes all documents that has the name Mary
const removeManyPeople = (done) => {
const nameToRemove = "Mary";
Person.remove({ name: nameToRemove }, (err, data) => {
done(null, data);
});
};