mongodb-cheatsheet/README.md
2021-09-15 12:30:13 -05:00

333 lines
7.5 KiB
Markdown

This are my personal notes for MongoDB.
If something is unaccurate please let me know.
# Concepts
## Databases
- A container of collections
- Each database has it own filesystem
- A cluster can have multiple databases
## Collections
- A group of documents
- It's similar to the tables on a relational database
- Doesn't need a schema
## Documents
- An entry of a document
- Similar to JSON (BSON (binary JSON))
- Basic unit inside MongoDB
- They can't be more than 16MB
## Drivers
Libraries that we use to communicate from our programming language to the database.
For example the Mongo drivers for Node.js
## Traversal startup for most languages
Create MongoClient connection -> Get MongoDatabase -> get a collection -> CRUD
## Data types
- `ObjectId`: `ObjectId("6141fc6e672cb3a30ee6711d")`
- `String`: `"Some text"`
- `Boolean`: `True` | `False`
- `Date`: `ISODate("2020-02-18T")`
- `Number`: `Double`, `Integer 32 bits`, `Integer 64 bits`, `Decimal`
- `Embedded sub-document`: `{}`
- `Arrays`: `[]`
[More types](https://docs.mongodb.com/manual/reference/bson-types/)
## Schemas and relations
Mongo doesn't require schema or relations but if you need to, you can have them.
`One-to-many` could be an ID that points to another ID but this is way too "SQL" style. The other approach is to have an embeded document.
`Many-to-many` is also the same concept but with arrays.
The issue is that we might have to update each embedded document in case some information changes. It is better to use references in this case.
[More about relationships here](https://docs.mongodb.com/manual/applications/data-models-relationships/)
You can also have JOIN-like queries with `$lookup`, learn more [here](https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#mongodb-pipeline-pipe.-lookup)
# Projections
Sometimes we don't need the whole document, just a few data, so we can select just the data we need in the second parameter like so:
```
> db.pokemons.findOne(
{_id: ObjectId("ABC...")}, // Filter
{ // Projection
name: 1
}
)
```
Will return:
```
{ "_id" : ObjectId("6141fc6e672cb3a30ee6711d"), "name" : "Pikachu" }
```
The `_id` will be always selected by default. If we don't want the `_id` we can specify `_id: 0`
# Operators
Syntax:
```
{ <field1>: {<operator1>: <value1>}, ... }
```
Example:
```
> db.pokemons.find({type: {$in: ["grass", "fire"]}})
```
Where `$in` is the operator
Comparison operators:
- `$eq`: `=`
- `$gt`: `>`
- `$gte`: `>=`
- `$lt`: `<`
- `$lte`: `<=`
- `$ne`: `!=`
- `$in`: Values inside of an array
- `$nin`: Values NOT inside of an array
Logical operators:
- `$and`: Logical AND
- `$or`: Logical OR
- `$not`: Logical NOT
- `$nor`: Logical NOT OR
Element operators:
- `$exist`: Documents that have an specific field
- `$type`: Documents that have an specific field type
Array operators:
- `$all`: Array that have all specified values
- `$elemMatch`: Query for arrays containing sub-documents
- `$size`: Arrays with specific length
[More operators here](https://docs.mongodb.com/manual/reference/operator/)
# Commands
## Connect to your local mongo instance:
```
mongo
```
## Connect to a remote instance
```
mongo "mongodb+srv://HOST:PORT/DATABASE" --username yourusername
```
It will prompt for your password
Show the databases
```
> show dbs
```
## Switch to the "gamefreak" database
```
> use gamefreak
```
## Show the current database
```
> db
```
## Show the collections of the current database
```
> show collections
```
Get help of the commands of the collection
```
> db.pokemons.help()
```
## Insert to the "pokemons" collection.
If the collection doesn't exists, Mongo will create it.
```
> db.pokemons.insertOne({
name: "Pikachu",
pokedexId: 1,
type: ["electric", "normal"]
})
```
And it will return something like this:
```
{
"acknowledged" : true,
"insertedId" : ObjectId("6141fc6e672cb3a30ee6711d")
}
```
You could specify an `_id`, but it's a good practice to leave Mongo to create it own ID, since they must be unique.
Insert many
```
> db.pokemons.insertMany([
{
name: "Bulbasaur",
pokedexId: 2,
type: ["plant"]
},
{
name: "Charmander",
pokedexId: 3,
type: ["fire"]
},
])
```
Will return something like:
```
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("61420dea672cb3a30ee6711e"),
ObjectId("61420dea672cb3a30ee6711f")
]
}
```
## Update
First parameter is the filter and the second is the data
```
> db.pokemons.updateOne(
{_id: ObjectId("61420dea672cb3a30ee6711e")}, // Filter
{ // data
$set: {
level: 50
}
}
)
```
Will return:
```
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
```
You can also use `.update()` to update many instead of just one
## Updating arrays
We can use `$addToSet` to insert a value into an array and `$pull` to remove it
```
> db.pokemons.updateOne(
{_id: ObjectId("61420dea672cb3a30ee6711e")}
{
$addToSet: {type: "normal"}
}
)
```
Will result in:
```
{
"_id" : ObjectId("61420dea672cb3a30ee6711e"),
"name" : "Bulbasaur",
"pokedexId" : 1,
"type" : [
"plant",
"normal"
],
"level" : 50
}
```
And using `$pull` instead results in:
```
{
"_id" : ObjectId("61420dea672cb3a30ee6711e"),
"name" : "Bulbasaur",
"pokedexId" : 1,
"type" : [
"plant"
],
"level" : 50
}
```
## Delete
`.deleteMany()` or `.deleteOne()` will receive one parameter which is the filter
```
> db.pokemons.deleteOne({_id: ObjectId("61420dea672cb3a30ee6711e"))
```
## Finding
Find many (the `.find()` could be empty if you want to fetch all)
with the specified condition. The `.pretty()` is not necessary, is just for a better visual.
```
> db.pokemons.find({pokedexId: 1})
```
Will return
```
{
"_id" : ObjectId("6141fc6e672cb3a30ee6711d"),
"name" : "Pikachu",
"pokedexId" : 1
}
{
"_id" : ObjectId("61420dea672cb3a30ee6711e"),
"name" : "Bulbasaur",
"pokedexId" : 1,
"type" : [
"plant"
]
}
```
Find one on the "pokemons" collection.
```
> db.pokemons.findOne()
```
You can also specify filters. It's important to specify the ObjectId
```
> db.pokemons.findOne({_id: ObjectId("6141fc6e672cb3a30ee6711d")})
```
Returns:
```
{
"_id" : ObjectId("6141fc6e672cb3a30ee6711d"),
"name" : "Pikachu",
"pokedexId" : 1
}
```
## AND operation
Find the pokemon with a level lower than 50
```
> db.pokemons.find({
level: {
$lte: 50
}
})
```
`$lte`: Less than
It will return `null` if nothing was found
## Count the documents returned
```
> db.pokemons.find({pokedexId: 1}).count()
```
Returns:
```
2
```
## .limit(), .skip() and .sort()
This doesn't need that much explanation, it works similar to SQL's LIMIT, OFFSET and ORDER BY
```
> db.pokemons.find({}).limit(10).skip(10).sort({pokedexId: "desc"})
```
[More SQL vs Mongo comparisons](https://docs.mongodb.com/manual/reference/sql-aggregation-comparison/)
## Aggregations $match, $group, $sum, $avg, $multip
You can create complex queries with aggregations
Example if we want to group by level:
```
> db.pokemons.aggregate([
{
$match: { // This is kinda like the "SQL WHERE". Here will be the filters
$or: [{name: "Bulbasaur"}, {name: "Charmander"}]
}
},
{
$group: { // GROUP BY name and SUM the levels
_id: "$name",
total: {$sum: "$level"}
}
}
])
```
Returns:
```
{ "_id" : "Bulbasaur", "total" : 10 }
{ "_id" : "Charmander", "total" : 20 }
```
[More SQL vs Mongo examples](https://docs.mongodb.com/manual/reference/sql-aggregation-comparison/)