331 lines
7.3 KiB
Markdown
331 lines
7.3 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.
|
||
|
|
||
|
# 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/)
|