# Info sul corso & setup
## Slides
0- [Introduzione](https://docs.google.com/presentation/d/1md_fAuSIPrLf_gItNSB9pvqehKNrR9hojglHqSXzZKQ/edit?usp=sharing)
1 - [Installazione](https://docs.google.com/presentation/d/1pYVaASF576BkbHNEK-DCQqTv6iXZsMBtl642P1GCK2U/edit?usp=sharing)
2 - [Shell](https://docs.google.com/presentation/d/1O200ptoHUnmJl7mYjikpeFQHWu7EzMQcSIXVHVhhtKc/edit?usp=sharing)
3 - [CRUD](https://docs.google.com/presentation/d/1NT1aT9nEjHShMGJR4TomtXvl9XtQl7-6_4taFzSEMxc/edit?usp=sharing)
6 - [NO-ACID](https://docs.google.com/presentation/d/12FSX_942Wsu-XklzpK_cST4Y2wFSAlmxYQ56B9v7al0/edit?usp=sharing)
## Organizzazione
si inizia alle 9:00 e si finisce alle 17:00.s
pausa 15 min alle 11:00.
pausa 15 min alle 16:00.
Pausa pranzo 13-14.
Altri due giorni di corso settimana prossima giovedi e venerdi.
Argomento di giovedi: Cluster replica + sharding + riferimenti per locking
Argomento di venerdi: LDAP auth + tsl + riferimenti OpManager https://docs.opsmanager.mongodb.com/current/
## Ambiente di lab (35.180.47.33)
Noi lavoreremo su container https://hub.docker.com/_/mongo
al quale e' stato aggiunto un servizio sshd per potercisi connettere. Quindi e' gia' tutto installato.
Per installare a mano si puo' guardare https://docs.mongodb.com/manual/installation/
in particolare per Red-Hat https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/
Su ambienti Arch si puo' scaricare dal AUR con
```shell
yay -Syu mongo-bin mongo-tools-bin
```
I sorgenti del ambiente di laboratorio si possono trovare su
https://gist.github.com/zommiommy/e2ef69f2464ead75e51576712e377281
## Sito ambienti di lab
L'ambiente e' hostato su una macchina EC2 di AWS:
http://ec2-52-47-116-219.eu-west-3.compute.amazonaws.com:5000/
la password e' mongo2020, creare un container su una porta libera
Poi collegatevi con ssh, putty o mobaXterm
per mettere la tastiera in italiana eseguite
```shell
setxkbmap it
```
## Lanciamo il server
Lanciamo il demone (come porta usate quella successiva a quella del ssh)
```shell
mongod --port 8889
```
Questo rimane in ascolto quindi ci si collega su una seconda instanza e proseguiamo:
Per lanciarlo in detached e che non stampi a schermo:
```shell
mongod --port 8889 > /dev/null &
```
Scarichiamo i dati:
```shell
wget https://atlas-data-lake.s3.amazonaws.com/json/sample_airbnb/listingsAndReviews.json
```
carichiamo i dati
```shell
mongoimport ./listingsAndReviews.json -c=airbnb --port 8889
```
## Lanciamo il client
```shell
mongo --port 8889
```
Ora dovrebbe esserci un nuovo db:
```javascript
show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
test 0.051GB
```
selezioniamo il nuovo db:
```javascript
use test
switched to db test
```
possiamo stampare le collections
```javascript
show collections
airbnb
```
Possiamo fare la prima query:
```javascript
db.airbnb.findOne({})
...DATI...
```
Questa e' equivalente a:
```SQL
SELECT * FROM airbnb LIMIT 1
```
# Prima parte, queries base
Per dati di utility possiamo usare
```javascript
db.serverStatus()
db.stats()
```
Inseriamo un documento:
```javascript
db.airbnb.insertOne({name:"pippo", test:1})
{
"acknowledged" : true,
"insertedId" : ObjectId("5ebd187bfda7af5b364f46b2")
}
```
Cerchiamolo
```javascript
db.airbnb.findOne({name:"pippo"})
{
"_id" : ObjectId("5ebd187bfda7af5b364f46b2"),
"name" : "pippo",
"test" : 1
}
```
```sql
SELECT * FROM airbnb WHERE name = "pippo" LIMIT 1
```
Per vedere esempi di traduzione da SQL a mongo possiamo usare https://docs.mongodb.com/manual/reference/sql-comparison/
Possiamo cercare piu' valori:
```javascript
db.airbnb.find({beds:1}).limit(5).pretty()
```
```sql
SELECT * FROM airbnb WHERE beds = 1 LIMIT 5
```
e possiamo inserire piu' valori
```javascript
db.airbnb.insertMany([{"name":"gino"}, {"name":"giovanni", "test":"ciao"}])
```
Possiamo filtrare le colonne che ci interessano
```javascript
> db.airbnb.find({beds:1}, {beds:1, bedrooms:1})
{ "_id" : "10021707", "bedrooms" : 1, "beds" : 1 }
{ "_id" : "10009999", "bedrooms" : 1, "beds" : 2 }
{ "_id" : "10006546", "bedrooms" : 3, "beds" : 5 }
{ "_id" : "10030955", "bedrooms" : 1, "beds" : 1 }
{ "_id" : "10047964", "bedrooms" : 2, "beds" : 6 }
{ "_id" : "10051164", "bedrooms" : 1, "beds" : 8 }
{ "_id" : "10057447", "bedrooms" : 1, "beds" : 2 }
{ "_id" : "10057826", "bedrooms" : 0, "beds" : 2 }
{ "_id" : "1003530", "bedrooms" : 1, "beds" : 1 }
```
```sql
SELECT beds, bedrooms FROM airbnb WHERE beds = 1
```
per non metter l'id si fa;
```javascript
> db.airbnb.find({}, {beds:1, bedrooms:1, _id:0})
{ "bedrooms" : 1, "beds" : 1 }
{ "bedrooms" : 1, "beds" : 2 }
{ "bedrooms" : 3, "beds" : 5 }
{ "bedrooms" : 1, "beds" : 1 }
{ "bedrooms" : 2, "beds" : 6 }
{ "bedrooms" : 1, "beds" : 8 }
{ "bedrooms" : 1, "beds" : 2 }
{ "bedrooms" : 0, "beds" : 2 }
{ "bedrooms" : 1, "beds" : 1 }
```
```sql
SELECT beds FROM airbnb
```
possiamo aggiungere commenti
```javascript
db.airbnb.find({beds:1, $comment:"Cerco tutte le stanze con un solo letto"}).limit(1).pretty()
```
Per contare quanti risultati abbiamo possiamo fare:
```javascript
db.airbnb.find({beds:1}).count()
```
Oppure possiamo cercare che i valori siano in una lista di valori (equivalentemnte per $nin)
```
db.airbnb.findOne({beds:{$in:[1, 3]}})
```
```sql
SELECT * FROM airbnb WHERE beds IN (1, 3)
```
per cancellare i valori
```javascript
db.airbnb.remove({name:"pippo"})
WriteResult({ "nRemoved" : 1 })
```
La creazione di collections e db e' implicita
```javascript
> use db_di_test
switched to db db_di_test
> db.airbnb2.insertOne({name:"pippo"})
{
"acknowledged" : true,
"insertedId" : ObjectId("5ebd1fe5fd24da789c9a3f04")
}
> show collections
airbnb2
> show dbs
admin 0.000GB
config 0.000GB
db_di_test 0.000GB
local 0.000GB
test 0.052GB
```
Aggiungiamo il campo "age" con valore 22 ad un documento con beds = 1
```javascript
db.airbnb.updateOne({beds:1}, {$set:{age:22}})
```
Possiamo rinominare i campi:
```javascript
db.airbnb.updateMany({beds:1}, {$rename:{beds:"letti"}})
{ "acknowledged" : true, "matchedCount" : 2699, "modifiedCount" : 2698 }
```
Possiamo cancellare campi a tutti i documenti
```javascript
> db.airbnb.updateMany({}, {$unset:{age:null}})
{ "acknowledged" : true, "matchedCount" : 5558, "modifiedCount" : 2700 }
> db.airbnb.findOne({beds:1}, {beds:1, age:1})
{ "_id" : "10030955", "beds" : 1 }
```
Null e' un placeholder che viene "ignorato" se usato "nella select"
```javascript
> db.airbnb.find({CIAO:null}).count()
5558
> db.airbnb.find().count()
5558
```
Possiamo settare un valore di default:
```javascript
db.airbnb.updateMany({name:{$exists:false}}, {$set:{name:"NOME-NON-PRESENTE"}})
```
Possiamo accedere ai valori di sotto oggetti (servono le viroglette nella chiave)
```javascript
> db.airbnb.findOne({"address.country_code":"US"})
```
Possiamo sottrarre 2 a tutti i valori dei beds
```javascript
> db.airbnb.updateMany({}, {$inc: {beds: -2 }})
{ "acknowledged" : true, "matchedCount" : 5558, "modifiedCount" : 5558 }
```
Troviamo tutti i documenti con beds negativi
```javascript
> db.airbnb.find({beds:{$lt:0}}).count()
```
in generale abbiamo gli operatori:
1. $lt <
2. $lte <=
3. $gt >
4. $gte >=
5. $eq ==
6. $ne !=
Chiarificazione sui null
```javascript
/// nella insert i null si comportano normalmente
> db.airbnb.insertOne({"name":"rita", test:null})
{
"acknowledged" : true,
"insertedId" : ObjectId("5ebd432a5e8da77ec546fedc")
}
/// infatti vengono assegnati
> db.airbnb.findOne({"name":"rita"})
{
"_id" : ObjectId("5ebd432a5e8da77ec546fedc"),
"name" : "rita",
"test" : null
}
/// qui invece i null viene ignorato
> db.airbnb.find({test:null}).count()
5557
/// cosi cerchiamo i documenti dove test e' null
> db.airbnb.find({test:{$type:10}}).count()
1
```
## Esercizi prima parte:
1. Una query che ritorna tutti i risultati
2. Una query che ritorna name, summary e property_type
3. Come sopra ma senza _id
4. Una query che ritorna tutti i documenti con property_type = "Apartment"
5. Come sopra ma solo 5 documenti
6. Conta quanti documenti hanno bed_type = "Real Bed"
7. Una query che conta quanti documenti ci sono 2 o 5 bedrooms
8. Fare una query che setta age = 18 come default se il campo age non e' presente
9. Contare quanti documenti hanno un number_of_reviews inferiore a 5
10. Trovare i documenti che non hanno il campo availability
11. Trovare 5 documenti che hanno come suburb "Brooklyn" dentro al address
12. Trovare tutti i documenti che hanno nel address, dentro a location, is_location_exact uguale a true
13. Troviare tutti i documenti dove minimum_nights sia "14" e maximum_nights sia "1125"
14. Trovare tutti i documenti dove i campi minimum_nights e maxnimum_nights sono di tipo stringa, i valori numerici dei tipi si possono trovare [a questo url](http://bsonspec.org/spec.html) o [qua (Piu' leggibile)](https://docs.mongodb.com/manual/reference/operator/query/type/)
15. Come sopra ma di tipo Double
16. Come sopra ma di tipo 64-bit integer
17. Cancellare il campo notes da tutti i documenti con numero di accommodates maggiore di 3
18. Trovare tutti i documenti con cancellation_policy uguale a "flexible", un numero di beds superiore a 3, property_type uguale ad "Apartment" e country_code sia "US"
19. Cosa fa `db.airbnb.find({notes:{$type:8}})` ?
## Soluzioni
1.
```javascript
db.airbnb.find()
```
o equivalentemente
```javascript
db.airbnb.find({})
```
2.
```javascript
db.airbnb.find({}, {name:1, summary:1, property_type:1})
```
3.
```javascript
db.airbnb.find({}, {_id:0, name:1, summary:1, property_type:1})
```
4.
```javascript
db.airbnb.find({property_type:"Apartment"})
```
5.
```javascript
db.airbnb.find({property_type:"Apartment"}).limit(5)
```
6.
```javascript
db.airbnb.find({bed_type:"Real Bed"}).count()
```
7.
```javascript
db.airbnb.find({bedrooms:{$in:[2, 5]}}).count()
```
8.
```javascript
db.airbnb.updateMany({age:{$exists:false}}, {$set:{age:18}})
```
9.
```javascript
db.airbnb.find({number_of_reviews:{$lt:5}})
```
10.
```javascript
db.airbnb.find({availability:{$exists:false}})
```
11.
```javascript
db.airbnb.find({"address.suburb":"Brooklyn"})
```
12.
```javascript
db.airbnb.find({"address.location.is_location_exact":true})
```
13.
```javascript
db.airbnb.find({minimum_nights:"14", maximum_nights:"1125"})
```
14.
```javascript
db.airbnb.find({minimum_nights:{$type:2}, maximum_nights:{$type:2}})
```
15.
```javascript
db.airbnb.find({minimum_nights:{$type:1}, maximum_nights:{$type:1}})
```
16.
```javascript
db.airbnb.find({minimum_nights:{$type:18}, maximum_nights:{$type:18}})
```
o equivalentemente
```javascript
db.airbnb.find({minimum_nights:{$type:0x12}, maximum_nights:{$type:0x12}})
```
17.
```javascript
db.airbnb.updateMany({accomodates:{$gt:3}}, {$unset:{notes:null}})
```
18.
```javascript
db.airbnb.find({
cancellation_policy:"flexible",
beds:{$gt:3},
property_type:"Apartment",
"address.country_code":"US"
})
```
19.
equivale a cercare se il valore e' boolean
# Parte due, modifichiamo i dati
Possiamo moltiplicare il valore dei documenti che hanno beds = 1, (modifica il campo originale)
```javascript
db.airbnb.update({beds:1}, {$mul:{beds:2}})
```
Se lo faccio su campi non esistenti:
```javascript
> db.airbnb.update({beds:1}, {$mul:{qty:2, qty2:3}})
> db.airbnb.findOne({qty:{$exists:true}})
"qty":0,
"qty2":0
```
Quindi se non esiste usa il valore 0 di default
Un altro esempio piu' semplice:
```javascript
> db.airbnb.findOne({_id:1})
{ "_id" : 1, "highScore" : 900, "lowScore" : 200 }
> db.airbnb.update({_id:1}, {$mul:{lowScore:2}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.airbnb.findOne({_id:1})
{ "_id" : 1, "highScore" : 900, "lowScore" : 400 }
```
Operazione di max (equivalentemente per min)
```javascript
> db.airbnb.insertOne({_id:1, highScore: 800, lowScore:200})
{ "acknowledged" : true, "insertedId" : 1 }
> db.airbnb.findOne({_id:1})
{ "_id" : 1, "highScore" : 800, "lowScore" : 200 }
> db.airbnb.update({_id:1}, {$max:{highScore:900}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }))
> db.airbnb.findOne({_id:1})
{ "_id" : 1, "highScore" : 900, "lowScore" : 200 }
```
altro esempio
```javascript
> db.airbnb.findOne({_id:1})
{ "_id" : 1, "highScore" : 900, "lowScore" : 400 }
> db.airbnb.update({_id:1}, {$max:{lowScore:500}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.airbnb.findOne({_id:1})
{ "_id" : 1, "highScore" : 900, "lowScore" : 500 }
```
Possiamo **sostituire** un documento:
```javascript
> db.airbnb.findOne({_id:1})
{ "_id" : 1, "highScore" : 900, "lowScore" : 200 }
> db.airbnb.replaceOne({_id:1}, {_id:1, highScore:800, lowScore:200})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.airbnb.findOne({_id:1})
{ "_id" : 1, "highScore" : 800, "lowScore" : 200 }
```
questo cancella tutto il vecchio documento
```javascript
> db.airbnb.findOne({_id:1})
{ "_id" : 1, "highScore" : 800, "lowScore" : 200 }
> db.airbnb.update({_id:1}, {$set:{test:10}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.airbnb.findOne({_id:1})
{ "_id" : 1, "highScore" : 800, "lowScore" : 200, "test" : 10 }
> db.airbnb.replaceOne({_id:1}, {_id:1, highScore:800, lowScore:200})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.airbnb.findOne({_id:1})
{ "_id" : 1, "highScore" : 800, "lowScore" : 200 }
```
Se settiamo upsert come settings il nuovo documento viene inserito anche se nessun documento matcha la query.
```javascript
> db.airbnb.replaceOne({beds:"ciao"}, {name:"my_test", test:1}, {upsert:true})
{
"acknowledged" : true,
"matchedCount" : 0,
"modifiedCount" : 0,
"upsertedId" : ObjectId("5ebd4ec5c8b492c39c3336e4")
}
```
Multiple condizioni su un campo:
```javascript
> db.airbnb.find({beds:{$gt:2, $ne:3}}, {beds:1}).limit(1).pretty()
{ "_id" : "10047964", "beds" : 4 }
```
combiniamo condizioni con l'$and (equivalentemente con $or, $nand, $nor, $xor)
```javascript
> db.airbnb.find({$and:[{beds:{$gt:2}}, {beds:{$ne:3}}]}, {beds:1}).limit(1).pretty()
{ "_id" : "10047964", "beds" : 4 }
> db.airbnb.find({$and:[{beds:{$gt:2}}, {bedrooms:{$lt:2, $ne:0}}]}, {beds:1, bedrooms:1}).limit(1).pretty()
{ "_id" : "10051164", "bedrooms" : 1, "beds" : 6 }
```
O invertire una condizione:
```javascript
> db.airbnb.find({$and:[{beds:{$not:{$gt:2}}}, {bedrooms:{$lt:2, $ne:0}}]}, {beds:1, bedrooms:1}).limit(1).pretty()
{ "_id" : "10021707", "bedrooms" : 1, "beds" : -4 }
```
Possiamo usare $where per usare funzioni di javascript come condizioni arbitrarie:
```javascript
var test = function(){ return this.beds % 2 == 0; }
db.airbnb.find({$where:test}).count()
```
Pero' e' molto lento perche' la funzione deve essere serializzata, mandata al server e poi poiche' e' una funzione "black-box" il motore non sa cosa fa quindi non vengono utilizzati eventuali indici. Questo comporta che le performance sono **molto** piu' lente rispetto ad una query nativa.
Ad esempio, se i dati stanno in RAM, possiamo scaricare i dati e filtrarli localmente usando il javascript:
```javascript
var x = db.airbnb.find({});
var total = 0;
for(var i = 0; i < x.length(); i++){
if(x[i].beds % 2 == 0)
total++;
}
print(total);
```
Oppure visto che $mod esiste possiamo fare:
```javascript
db.airbnb.find({beds:{$mod:[2, 0]}}).count()
1976
```
Ordiniamo i risultati:
```javascript
> db.airbnb.find({test:{$exists:true}}).sort({name:1})
{ "_id" : "ciao", "name" : "carlo", "test" : 1, "beds" : -2 }
{ "_id" : ObjectId("5ebd1a5104d726496b75f7e4"), "name" : "giovanni", "test" : "ciao", "beds" : -2 }
{ "_id" : ObjectId("5ebd4ec5c8b492c39c3336e4"), "name" : "my_test", "test" : 1 }
{ "_id" : ObjectId("5ebd432a5e8da77ec546fedc"), "name" : "rita", "test" : null }
> db.airbnb.find({test:{$exists:true}}).sort({name:-1})
{ "_id" : ObjectId("5ebd432a5e8da77ec546fedc"), "name" : "rita", "test" : null }
{ "_id" : ObjectId("5ebd4ec5c8b492c39c3336e4"), "name" : "my_test", "test" : 1 }
{ "_id" : ObjectId("5ebd1a5104d726496b75f7e4"), "name" : "giovanni", "test" : "ciao", "beds" : -2 }
{ "_id" : "ciao", "name" : "carlo", "test" : 1, "beds" : -2 }
```
1 per crescente, -1 per decrescente, questo pero' se non c'e' un indice su quel campo puo' richiedere quantita' di RAM proibitive.
## Esercizi Parte Due
1. Trovare tutti i documenti che hanno (piu' di 2 beds ma non 3) oppure (hanno in address, in location, is_location_exact uguale a true e hanno property_type = "Apartment")
2. Contare quanti documenti hanno property_type = "Apartment" o numero di reviews maggiore di 5, ma non entrambe.
3. Moltiplicare per 3 i beds dei documenti che hanno property_type = "Apartment" e per 5 gli altri.
4. Sottrarre 2 al number_of_reviews se presente, se non e' presente, inserire 0 come valore di default.
## Soluzioni
1.
```javascript
db.airbnb.find({$or:[{beds:{$gt:2, $ne:3}, {"address.location.is_location_exact":true, property_type:"Apartment"}}]})
```
2.
```javascript
> db.airbnb.find({$and:[{$nor:[{$and:[{property_type:"Apartment"}, {number_of_reviews:{$gt:5}}]}]}, {$or:[{property_type:"Apartment"}, {number_of_reviews:{$gt:5}}]}]}).count()
```
oppure
```javascript
var condition = function(){
return this.property_type === "Apartment" ^ this.number_of_reviews > 5;
}
db.airbnb.find($where:condition)
```
3.
```javascript
db.airbnb.updateMany({property_type:"Apartment"}, {$mul:{beds:3}}})
db.airbnb.updateMany({property_type:{$ne:"Apartment"}, {$mul:{beds:5}}})
```
4.
```javascript
db.airbnb.updateMany({number_of_reviews:{$exists:true}}, {$inc:{number_of_reviews:-2}}})
db.airbnb.updateMany({number_of_reviews:{$exists:false}, {$set:{number_of_reviews:0}}})
```
# Configurazione
[Link Documentazione](https://docs.mongodb.com/manual/reference/configuration-options/)
[Security Checklist](https://docs.mongodb.com/manual/administration/security-checklist/)
[Strumeto di editing grafico](https://studio3t.com/features/)
Di default il file di config e' in `/etc/mongod.conf`, `/etc/mongod.conf.orig`
Altrimenti possiamo crearci un file dove vogliamo e facciam partire il server con
`mongod --config /path/to/file` o shorthand `mongod -f /path/to/file`
Un file di conf di solito e' fatto circa cosi
```yaml
systemLog:
destination: file
path: "/var/log/mongodb/mongod.log"
logAppend: true
verbosity: 0 # default
quiet: false
traceAllExceptions: true
storage:
journal:
enabled: true
#
processManagement:
fork: true
security:
authorization: "disabled"
javascriptEnabled: true # in produzione potrebbe aver senso disabilitarlo
net:
bindIp: 127.0.0.1 # Per ascoltare su tutto mettere 0.0.0.0, :: oppure *
port: 27017
ipv6: true
bindIpAll: true
maxIncomingConnections: 65536 # default
setParameter:
enableLocalhostAuthBypass: false
```
## Ruoli generici su tutti i database
- `readAnyDatabase`
- `writeAnyDatabase`
- `userAdminAnyDatabase`
e `clusterAdmin` e' l'unione di `clusterManager`, `clusterMonitor`, `hostManager` + il comando `dropDatabase`
ci sono i ruoli specifici per fare backup e restore (separati)
piu' il super user role:
- `root`
## Creiamo un utente root
Creiamo un utente di root (root ed altri ruoli che agiscono su tutti i db sono definiti solo dentro ad admin):
```javascript
use admin
db.createUser({user:"root", pwd:"toor", roles:["root"]})
```
Spegniamo il server con
```javascript
db.adminCommand({shutdown:1, force:true})
```
oppure
```shell
mongod shutdown
```
nel file di confs abilitiamo l'authenticaizone:
```yaml
security:
authorization: "enabled"
```
Rilanciamo il server:
```shell
mongod -f /path/to/file
```
Ci colleghiamo authenticandoci
```shell
mongo --port 8889 --authenticationDatabase "admin" -u "root" -p
```
o alternativamente possiamo fare:
```javascript
use admin
db.auth("root", "toor")
```
Modifichiamo root in modo che possa essere usato solo da localhost
```javascript
db.updateUser("root", {authenticationRestrictions:[{clientSource:["127.0.0.1"]}]})
```
Cambiamo password a root:
```javascript
db.changeUserPassword("root", "password")
```
Prendiamo la lista degli utenti:
```javascript
use admin
db.system.users.find()
```
oppure info sul utente specifico e non mostrare dati semsibili
```javascript
db.getUser("root", {showCredentials:false})
```
Creiamo un secondo user per il backup:
```javascript
> db.createUser({user:"backup", pwd:"password", roles:[{role:"read", db:"test"}]})
Successfully added user: {
"user" : "backup",
"roles" : [
{
"role" : "read",
"db" : "test"
}
]
}
```
Possiamo rimuoverer il ruolo dal user
```javascript
> db.revokeRolesFromUser("backup", [{role:"read", db:"test"}])
```
E infatti se prendiamo le info, il ruolo e' stato rimosso.
```javascript
> db.getUser("backup")
{
"_id" : "admin.backup",
"userId" : UUID("44a3baa1-28cc-46d6-8c02-bd833a245161"),
"user" : "backup",
"db" : "admin",
"roles" : [ ],
"mechanisms" : [
"SCRAM-SHA-1",
"SCRAM-SHA-256"
]
}
```
## Creiamo un ruolo
```javascript
db.createRole({role:"ruolo_su_collection", privileges:[{resource:{db:"test", collection:"airbnb"}, actions:["find"]}], roles:[]})
{
"role" : "ruolo_su_collection",
"privileges" : [
{
"resource" : {
"db" : "test",
"collection" : "airbnb"
},
"actions" : [
"find"
]
}
],
"roles" : [ ]
}
```
## Indici
Creiamo un utente che possa creare indici sul db di test:
```javascript
db.createUser({user:"test", pwd:"test", roles:[{role:"dbAdmin", db:"test"}]})
```
Aggiungiamo i ruoli per vedere e scrivere i dati
```javascript
db.grantRolesToUser("test", [{role:"readWrite", db:"test"}])
```
Controlliamo di avere i permessi:
```javascript
db.getUser("test")
```
Creiamo e controlliamo l'indice (crescente 1, decrescente -1) per beds
```javascript
> db.airbnb.createIndex({beds:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.airbnb.getInd
db.airbnb.getIndexKeys( db.airbnb.getIndexSpecs( db.airbnb.getIndexes( db.airbnb.getIndices(
> db.airbnb.getIndices()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.airbnb"
},
{
"v" : 2,
"key" : {
"beds" : 1
},
"name" : "beds_1",
"ns" : "test.airbnb"
}
]
```
Quindi ora possiamo fare le operazioni di sorting
```javascript
db.airbnb.find({}, {beds:1}).sort({beds:-1}).limit(1)
{ "_id" : "20701559", "beds" : 23 }
```
Creo uno user per compass:
```javascript
db.createUser({user:"compass", pwd:"compass", roles:["root"]})
```
creiamo un indice testuale:
```javascript
> db.airbnb.createIndex({description:"text"})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
```
Creiamo una collection a cui aggiungiamo un validator
```javascript
> db.createCollection("test_coll", {validator:{beds:1}, validationLevel:"strict"})
{ "ok" : 1 }
```
Il validator prende una query e fa la stessa operazione della find e se il nuovo documento matcha allora e' valido.
Inseriamo un documento valido
```javascript
> db.test_coll.insertOne({beds:1})
{
"acknowledged" : true,
"insertedId" : ObjectId("5ebe8e2f5e3702b8782f3438")
}
```
Inseriamo un documento non valido
```javascript
> db.test_coll.insertOne({beds:2})
2020-05-15T12:42:25.443+0000 E QUERY [js] WriteError({
"index" : 0,
"code" : 121,
"errmsg" : "Document failed validation",
"op" : {
"_id" : ObjectId("5ebe8e315e3702b8782f3439"),
"beds" : 2
}
}) :
WriteError({
"index" : 0,
"code" : 121,
"errmsg" : "Document failed validation",
"op" : {
"_id" : ObjectId("5ebe8e315e3702b8782f3439"),
"beds" : 2
}
})
```
Abbiamo due livelli di `validationLevel`, `strict` che applica la validazione a tutto e `moderate` che applica solo alle insert ed agli update di documenti gia' validi.
Possiamo cambiare il livello di una colleciton con:
```javascript
db.runCommand({collMod:"test_coll", {validationLevel:"moderate"}})
```
Se vogliamo che venga sollevato solamente un warning invece che un errore modifichiamo `validationAction`.
```javascript
db.runCommand({collMod:"test_coll", {validationAction:"warn"}})
```
con `$jsonSchema` possiamo fare query sulla struttura dei documenti. Ad esempio:
```javascript
var validator = {
$jsonSchema: {
required: [ "name", "major", "gpa", "address" ],
properties: {
name: {
bsonType: "string",
description: "must be a string and is required"
},
address: {
bsonType: "object",
required: [ "zipcode" ],
properties: {
"street": { bsonType: "string" },
"zipcode": { bsonType: "string" }
}
}
}
}
}
```
Regex siti [Regex101](https://regex101.com/) [debuggerx](https://www.debuggex.com/)
Potremmo ad esempio validare il campo email cosi:
```javascript
{email:{$regex:\w+@\w+\.\w{1,6}}}
```
cosi da imporre che il campo email oltre ad esistere sia anche una email ben formata.
## Schemas
[Esempi](https://docs.mongodb.com/manual/tutorial/model-embedded-one-to-one-relationships-between-documents/)
## Statistiche del server
```shell
mongostat --discover --port 8889
mongotop --port 8889
```
Per visualizzare le operazioni in corso, ed il loro stato, si puo' usare:
```shell
db.currentOp()
```
## Profiling
[exit codes table](https://docs.mongodb.com/manual/reference/exit-codes/)
cerca profiling sul server basandosi sul timestamp:
```javascript
db.system.profile.find({ts:{$gt: new ISODate("2020-05-15T16:00:00Z")}})
```
# Backup
Per beckuppare e' buona norma fare fsync [RIFERIMENTO](https://docs.mongodb.com/manual/reference/command/fsync/)
che forza un flush e blocca tutte le modifiche.
```javascript
db.fsyncLock()
```
poi per sbloccare fare
```javascript
db.fsyncUnlock()
```
Tutti i dati sono di default in `/data/db` ma e' configurabile con
`mongod --dbpath /data/mydb` o nei settings con
```yaml
storage:
dbPath: /data/testdb
```
## Usando MongoDB Tools
mongodump / mongorestore sono binary
mongoexport / mongoimport sono human readable
Creiamo il backup nella cartella specificata
```shell
mongodump --collection=myCollection --db=test --out=/data/backup/
```
Se non passiamo collection e db beckuppa tutto.
```shell
mongorestore /data/backup/dump-2013-10-25/
```
O la versione human readable
```shell
mongoexport --db=test --collection=airbnb --out=backup.json
```
```shell
mongoimport --out=backup.json
```
Ma non sono troppo performani quindi il metodo consigliato e' il seguente
## Filesystem snapshots con LVM
[Riferimenti](https://docs.mongodb.com/manual/tutorial/backup-with-filesystem-snapshots/)
Creiamo un ramdisk e cloniamo la /data/db
Creiamo lo snapshot
```s
lvcreate --size 100M --snapshot --name mdb-snap01 /dev/vg0/mongodb
```
Salviamo e comprimiamo
```s
umount /dev/vg0/mdb-snap01
dd if=/dev/vg0/mdb-snap01 | gzip > mdb-snap01.gz
```
Restoring
```s
lvcreate --size 1G --name mdb-new vg0
gzip -d -c mdb-snap01.gz | dd of=/dev/vg0/mdb-new
mount /dev/vg0/mdb-new /srv/mongodb
```
## Rotate Logging
```js
db.adminCommand( { logRotate : 1 } )
```
# Replica sets
Replica Set = Cluster ad alta affidabilita'
almeno 3 max 50 nodi di cui al massimo 7 votanti.
Nel caso di 3 nodi ci possono essere:
o un principale e 2 secondari
o un principale un secondario ed un arbitro
arbitro server per le votazioni ma non ha dati.
le votazioni vanno a maggioranza e quindi i votanti devono essere dispari, nel caso di server pari (di numero inferiore a 7) si aggiunge un arbitro.
L'arbitro puo' essere solo uno e vota solo in caso di partia', altrimenti non fa nulla.
L'arbitro non salva dati.
Di per se l'arbitro non e' necessario. Perche' anche con un cluster da 2 o 4 possiamo rendere uno dei nodi non votanti e quindi avere un numero di nodi votanti dispari, e quindi possibilita' di fare elezioni.
Pero' un nodo non votante non puo' essere eletto Principale, quindi in realta' cosi andiamo a perdere il possibile utilizzo di quel server, quindi aggiungendo l'arbitro allora anche questo nodo puo' diventare principale.
Nel caso di cluster con piu' di 8 server il problema non si pone perche' tanto il massimo di votanti e' 7.
Write solo al principale
Read a tutti seconda readpreference, se si abilita setSlaveOk
I server mantengono un heartbeat, un ping a cui va risposto.
Se il principale crasha viene eletto uno nuovo, normalmente il timeout e' di circa 12 secondi.
Puo' essere cambiato a `electionTimeoutMillis` ma sopratutto su cluster distribuiti geograficamente la latenza puo' limitare questo valore.
Un sottocaso dei nodi a proprity 0 e' un nodo nascosto.
Si puo' nascondere un nodo, in modo da tenerlo solo di backup ma che nessuno va a leggere o scrivere.
Questi servono principalmente come puro backup poiche' sono totalmente "inutili" ma mantengono i dati aggiornati.
Un sottocaso e' il delayed replica set
In pratica mantiene i dati con ritardo fissato, questi hanno comunque l'operationlog aggiornato, ma eseguono le operazioni dopo un delay
Questo aiuta nel caso di errori umani dove uno cancella un DB.
## Creiamo un piccolo replica set di 3 nodi
Per tutti e 3 i nodi copiamo le conf di default scegliendo porte diverse perche' siamo sulla stessa macchina (1336, 1331, 1332)
```
cp /etc/mongod.conf.orig /etc/mongod.conf
```
E poi modifichiamo questi settings:
```yaml
# Dove salva i dati del db
storage:
dbPath: /data
# porta diversa perche' siamo sulla stessa macchina
net:
port: 1338
bindIp: 0.0.0.0
# Nome del replica set di cui il server fara' parte
replication:
replSetName: "rs0"
# per avere il pad quando lo lanciamo (non necerssaria su server veri
# dove mongod gira come servizio)
processManagement:
fork:true
```
Facciamo partire le instanze con
```shell
mongod --config /etc/mongod.conf
```
Colleghiamo ci col client ad uno qualunque dei nodi:
```
mongo --port 1331
```
Creiamo una variable con le configurazioni del replica sets:
```javascript
rsconf = {
_id: "rs0",
members: [
{
_id: 0,
host: "127.0.0.1:1331"
},
{
_id: 1,
host: "127.0.0.1:1332"
},
{
_id: 2,
host: "127.0.0.1:1336"
}
]
}
```
Ora passiamo queste configurazioni al comando per inizzializzare il replica set
```javascript
rs.initiate(rsconf)
```
e guardiamo le confs con
```javascript
rs.conf()
```
Come test uccidiamo uno dei nodi e guardiamo lo stato del replica set
```javascript
rs.status()
```
e cosi possiamo vedere quale membro e' in stato not reachable / healty.
successivamente possiamo escluderlo dal replica set con:
```javascript
rs.remove("127.0.0.1:1331")
```
Possiamo far diventare secondario un nodo primario
```javascript
rs.stepDown()
```
dopo aver riavviato il nodo possiamo riaggiungerlo con:
```javascript
rs.add("127.0.0.1:1331")
```
per abilitare la possibilita' di leggere da nodi secondari usiamo
```javascript
db.getMongo().setSlaveOk()
```
## Test read only
Se nel cluster a 3 nodi ne killiamo due vediamo che il nodo rimanente diventa secondario e questo andra' in sola lettura perche' non non puo' piu' raggiungere la maggioranza e quindi non puo' eleggere il primario.
```javascript
cfg = rs.conf()
// rimuoviamo l'ultimo nodo
cfg.members.pop()
// alternativamente per rimuovere il nodo al indice index:
// cfg.members.splice(index, 1)
rs.reconfig(cfg, {force:true})
```
Quindi per ritornare a poter scrivere rimuoviamo i due nodi morti (per usare la remove bisogna specificare force a true):
```javascript
cfg = rs.conf()
// rimuoviamo l'ultimo nodo
cfg.members.splice(0, 1)
cfg.members.splice(0, 1)
rs.reconfig(cfg, {force:true})
```
Ora abbiamo un replica set con un solo nodo, e siamo il primario.
Quidni ora si puo' continuare a scrivere.
Successivamente quando fixiamo gli altri nodi, possiamo riaggiungerli con
```javascript
rs.add("127.0.0.1:1331")
```
Per connetterci al replica set "come client" usiamo:
```shell
mongo "mongodb://127.0.0.1:1331,127.0.0.1:1332,127.0.0.1:1336/?replicaSet=rs0"
```
## Oplog
possiamo vederne la dimensione e l'intervallo di tempo recuperabile:
```javascript
rs.printReplicationInfo( )
```
Questa dimensione puo' essere cambiata con: (dimensione in Mb)
```javascript
db.adminCommand({replSetResizeOplog:1, size: 163384})
```
Alternativamente possiamo settare `flowControlTargetLagSeconds` per rallentare le write sul primario nel caso i secondari siano limitati dalla quantita' di banda.
Nel caso di datacenter puo' aver senso far sincronizzare un solo secondario e gli altri nodi farli sicronizzare da questo con:
```javascript
rs.syncFrom("127.0.0.1:1331")
```
Per trovare il primario possiamo fare
```javascript
rs.status().members.filter(node => node.stateStr == "PRIMARY")
```
## Aggiungiamo un altro nodo ed un arbitro
Arbitro (8891), nuovo nodo (8889) con le stesse configuraizoni di prima.
Il nodo normale lo aggiungiamo sempre con:
```javascript
rs.add("127.0.0.1:8889")
```
Poi per aggiungere l'arbitro usiamo:
```javascript
rs.addArb("127.0.0.1:8891")
````
## Mettiamo majoirty come write concern di default
```javascript
conf = rs.conf()
conf.settings.getLastErrorDefaults.w = "majority"
rs.reconfig(conf)
```
ovviamente possiamo utilizzare write concern diverso specificiandolo esplicitamente:
```javascript
db.airbnb.insertOne({test:true, name:"test"}, {w:1})
```
## Una alternativa alla majority
Possiamo fare una query che prima controlla quanti nodi sani ci sono e poi usa quel numero (escluso l'arbitro) come write concern, cosi che le write scrivano sempre su tutti i server vivi.
```javascript
db.testcol.insertOne({test:"test"}, {w:rs.status().members.filter(member => member.health == 1).length - 1})
```
Oppure possiamo fare in modo che ogni membro non attivo sia kickato dal replica set.
```javascript
while true {
rs.status().members.filter(member => member.health == 0).forEach(node => rs.remove(node.name));
sleep(1000);
}
```
## User per replica set
ci colleghiamo al primario e creiamo lo user:
```shell
db.createUser({user:"root", pwd:"toor", roles:["root"]})
```
creiamo una chiave condivisa per tutti i server con:
```shell
openssl rand -base64 741 > mongodb-keyfile
chmod 400 mongodb-keyfile
```
cioe':
```shell
cat mongodb-keyfile
3I+e635TySL8Pghn5F7XSWc6kPTJ4hwqogLhHOv2PbV83VqwvyJ0Gd2pSdF8aFKGqpYvLrenuylvdAXEM/quKD0ECdghpivWGm16CoQi7ePDD3MVE+2JgB5FvtthxoJRsQuxgb4Ci3nxBHcN9IYIomu+zrohNWKrPBGIIkXXueOL2bvgn2mkLrxqjWB37mIs49/kUbWYdh1mIZzJjsm8hjtaWbCnLqNL4a3rmZqwEP+gUSMm7KRf9v2SMd88eKYob108zXvcm3AmJsy27+J+T5HQLxJyihk+BdSVcqDazkCWfYwsHAP2vDz9/sB/S3V4TyQft9jJ2QwDcK6NrqoUIJX4Z5bRqFdVS+wcB+BCmNz6Lf7ESB3pvQDjR3pQOR29qJDnYQDffkbizKRcvJvEwi9mkT06gSPdi+dNgb6k2ct3+Bl1mRiDKHHBSwjNbTJBcVaw65Abrerqd6d32nlyugAMRrQ+UqMTG5cF6KeOf4XeIKWapL57x9CrTzvkmAx8g3cmnV3Hf7L/F2wNyE34CvQLyMOQ7aEzy3GzdRO3QhBbk+S5BAm+aTPke0ggVinUymrh3agIFLuYAE3P67qngZhWyipDFJhw4kl/boWvN9diM/b+KuxGJrmWTguXud6qxK8dmoBIKcBuLHRmsicGZHMj6exgW87+ZtcQX6ePb2wQT9+jzwVCEi0USwCfqtzVmxjqq3X2wdnEBZ+Xd0FW8sGpm79GyB7bo+L0MWaGhV1hNivrCRNfacx98DEIFUecww2BirdwD3wu7zM+HI0atK3AQkF79+6ntyWJy+L7Br8ZfnlRzwIYMLZ04xm1ByBToMP2/o7LVGT/B8PHqTizcLfsKFG858KDlY/kSmnqyVVxE17+tQAlCDZI7X4YsvMBGW+plTkZmaE5AgHIDR93PmBMUD0O8o/Oz9d2Fl2fm+0TuYqno0x0pjXpnV35DAgSJ20NxV3jT7/21+E3cvLSZjR+oiwM
```
poi abilitiamo l'authenticazione nei file di confs di tutti i server (compreso l'arbitro):
```yaml
security:
authorization: "enabled"
keyFile: /root/mongodb-keyfile
```
Riavviamo uno ad uno i server.
Poi connettiamoci authenticandoci con:
```shell
mongo "mongodb://127.0.0.1:1331,127.0.0.1:1332,127.0.0.1:1336,127.0.0.1:8889,127.0.0.1:8891/?replicaSet=rs0" --authenticationDatabase "admin" -u "root" -p
```
Nel caso dia problemi possiamo rimuovere tutti i nodi dal replica set e poi riaggiungerli uno ad uno.
# Sharding
Il piano e': (ssh, port)
- config_server_1 (8000, 8001)
- config_server_2 (8002, 8003)
- config_server_3 (8004, 8005)
- shard_1_1 (8006, 8007)
- shard_1_2 (8008, 8009)
- shard_1_3 (8010, 8011)
- shard_2_1 (8012, 8013)
- shard_2_2 (8014, 8015)
- shard_2_3 (8016, 8017)
- router_1 (8018, 8019)
- router_2 (8020, 8021)
### settings config_server:
```yaml
# Dove salva i dati del db
storage:
dbPath: /data
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
# porta da cambiare perche' siamo sulla stessa macchina
net:
port: 1338
bindIp: 0.0.0.0
# Nome del replica set di cui il server fara' parte
replication:
replSetName: "rscs0"
sharding:
clusterRole: "configsvr"
# per avere il pad quando lo lanciamo (non necerssaria su server veri
# dove mongod gira come servizio)
processManagement:
fork: true
```
### settings shard1:
```yaml
# Dove salva i dati del db
storage:
dbPath: /data
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
# porta da cambiare perche' siamo sulla stessa macchina
net:
port: 1338
bindIp: 0.0.0.0
# Nome del replica set di cui il server fara' parte
replication:
replSetName: "rss1"
sharding:
clusterRole: "shardsvr"
# per avere il pad quando lo lanciamo (non necerssaria su server veri
# dove mongod gira come servizio)
processManagement:
fork: true
```
### settings shard2:
```yaml
# Dove salva i dati del db
storage:
dbPath: /data
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
# porta da cambiare perche' siamo sulla stessa macchina
net:
port: 1338
bindIp: 0.0.0.0
# Nome del replica set di cui il server fara' parte
replication:
replSetName: "rss2"
sharding:
clusterRole: "shardsvr"
# per avere il pad quando lo lanciamo (non necerssaria su server veri
# dove mongod gira come servizio)
processManagement:
fork: true
```
### settings router:
```yaml
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
# porta da cambiare perche' siamo sulla stessa macchina
net:
port: 1338
bindIp: 0.0.0.0
sharding:
configDB: "rscs0/127.0.0.1:8001,127.0.0.1:8003,127.0.0.1:8005"
# per avere il pad quando lo lanciamo (non necerssaria su server veri
# dove mongod gira come servizio)
processManagement:
fork: true
```
### Inizzializziamo il config server
facciamo partire i server con:
```shell
mongod -f conf.yaml
```
poi collegnadoci ad uno qualsiasi dei server di questo replica set:
```shell
mongo --port 8001
```
creiamo le settings:
```javascript
confs = {
_id: "rscs0",
members: [
{_id: 0, host: "127.0.0.1:8001"},
{_id: 1, host: "127.0.0.1:8003"},
{_id: 2, host: "127.0.0.1:8005"},
]
}
```
e facciam partire il replica set:
```javascript
rs.initiate(confs)
```
### Inizzializziamo i replica set 1
facciamo partire i server con:
```shell
mongod -f conf.yaml
```
poi collegnadoci ad uno qualsiasi dei server di questo replica set:
```shell
mongo --port 8007
```
creiamo le settings:
```javascript
confs = {
_id: "rss1",
members: [
{_id: 0, host: "127.0.0.1:8007"},
{_id: 1, host: "127.0.0.1:8009"},
{_id: 2, host: "127.0.0.1:8011"},
]
}
```
e facciam partire il replica set:
```javascript
rs.initiate(confs)
```
### Inizzializziamo i replica set 2
facciamo partire i server con:
```shell
mongod -f conf.yaml
```
poi collegnadoci ad uno qualsiasi dei server di questo replica set:
```shell
mongo --port 8013
```
creiamo le settings:
```javascript
confs = {
_id: "rss2",
members: [
{_id: 0, host: "127.0.0.1:8013"},
{_id: 1, host: "127.0.0.1:8015"},
{_id: 2, host: "127.0.0.1:8017"},
]
}
```
e facciam partire il replica set:
```javascript
rs.initiate(confs)
```
## Configurazione
facciamo partire il router
```shell
mongos -f confs.yaml
```
colleghiamoci al router:
```shell
mongo --port 8019
```
aggiungiamo gli shard:
```javascript
sh.addShard("rss1/127.0.0.1:8007,127.0.0.1:8009,127.0.0.1:8011")
sh.addShard("rss2/127.0.0.1:8013,127.0.0.1:8015,127.0.0.1:8017")
```
## Importiamo i dati
ora importiamo i dati sul nostro sharded cluster
scarichiamo i dati:
```shell
wget https://atlas-data-lake.s3.amazonaws.com/json/sample_airbnb/listingsAndReviews.json
```
ed importiamo i dati:
```shell
mongoimport ./listingsAndReviews.json -d=airbnb -c=airbnb --port 8021
```
## Setuppiamo la collection come sharded
Ricolleghiamoci al router per poter abilitare lo sharding:
```shell
mongo --port 8021
```
Abilitiamo la possibilita' di fare sharding sul nostro nuovo db:
```javascript
sh.enableSharding("airbnb")
```
Possiamo controllare lo stato del db con:
```javascript
use airbnb
db.stats()
```
dove possiamo vedere che tutti i dati sono su un solo shard (guardando la storageSize)
oppure possiamo vedere lo stato dello sharding con:
```javascript
db.printShardingStatus()
```
Creiamo l'indice su cui vogliamo eseguire lo sharding:
```javascript
db.airbnb.createIndex({beds:1})
```
Poi shardiamo la collection:
```javascript
sh.shardCollection("airbnb.airbnb", {beds:1})
```
Pero' alcuni documenti non hanno beds! Quindi rimuoviamoli dai dati:
```javascript
// per contarli possiamo fare
// db.airbnb.find({beds:{$exists:false}}).count()
db.airbnb.deleteMany({beds:{$exists:false}})
```
Ora possiamo fare lo sharding perche' tutti i documenti hanno il campo beds:
```javascript
sh.shardCollection("airbnb.airbnb", {beds:1})
```
ora possiamo controllare che il balancer stia spostando i dati tra gli shards:
```javascript
db.stats()
```
Notiamo che i dati non sono ben bilanciati, probabilmente perche' non abbiamo scelto una buona chiave visto che avremo tantissimi documenti con beds == 1 e molti meno con un numero diverso.
Purtroppo fare l'unsharding di una collection non e' semplice https://jira.mongodb.org/browse/SERVER-9845
quindi per semplicita' la cancelliamo e ricreiamo:
```javascript
db.airbnb.drop()
```
E reimportiamo i dati e ci colleghiamo:
```shell
mongoimport ./listingsAndReviews.json -d=airbnb -c=airbnb --port 8021
mongo --port 8021
```
```javascript
use airbnb
db.airbnb.deleteMany({beds:{$exists:false}})
sh.enableSharding("airbnb")
```
creiamo un indice hashato (gli indici hashati non pososno essere multiindici)
```javascript
db.airbnb.createIndex({beds:"hashed"})
sh.shardCollection("airbnb.airbnb", {beds:"hashed"})
```
Ancora non abbiamo buoni risultati, quindi passiamo al indice come chiave:
ricancelliamo e reimportiamo i dati come prima,
```javascript
use airbnb
sh.enableSharding("airbnb")
db.airbnb.ensureIndex({_id:"hashed"})
sh.shardCollection("airbnb.airbnb", {_id:"hashed"})
```
ed otteniamo un risultato migliore che possiamo controllare con:
```javascript
db.airbnb.getShardDistribution()
```
Eventualmente possiamo rimuovere uno shard:
```javascript
db.adminCommand({removeShard:"rs0"})
```
Possiamo spostare i database tra shards con:
```javascript
db.adminCommand({movePrimary:"testdb", to:"rss2"})
```
Possiamo splittare il chunk in sotto chunks:
```javascript
db.splitFind("airbnb.airbnb", { "_id" : "10009999" })
```
# TLS
Generiamo un certificato per mongodb:
```shell
openssl genrsa -out rootCA.key 4096
```
Lo firmiamo:
```shell
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem
```
L'importante e' che in common name si metta l'ip / host della macchina (quindi 127.0.0.1 nel nostro caso)
Creiamo un CA per il server:
```shell
openssl genrsa -out mongodb.key 4096
```
Firmiamolo:
```shell
openssl req -new -key mongodb.key -out mongodb.csr
```
L'importante e' che in common name si metta l'ip / host della macchina (quindi 127.0.0.1 nel nostro caso)
Generiamo il pem per mongodb:
```shell
cat mongodb.key mongodb.crt > mongodb.pem
```
Nelle settings aggiungiamo:
```yaml
net:
port: 27017
bindIp: 127.0.0.1
tls:
mode: requireTLS
certificateKeyFile: /root/mongodb.pem
CAFile: /root/rootCA.pem
```
Facciamo partire il server:
```shell
mongod -f confs.yaml
```
Poi colleghiamoci col client con:
```shell
mongo --tls --host 127.0.0.1 --tlsCAFile ./rootCA.pem --tlsCertificateKeyFile ./mongodb.pem
```
# LDAP (Work in progress)
Modifichiamo in `/etc/default/saslauthd`
```
MECHANISM="ldap"
```
Poi creiamo / modifichiamo `/etc/saslauthd.conf`
```
ldap_servers: ldap://127.0.0.1:389
ldap_search_base: dc=example,dc=org
ldap_filter: (mail=%n)
```
Faccio partire il daemon di saslauthd:
```shell
saslauthd -a "ldap" &
```
Poi facciamo partire il server ed il client:
```shell
mongod -f confs.yaml
```
```shell
mongo --tls --host 127.0.0.1 --tlsCAFile ./rootCA.pem --tlsCertificateKeyFile ./mongodb.pem
```
e dentro eseguiamo:
```javascript
sdb = db.getSiblingDB("$external")
sdb.createUser({user:"test", roles:[{role:"read", db:"testdb"}]})
sdb.createUser({user:"admin", roles:[{role:"dbOwner", db:"testdb"}]})
```
E poi spegniamo il server:
```javascript
db.adminCommand({shutDown:1})
```
aggiungiamo alle settings:
```yaml
storage:
dbPath: /var/lib/mongodb
journal:
enabled: true
# engine:
# mmapv1:
# wiredTiger:
# where to write logging data.
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1
tls:
mode: requireTLS
certificateKeyFile: /root/mongodb.pem
CAFile: /root/rootCA.pem
# how the process runs
processManagement:
timeZoneInfo: /usr/share/zoneinfo
fork: true
security:
authorization: enabled
setParameter:
saslauthdPath: /var/run/saslauthd/mux
authenticationMechanisms: PLAIN
```
Mi collego con:
```shell
mongo --tls --host 127.0.0.1 --tlsCAFile ./rootCA.pem --tlsCertificateKeyFile ./mongodb.pem
```
poi mi authentico con:
```shell
sdb = db.getSiblingDB("$external")
sdb.auth({mechanism: "PLAIN", user:"admin@example.org", pwd:"admin"})
```