- Explain what an ODM is
- Connect to Mongo via text editor
- Create a Schema for a collection
- Create a model and save it
- Find a specific model
- Update a model already in the database
- Remove a model already in the database
- Combine actions
ODM stand for Object Document Model. It translates the documents in Mongo into upgraded JavaScript Objects that have more helpful methods and properties when used in conjunction with express.
Rather than use the Mongo shell to create, read, update and delete documents, we'll use an npm package called mongoose
. Mongoose will allow us to create schemas, do validations and make it easier to interact with Mongo inside an express app.
A schema will allow us to set specific keys in our objects. So if we have a key of name
, we won't be able to insert other keys that don't match like firstName
or names
. This helps keep our data more organized and reduces the chance of errors.
We can also specify the datatypes. We can set the datatype of name
to a string
, age
to a number
, dateOfBirth
to a Date, bff
to a Boolean etc.
We can also make some fields required and we can set default values as well.
Here is a sample Schema, with many options. We'll be making a smaller variation of this
const articleSchema = new Schema({
title: { type: String, required: true, unique: true }, //can say whether we want properties to be required or unique
author: { type: String, required: true },
body: String,
comments: [{ body: String, commentDate: Date }], // can have arrays of objects with specific properties
publishDate: { type: Date, default: Date.now }, // can set defaults for properties
hidden: Boolean,
meta: { // can have properties that are objects
votes: Number,
favs: Number
}
}, {timestamps: true});
mkdir intro_to_mongoose
cd intro_to_mongoose
touch app.js
npm init -y
and go through the promptsnpm i mongoose
touch tweet.js
code .
Inside app.js
- require mongoose
// Dependencies
const mongoose = require('mongoose');
const Tweet = require('./tweet.js');
- tell Mongoose where to connect with Mongo and have it connect with the sub-database
tweets
(if it doesn't exist, it will be created) - set
mongoose.connection
to a shorter variable name
// Global configuration
const mongoURI = 'mongodb://localhost:27017/tweets';
const db = mongoose.connection;
- Connect to mongo
// Connect to Mongo
mongoose.connect( mongoURI );
- In the Terminal, run
node app.js
Warnings are ok, it'll still work, for now. But in later versions it may stop working and you'll have to update your code.
This should clear up the errors:
mongoose.connect(mongoURI, { useNewUrlParser: true, useUnifiedTopology: true}, () => {
console.log('the connection with mongod is established')
})
- OPTIONAL provide error/success messages about the connections
// Connection Error/Success
// Define callback functions for various events
db.on('error', (err) => console.log(err.message + ' is mongod not running?'))
db.on('connected', () => console.log('mongo connected: ', mongoURI))
db.on('disconnected', () => console.log('mongo disconnected'))
- While the connection is open, we won't have control of our terminal. If we want to regain control, we have to close the connection. Let's set leave the connection open for 5 seconds to demonstrate that the app will hang and then we'll get our close message.
Otherwise we have to press control c
. When we run an express app, we typically want to leave the connection open, we don't need to get control of terminal back, we just let the app run.
// Automatically close after 5 seconds
// for demonstration purposes to see that you must use `db.close()` in order to regain control of Terminal tab
setTimeout(()=>{db.close()}, 5000)
- The entire configuration for mongoose:
- Don't memorize it, just set a bookmark and refer back to this as you need it.
- note the setTimeout was just to demonstrate what
db.close()
does, you don't always need it
// Dependencies
const mongoose = require('mongoose');
const Tweet = require('./tweet.js');
// Global Configuration
const mongoURI = 'mongodb://localhost:27017/tweets';
const db = mongoose.connection;
// Connect to Mongo
mongoose.connect(mongoURI, { useNewUrlParser: true }, () => {
console.log('the connection with mongod is established')
})
// Connection Error/Success - optional but can be helpful
// Define callback functions for various events
db.on('error', (err) => console.log(err.message + ' is Mongod not running?'));
db.on('connected', () => console.log('mongo connected: ', mongoURI));
db.on('disconnected', () => console.log('mongo disconnected'));
In tweet.js
const mongoose = require('mongoose');// require mongoose
const Schema = mongoose.Schema; // create a shorthand for the mongoose Schema constructor
// create a new Schema
// This will define the shape of the documents in the collection
// https://mongoosejs.com/docs/guide.html
const tweetSchema = new Schema({
title: String,
body: String,
author: String,
likes:{ type: Number, default: 0},
sponsored: {type: Boolean, default: false}
}, {timestamps: true});
// Creating Tweet model : We need to convert our schema into a model-- will be stored in 'tweets' collection. Mongo does this for you automatically
// Model's are fancy constructors compiled from Schema definitions
// An instance of a model is called a document.
// Models are responsible for creating and reading documents from the underlying MongoDB Database
// from here: https://mongoosejs.com/docs/models.html
const Tweet = mongoose.model('Tweet', tweetSchema);
//make this exportable to be accessed in `app.js`
module.exports = Tweet;
In app.js
Let's make ourselves an object to insert into our database. When we connect with an express app, our data will be coming in as an object from the browser.
const myFirstTweet = {
title: 'Deep Thoughts',
body: 'Friends, I have been navel-gazing',
author: 'Karolin'
}
Tweet.create(myFirstTweet , (error, tweet) => {
if(error){ //if there is an error console log it
console.log(error);
} else { // else show us the created tweet
console.log(tweet);
}
// get control of terminal back
// else just use control c
db.close();
});
Let's run this with
node app.js
We should see:
Timestamps, deleted, and likes had default values, a unique _id has been generated
Every time we run node app.js
it will run the code, and thus insert this object over and over again. Let's not do that. Let's comment it out.
Let's insert many more tweets
const manyTweets = [
{
title: 'Deep Thoughts',
body: 'Friends, I have been navel-gazing',
author: 'Karolin'
},
{
title: 'Sage Advice',
body: 'Friends, I am vegan and so should you',
author: 'Karolin',
likes: 20
},
{
title: 'Whole Reality',
body: 'I shall deny friendship to anyone who does not exclusively shop at Whole Foods',
author: 'Karolin',
likes: 40
},
{
title: 'Organic',
body: 'Friends, I have spent $2300 to be one of the first people to own an organic smartphone',
author: 'Karolin',
likes: 162
},
{
title: 'Confusion',
body: 'Friends, why do you just respond with the word `dislike`? Surely you mean to click the like button?',
author: 'Karolin',
likes: -100
},
{
title: 'Vespa',
body: 'Friends, my Vespa has been upgraded to run on old french fry oil. Its top speed is now 11 mph',
author: 'Karolin',
likes: 2
},
{
title: 'Licensed',
body: 'Friends, I am now officially licensed to teach yogalates. Like this to get 10% off a private lesson',
author: 'Karolin',
likes: 3
},
{
title: 'Water',
body: 'Friends, I have been collecting rain water so I can indulge in locally sourced raw water. Ask me how',
author: 'Karolin',
},
];
Let's insert all these tweets:
Tweet.insertMany(manyTweets, (error, tweets ) => {
if(error){
console.log(error)
} else {
console.log(tweets);
} db.close()
});
node app.js
and let's comment it out so we don't insert duplicates
- Mongoose has 4 methods for this
find
- genericfindById
- finds by ID - great for Show routes!findOne
- limits the search to the first document foundwhere
- allows you to build queries, we won't cover this today
Let's find all
Tweet.find((err, tweets) => {
console.log(tweets);
db.close()
})
Let's limit the fields returned, the second argument allows us to pass a string with the fields we are interested in:
Tweet.find({}, 'title body', (err, tweets) => {
console.log(tweets);
db.close();
})
Let's look for a specific tweet:
Tweet.find({title: 'Water'}, (err, tweet) => {
console.log(tweet);
db.close();
})
We can also use advanced query options. Let's find the tweets that have 20 or more likes
Tweet.find({ likes: { $gte: 20 }}, (err, tweets) => {
console.log(tweets);
db.close();
});
We have two copies of our first tweet and a few options to delete it
.remove()
danger! Will remove all instances.findOneAndRemove()
- this seems like a great choice.findByIdAndRemove()
- finds by ID - great for delete routes in an express app!
Tweet.findOneAndRemove({ title: 'Deep Thoughts' }, (err, tweet) => {
if (err){
console.log(err);
} else {
console.log('This is the deleted tweet:', tweet);
}
db.close()
})
Finally, we have a few options for updating
.update()
- the most generic one.findOneAndUpdate()
- Let's us find one and update it.findByIdAndUpdate()
- Let's us find by ID and update - great for update/put routes in an express app!
If we want to have our updated document returned to us in the callback, we have to set an option of {new: true}
as the third argument
Tweet.findOneAndUpdate({ title: 'Vespa' }, { sponsored: true }, { new: true }, (err, tweet) => {
if (err){
console.log(err);
} else {
console.log(tweet);
}
db.close()
})
We'll see the console.logged tweet will have the value of sponsored updated to true. Without {new: true}
we would get the original unaltered tweet back.
We can count how many tweets we have with likes greater than 20
Tweet.countDocuments({ likes: { $gte: 20 } }, (err, tweets) => {
console.log(tweets);
db.close();
});
We can check out all the things we can do at the Mongoose API docs
Mongoose 5.0.0 just came out on January 17, 2018 It has an updated query builder that chains much like jQuery.
Do a search, limit the number of returned queries to 2, sort them by title
Tweet.find({ likes: { $gte: 20 } }, 'title -_id')
.limit(2)
.sort('title')
.exec((err, tweets) => {
console.log(tweets);
db.close();
});