---
# System prepended metadata

title: Info sul corso & setup

---

# 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"})
```