Terms
Database -> Collections -> Documents -> Fields
Concepts
Sharding (Data Partition, Horizontal Partition):
Breaks up the data among multiple servers.
Each shard is held on a separate database server instance, to spread the load.
The meta-information of where the data lives (in which Shard) is kept in a Config Server.
Replication (Replica Set):
Maintain a copy of all data in multiple servers.
Replicates your data on any server in a set.
When you connect, your queries are automatically delegated to one of the members in a set.
Writes are run on the Primary server/instance, and reads will usually run on any of the Secondary servers/instances.
To download MongoDB image
docker image pull mongo
docker image pull mongo:4.4.19
To look at MongoDB version and exposed ports
docker image inspect mongo
To run the container
docker container run -d --name mongo-on-docker -p 27017:27017 mongo
Let Docker manage the storage of your database data by writing the database files to disk on the host system using its own internal volume management. This is the default and is easy and fairly transparent to the user.
docker container run -it --name mongo-on-docker -p 27017:27017 -v /Users/percyvega/WORK/MongoDB/learning-mongodb/_data/db:/data/db mongo
Create a data directory on the host system (outside the container) and mount this to a directory visible from inside the container. This places the database files in a known location on the host system.
docker container run -it --name mongo-on-docker -p 27017:27017 -v /Users/percyvega/WORK/MongoDB/learning-mongodb:/etc/mongo mongo --config /etc/mongo/mongod.conf
Using a custom MongoDB configuration file.
docker container run -d --name mongo-on-docker -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=example mongo
Creates a new user and set that user's password. This user is created in the admin authentication database and given the role of root, which is a "superuser" role.
With a docker-compose.yml file:
version: '3.1'
services:
mongo:
image: mongo
container_name: mongo-on-docker
volumes:
- ./_data/db:/data/db
ports:
- 27017:27017
To run a command inside the running container
docker container exec -it mongo-on-docker bash
To run mongo commands
mongodb
To exit
exit
To look at the logs inside the container
docker container logs mongo-on-docker
Create your connection string with this format:
mongodb://root:password@localhost:27017/?authSource=admin
db db.getName() show current database show dbs list all available databases use specific_database to use a specific database db.createCollection("recipes") creates a collection in which to store documents db.users.drop() deletes the users collection
db.recipes.insertOne({"title": "Ceviche", cook_time: NumberInt(23)})
If collection doesn't exist, it will be created.
If no _id field is specified, MongoDB driver will generate it.
The _id field must be unique.
db.recipes.insertMany([{...}, {...}, {...}, ...])
count
db.recipes.find({}).count()
db.recipes.find({}) // or db.getCollection("recipes").find({})
db.recipes.find({}).pretty()
db.recipes.find({title:"Ceviche"})
db.recipes.find({cook_time: 23})
db.recipes.find({_id: ObjectId("6174df12fb7fbb6cd75d6e29")})
findOne (returns a natural document, not a cursor)
db.recipes.findOne()
db.recipes.findOne({_id: ObjectId("5e6fd805fa98021236426a24")})
Non-logical operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $regex
These two are equivalent:
db.recipes.find({cook_time: 23})
db.recipes.find({cook_time: {$eq: 23}})
db.recipes.find({cook_time: {$neq: 23}})
db.recipes.find({cook_time: {$gle: 100, $gte: 23}})
db.recipes.find({writtenOn: {$gt: ISODate("2020-02-20T10:15:00Z")}})
db.recipes.find({cook_time: {$gte: 23}, desc: {$regex: /Peru/i}})
>= 32 and desc contains Peru (case-insensitive)
db.recipes.find({version: {$in: ["v1", "v2"]}})
db.recipes.find({name: {$in: [/^The/, /elevate/]}})
Any document with a name that starts with "The" or that contains "elevate"
$in & $nin can be used with regular expressions
db.recipes.find({country: {$regex: /[A-Z]{2,3}/}})
By specifying fields to display (_id will be displayed unless hidden explicitly)
db.recipes.find({}, {"title": 1}).pretty()
list only the title column
db.recipes.find({"title": "Tacos"}, {"title": 0}).pretty()
list all except the title column
$or
db.recipes.find({$or: [{cook_time: {$gte: 23}}, {desc: {$regex: /Peru/i}}]})
db.recipes.find({$or: [{cook_time: {$gte: 15}}, {cook_time: {$lte: 50}}]})
$and
These two are equivalent:
db.recipes.find({$and: [{cook_time: {$gte: 23}}, {desc: {$regex: /ican/i}}]})
db.recipes.find({{cook_time: {$gte: 23}}, {desc: {$regex: /ican/i}})
// if you used the same field with the previous syntax, then the result would be incoherent
These two are equivalent:
db.recipes.find({$and: [{cook_time: {$gte: 15}}, {cook_time: {$lte: 50}}]})
db.recipes.find({cook_time: {$gte: 15, $lte: 50}})
Empty, null and $exists
db.recipes.find({cook_time: ""})
cook_time is an empty space
db.recipes.find({cook_time: null})
cook_time is empty or does not exist (i.e. is not present)
db.recipes.find({cook_time: {$exists: false}})
cook_time does not exist (i.e. is not present)
$type
db.people.find({e_mail: {$type: "string"}})
db.people.find({e_mail: {$type: "null"}})
e_mail is null (not missing)
db.people.find({address: {$type: "object"}})
db.people.find({Favorite_Colors: {$type: "array"}})
sort
db.recipes.find({}).sort({"title": 1})
title ascending
db.recipes.find({}).sort({"title": -1})
title descending
limit
db.recipes.find().limit(3)
skip
db.recipes.find({}).skip(1)
Free Text Search
To support fast text searches on string and arrays of string fields.
It will only work for the fields that are "text" indexed and that are $type: "string"
db.people.createIndex({name: "text", skills: "text"})
db.people.find({$text: {$search: "engineer Percy"}})
text-indexed fields contain any of the words: engineer, Percy
db.people.find({$text: {$search: "Green Red"}})
contain either of these words
db.people.find({$text: {$search: "Green Red"}}, {score: {$meta: "textScore"}}).sort({"score": 1})
sort them by match score desc
db.recipes.find({tags: "mexican"})
all documents with a tags array containing "mexican"
db.recipes.find({tags: {$in: ["easy", "quick"]}})
one of the tags contains easy, quick or both
These two are equivalent:
db.recipes.find({tags: {$all: ["easy", "quick"]}})
db.recipes.find({$and: [{tags: "easy"}, {tags: "quick"}]})
tags contains at least easy and quick, in any order
db.recipes.find({tags: {$size: 1}})
tags contains exactly 1 element
These two are equivalent:
db.recipes.find({"tags.1": {$exists: true}})
db.recipes.find({"tags": {$exists: true, $not: {$size: 0}}}
tags contains at least 1 element
These two are equivalent:
db.recipes.find({emergency_contacts: {$elemMatch: {relationship: "Brother", age: {$gte: 18}}}})
db.recipes.find({"emergency_contacts.relationship": "Brother", "emergency_contacts.age": {$gte: 18}})
emergency_contacts contains at least one object with relationship=Brother and age>=18
Find by Exact Array
db.recipes.find({tags: ["sweets", "easy"]})
tags arrays that are exactly this, and in the exact order
db.people.find({"address.country": "United States", "address.state": "Connecticut"})
The address object has these fields and values
Find by Exact Object
db.people.find({"address": {state: "Connecticut", country: "United States"}})
documents with exactly this (same list of keys, same values, and same order)
It's an atomic operation for a single document
_id fields cannot be modified
$set updates an existing field or creates it with the specified value if it doesn't exist
$set - updates of adds fields
db.recipes.updateOne({title: "Pizza"}, {$set: {title: "Yummy Pizza"}})
db.recipes.updateMany({title: "Pizza"}, {$set: {title: "Yummy Pizza"}})
db.recipes.updateOne({title: "Pizza"}, {$set: {title: "Yummy Pizza", spicy: true}})
db.recipes.updateOne({title: "Pizza"}, {$set: {jalapenos: true}})
sets (or adds, if it doesn't exist) the document field jalapenos
$unset - remove field
db.recipes.updateOne({title: "Pizza"}, {$unset: {jalapenos: 1}})
unsets (removes) the field jalapenos from the document
$inc - Increment
db.recipes.updateOne({title: "Pizza"}, {$inc: {cook_time: 5}})
increment cook_time by 5. The number can also be negative.
$mul - Multiply
db.recipes.updateOne({title: "Pizza"}, {$mul: {cost: 1.5}})
multiplies the field by 1.5, and if it doesn't exist, sets it to 0
replaceOne
db.recipes.replaceOne({_id: "123", {name: "Percy", age: "23"}})
replaces the entire document with the new document, leaving only the key intact.
Works with updateOne, updateMany, and replaceOne
db.getCollection('recipes').updateOne({title: "Ceviches"}, {$set: {prep_time: 8}}, {upsert: true})
if title=Ceviches exist, its prep_time will be set to 8.
if title=Ceviches doesn't exist, a document with _id, title, and prep_time will be inserted.
db.getCollection('recipes').updateOne({title: "Ceviches"}, {$set: {prep_time: 8}, $setOnInsert: {likes_count: 0, desc: "Yet another Peruvian recipe"}}, {upsert: true})
if title=Ceviches doesn't exist, a document with those extra setOnInsert fields will be inserted.
$push - add elements
db.recipes.updateOne({title: "Pizza"}, {$push: {likes: 23}})
will push (add) to the "likes" array the element 23 at the end of the array
db.recipes.updateOne({title: "Pizza"}, {$push: {likes: {$each: [8, 23, 79]}}})
will push (add) to the "likes" array these elements at the end of the array
db.recipes.updateOne({title: "Pizza"}, {$push: {likes: {$each: [8, 23, 79], $position: 0}}})
will push (add) to the "likes" array these elements at the specified index
keep in mind that the $position modifier can only be used with the $each modifier
$pop - remove element
db.recipes.updateOne({_id: "mongo101"}, {$pop: {likes: -1}})
removes from the "likes" array the first element
db.recipes.updateOne({_id: "mongo101"}, {$pop: {likes: 1}})
removes from the "likes" array the last element
$pull - remove elements
db.recipes.updateOne({title: "Pizza"}, {$pull: {ratings: {$lte: 5}})}
will pull (remove) from the "ratings" array all values <= 5
db.recipes.deleteOne({title: "Delete me"})
db.recipes.deleteMany({title: "Delete me"})
db.recipes.bulkWrite([
{insertOne: {document: {title: "MongoDB Conf"}}},
{updateOne: {filter: {_id: 1}, update: {title: {$set: "MongoDB Conference"}}}},
{deleteOne: {filter: {_id: 10}}}
], {ordered: false})
db.recipes.getIndexes()
db.recipes.find({cook_time: 23}).explain("executionStats") // "totalKeysExamined" : 0.0, "totalDocsExamined" : 9.0
db.recipes.createIndex({"cook_time": 1})
db.recipes.find({cook_time: 23}).explain("executionStats") // "totalKeysExamined" : 2.0, "totalDocsExamined" : 2.0
This shows that the index made the execution search only 2 documents intead of 9.
db.user.createIndex({name: 1}, {age: -1})
creates an ascending-order index for name, and a descending-order index for age
db.user.createIndex({email: 1}, {unique: true})
creates an ascending-order index for field email, making it also unique
db.user.getIndexes()
db.user.dropIndex("name.given_1")
specify the actual name of the index, not the field indexed