Professional Documents
Culture Documents
Mongo will be separated from the outside world (Internet) by a web server (Node),
so this web server will filter any request before it hosts our database
Mongo allows us to have multiple internal databases within a single Mongo instance
to be able to work on multiple projects with no additional setups
Each database could have more than one Collection, its the core unit of what
stores data inside of a Mongo database, each collection is related to single type
of resources that will be available in our app (no mixing of type of data)
Mocha is the most popular framework for doing any type of javascript testing
around the Node.JS environment
Mongoose won't try to find any instance of Mongo in our local machine, instead we
need to hard-code it: mongoose.connect('mongodb://localhost:port/db_name');
if 'db_name' does not exist, it will be created (node test/test_helper.js)
Model represents all of the records that sit inside of the related collection
Schema describe exact properties of each record in the collection and their types
Mocha gives us global access to describe() & it() functions (unlike assert()):
const assert = require("assert");
describe("test name", () => { it("instruction", () => { assert(1+1 === 2); }); });
a hook is a function that will be executed before any test get executed:
we need to use hook to clean-up database before any test:
beforeEach((done) => {mongoose.connection.collections.users.drop(() => {done()})})
must call for done() to indicate that drop operation is done & can pass to test
using beforeEach() INSIDE describe() make it run only before each test inside it.
an instance of a model have an id even before it's been saved (unlike other ORMs)
it("using create()", (done) => { joe = new User({ name: "Joe" });
User.create(joe).then((res) => { assert(res.name === "Joe"); done(); }); });
==> alternative way of adding a record using collection.create(instance)
==> old vs new syntax of removing a record using an instance need to get rid of
done() to avoid error: Specify a callback (done) *or* return a Promise; not both.
User.findOneAndDelete({ name: 'joe' }).then( ... ==> remove only 1st occurence
User.findByIdAndDelete(joe._id).then( ... ==> remove by id
avoid loading data from Mongo into our server as much as possible to enhance
performance, in particular cases we will rely on Mongo to make changes instead of
our server using Mongo update operators
... = new Schema({ name: { type: String, required: [true, "required"] }, ...
it("requires a user's name", () => {
const user = new User({ name: undefined });
const validationResult = user.validateSync();
const { message } = validationResult.errors.name;
assert(message === "Name is required"); });
when embedding a collection as a new field in a Model we don't don't need to make
another Model for it, we only need to make a Schema (array of nested resource)
module.exports = PostSchema;
PostSchema = require('./post');
... = new Schema({ ..., posts: [PostSchema]};
==> import the sub-document and used as value of a new field
Virtual type is any field on a Model that does not get persistant over MongoDB
database it does not get saved to db, its a derivative of other field(s)
to make mocha ignore a test we rename it() to become xit() so it won't be executed
when its not necessary true that an embedded document is related directly to the
main document (think about a comment made by user1 on post made by user2), maybe
its better to use a separated collection (related by ids):
pros: easy to find list of embedded collection related to specific main collection
cons: touch db multiple times (no JOIN operation in MongoDB)
to run a single test & ignore the rest replace conserned it() to it.only()
to load particular nested data of a Model instance we use the "modifier" populate:
Model.query( criteria ).populate('name_of_field_as_string').then()
User.findOne({ name: 'Joe' }}).populate('blogPosts').then(user => {
assert(user.blogPosts[0].title = 'title'); done(); });
we can only load ALL nested data EXPLICITLY, Mongo don't provide a way for it:
UserSchema.pre('remove', function(next) {
const BlogPost = mongoose.model('blogPost');
BlogPost.remove({ _id: { $in: this.blogPosts } })
.then(() => next()); });
we didn't require the subdocument's related Model in current Model file to prevent
cyclic requires, instead we load it inside a callback function that in theory will
be called after the app first loaded.
'this' refer to instance of the Model, $in emulating loops but out of our server.
all different Models can have their own middlewares.
next() indicate that current middleware is finished executing and call for next
one if there's one otherwise run the event (similar for .post())
describe('Middleware', () => {
beforeEach(done => { ... Promise.all([joe.save(), blogPost.save()]) then done})
it('clining up dangling data on remove', done => {
joe.remove().then(() => BlogPost.count())
.then(count => { assert(count === 0); done(); }) }); });
we use skip() & limit() when we want to query a particular number of records from
a collection strating from a particular index:
==> since its not guaranteed that index of records follow the order which they get
odered to be saved by the server, we need to sort all of the records before using
skip() function, its object parameter has the field for records to be sorted by &
its value describe the way (ascending > 0 vs descending < 0) its gonna be sorted
better use Number type for money units for calculation purpose
* Index is a system that Mongo uses to make very efficient queries whenever we're
looking for dara (by default an index is automatically created for any new added
collection thus findById() will run faster)
all shell commands are listed at the end of this file (including indexes)
db.artists.createIndex({ name: "text" })
it works when searching an entire word as part of text but not with a part of word
before updateOne() & updateMany() we used to add extra option to tell Mongo that
we need to update multiple records:
(_ids) => C.update({ _id: { $in: _ids } }, { retired: true }, { multi: true } )};
cannot use count() before skip() & limit() (for pagination), so we 2 diff. queries
==> return Promise.all([query, C.find(buikdQuery(criteria)).count()]).then( ...
# MongoDB Shell
```
show dbs
```
```
db
```
```
use acme
```
## Drop
```
db.dropDatabase()
```
## Create Collection
```
db.createCollection('posts')
```
## Show Collections
```
show collections
```
## Insert Row
```
db.posts.insert({
title: 'Post One',
body: 'Body of post one',
category: 'News',
tags: ['news', 'events'],
user: {
name: 'John Doe',
status: 'author'
},
date: Date()
})
```
```
db.posts.insertMany([
{
title: 'Post Two',
body: 'Body of post two',
category: 'Technology',
date: Date()
},
{
title: 'Post Three',
body: 'Body of post three',
category: 'News',
date: Date()
},
{
title: 'Post Four',
body: 'Body of post three',
category: 'Entertainment',
date: Date()
}
])
```
```
db.posts.find()
```
## Get All Rows Formatted
```
db.posts.find().pretty()
```
## Find Rows
```
db.posts.find({ category: 'News' })
```
## Sort Rows
```
# asc
db.posts.find().sort({ title: 1 }).pretty()
# desc
db.posts.find().sort({ title: -1 }).pretty()
```
## Count Rows
```
db.posts.find().count()
db.posts.find({ category: 'news' }).count()
```
## Limit Rows
```
db.posts.find().limit(2).pretty()
```
## Chaining
```
db.posts.find().limit(2).sort({ title: 1 }).pretty()
```
## Foreach
```
db.posts.find().forEach(function(doc) {
print("Blog Post: " + doc.title)
})
```
```
db.posts.findOne({ category: 'News' })
```
```
db.posts.find({ title: 'Post One' }, {
title: 1,
author: 1
})
```
## Update Row
#upsert option: if doc not fount, create it
```
db.posts.update({ title: 'Post Two' },
{
title: 'Post Two',
body: 'New body for post 2',
date: Date()
},
{
upsert: true
})
```
```
db.posts.update({ title: 'Post Two' },
{
$set: {
body: 'Body for post 2',
category: 'Technology'
}
})
```
```
db.posts.update({ title: 'Post Two' },
{
$inc: {
likes: 5
}
})
```
## Rename Field
```
db.posts.update({ title: 'Post Two' },
{
$rename: {
likes: 'views'
}
})
```
## Delete Row
```
db.posts.remove({ title: 'Post Four' })
```
## Sub-Documents
```
db.posts.update({ title: 'Post One' },
{
$set: {
comments: [
{
body: 'Comment One',
user: 'Mary Williams',
date: Date()
},
{
body: 'Comment Two',
user: 'Harry White',
date: Date()
}
]
}
})
```
```
db.posts.find({
comments: {
$elemMatch: {
user: 'Mary Williams'
}
}
}
)
```
## Add Index
```
db.posts.createIndex({ title: 'text' })
```
## Text Search
output: Post One
```
db.posts.find({
$text: {
$search: "\"Post O\""
}
})
```
```
db.posts.find({ views: { $gt: 2 } })
db.posts.find({ views: { $gte: 7 } })
db.posts.find({ views: { $lt: 7 } })
db.posts.find({ views: { $lte: 7 } })
```