In base alle esigenze del vostro software, potreste dare priorità alla flessibilità, alla scalabilità, alle prestazioni o alla velocità. Per questo motivo, developer e aziende sono spesso confusi nella scelta di un database per le loro esigenze. Se avete bisogno di un database che garantisca un’elevata flessibilità e scalabilità, nonché l’aggregazione dei dati per l’analisi dei clienti, MongoDB potrebbe fare al caso vostro!


In questo articolo parleremo della struttura del database MongoDB e di come creare, monitorare e gestire il vostro database! Iniziamo.

Come È Strutturato un Database MongoDB?

MongoDB è un database NoSQL senza schema. Ciò significa che non è necessario specificare una struttura per le tabelle/database come avviene per i database SQL.

Sapevate che i database NoSQL sono più veloci dei database relazionali? Ciò è dovuto a caratteristiche come l’indicizzazione, lo sharding e le pipeline di aggregazione. MongoDB è noto anche per la velocità di esecuzione delle query. Per questo motivo è preferito da aziende come Google, Toyota e Forbes.

Di seguito analizzeremo alcune caratteristiche chiave di MongoDB.

I Documenti

MongoDB ha un modello di dati documentale che memorizza i dati come documenti JSON. I documenti corrispondono naturalmente agli oggetti del codice dell’applicazione, rendendo più semplice l’utilizzo da parte di sviluppatrici e sviluppatori.

In una tabella di un database relazionale, per aggiungere un nuovo campo dovete aggiungere una colonna. Questo non è il caso dei campi di un documento JSON. I campi di un documento JSON possono variare da un documento all’altro, quindi non saranno aggiunti a tutti i record del database.

I documenti possono memorizzare strutture come gli array che possono essere annidati per esprimere relazioni gerarchiche. Inoltre, MongoDB converte i documenti in un tipo binario JSON (BSON). Questo garantisce un accesso più rapido e un maggiore supporto per vari tipi di dati come stringhe, numeri interi, numeri booleani e molto altro ancora!

Set di Repliche

Quando create un nuovo database in MongoDB, il sistema crea automaticamente almeno altre due copie dei vostri dati. Queste copie sono note come “set di replica” e replicano continuamente i dati tra loro, garantendo una maggiore disponibilità dei vostri dati. Inoltre, offrono una protezione contro i tempi di inattività durante un guasto del sistema o una manutenzione programmata.

Collezioni

Una collezione è un gruppo di documenti associati a un database. Sono simili alle tabelle dei database relazionali.

Le collezioni, tuttavia, sono molto più flessibili. Innanzitutto, non dipendono da uno schema. In secondo luogo, non è necessario che i documenti abbiano lo stesso tipo di dati!

Per visualizzare un elenco delle collezioni che appartengono a un database, usate il comando listCollections.

Pipeline di Aggregazione

Potete usare questo framework per raggruppare diversi operatori ed espressioni. È flessibile perché vi permette di elaborare, trasformare e analizzare dati di qualsiasi struttura.

Per questo motivo, MongoDB consente flussi di dati veloci e funzioni con 150 operatori ed espressioni. Ha anche diversi stadi, come lo stadio Union, che mette insieme in modo flessibile i risultati di più collezioni.

Indici

Potete indicizzare qualsiasi campo di un documento MongoDB per aumentarne l’efficienza e migliorare la velocità delle query. L’indicizzazione consente di risparmiare tempo grazie alla scansione dell’indice per limitare i documenti ispezionati. Non è molto meglio che leggere tutti i documenti della collezione?

Potete usare diverse strategie di indicizzazione, compresi gli indici composti su più campi. Per esempio, è utile se avete diversi documenti che contengono il nome e il cognome del dipendente in campi separati. Se volete che vengano restituiti il nome e il cognome, potete creare un indice che includa sia “Cognome” che “Nome”; molto meglio rispetto ad avere un indice su “Cognome” e un altro su “Nome”.

Potete usare strumenti come Performance Advisor per capire meglio quali query potrebbero trarre vantaggio dagli indici.

Sharding

Lo sharding distribuisce un singolo set di dati su più database. Questo set di dati può essere archiviato su più macchine per aumentare la capacità di archiviazione totale di un sistema. Questo perché divide i dataset più grandi in pezzi più piccoli e li archivia in diversi nodi di dati.

MongoDB suddivide i dati a livello di collezione, distribuendo i documenti di una collezione tra gli shard di un cluster. Questo garantisce la scalabilità, consentendo all’architettura di gestire le applicazioni più grandi.

Come Creare un Database MongoDB

Per prima cosa dovete installare il pacchetto MongoDB adatto al vostro sistema operativo. Andate alla pagina “MongoDB Community Server Download“. Tra le opzioni disponibili, selezionate l’ultima “versione”, il formato del “pacchetto” come file zip e la “piattaforma” come il vostro sistema operativo e clicca su “Download” come illustrato di seguito:

Schermata della pagina di MongoDB con le opzioni disponibili: Version, Platform e Package durante il download di MongoDB Community Server.
Processo di download del server comunitario MongoDB. (Fonte: MongoDB Community Server)

Il processo è abbastanza semplice, quindi avrete MongoDB installato nel vostro sistema in pochissimo tempo!

Una volta terminata l’installazione, aprite il prompt dei comandi e digitate mongod -version per verificarla. Se non ottenete il seguente risultato e vedete invece una serie di errori, forse dovrete reinstallare il sistema:

Lo snippet di codice per verificare la versione di MongoDB dopo l'installazione.
Verifica della versione di MongoDB. (Fonte immagine: configserverfirewall)

Usare la Shell di MongoDB

Prima di iniziare, verificate che:

  • Il vostro client sia dotato di Transport Layer Security e si trova nella vostra lista di IP consentiti.
  • Avete un account utente e una password sul cluster MongoDB desiderato.
  • Avete installato MongoDB sul vostro dispositivo.

Passo 1: Accedere alla Shell di MongoDB

Avviate il server MongoDB seguendo le istruzioni del vostro sistema operativo. Per Windows, digitate il seguente comando. Per gli altri sistemi operativi, consultate la documentazione di MongoDB.

net start MongoDB

Questo dovrebbe dare il seguente risultato:

Esecuzione del server MongoDB
Esecuzione del server MongoDB (fonte immagine: c-sharpcorner)

Il comando precedente ha inizializzato il server MongoDB. Per eseguirlo, dovremo digitare mongo nel prompt dei comandi.

Esecuzione della shell di MongoDB.
Esecuzione della shell di MongoDB. (fonte immagine: bmc)

Nella shell di MongoDB possiamo eseguire comandi per creare database, inserire dati, modificare dati, impartire comandi amministrativi e cancellare dati.

Passo 2: Creare il Database

A differenza dei comuni database relazionali, MongoDB non ha un comando di creazione del database. Esiste invece una parola chiave chiamata use che permette di passare a un database specificato. Se il database non esiste, ne crea uno nuovo, altrimenti si collega al database esistente.

Per esempio, per avviare un database chiamato “azienda”, digitate:

use Company
Frammento di codice per creare un database in MongoDB.
Creare un database in MongoDB.

Potete digitare db per confermare il database appena creato nel vostro sistema. Se il nuovo database creato viene visualizzato, la connessione è avvenuta con successo.

Se volete controllare i database esistenti, digitate show dbs e vi verranno mostrati tutti i database presenti nel vostro sistema:

Frammento di codice per visualizzare i database esistenti nel server MongoDB.
Visualizzazione dei database in MongoDB.

Per impostazione predefinita, l’installazione di MongoDB crea i database admin, config e local.

Avete notato che il database che abbiamo creato non viene visualizzato? Questo perché non abbiamo ancora salvato i valori nel database! Parleremo dell’inserimento nella sezione dedicata alla gestione dei database.

Usare l’Interfaccia Utente di Atlas

Potete anche iniziare a usare il servizio di database di MongoDB, Atlas. Sebbene sia necessario pagare per accedere ad alcune caratteristiche di Atlas, la maggior parte delle funzionalità del database sono disponibili con il livello gratuito. Le funzionalità del livello gratuito sono più che sufficienti per creare un database MongoDB.

Prima di iniziare, verificate che:

  1. Il vostro IP sia presente nella allowlist.
  2. Avete un account utente e una password sul cluster MongoDB che volete utilizzare.

Per creare un database MongoDB con AtlasUI, aprite una finestra del browser e accedete a https://cloud.mongodb.com. Dalla pagina del vostro cluster, fate clic su Browse Collections. Se non ci sono database nel cluster, potete creare il vostro database facendo clic sul pulsante Add My Own Data.

Il prompt vi chiederà di indicare il nome del database e della collezione. Dopo averli nominati, fate clic su Create e il gioco è fatto! Ora potete inserire nuovi documenti o connettervi al database utilizzando i driver.

Gestire il Database MongoDB

In questa sezione vi illustreremo alcuni modi interessanti per gestire efficacemente il vostro database MongoDB. Potete farlo usando il Compass di MongoDB o attraverso le collezioni.

Usare le Collezioni

Mentre i database relazionali possiedono tabelle ben definite con tipi di dati e colonne specifiche, i NoSQL hanno collezioni al posto delle tabelle. Queste collezioni non hanno alcuna struttura e i documenti possono variare: potete avere diversi tipi di dati e campi senza necessità di rimanere legati al formato di un altro documento nella stessa collezione.

Per dimostrarlo, creiamo una collezione chiamata “Employee” e aggiungiamole un documento essa:

db.Employee.insert(
  {
       "Employeename" : "Chris",
       "EmployeeDepartment" : "Sales"
  }
)

Se l’inserimento va a buon fine, verrà restituito WriteResult({ "nInserted" : 1 }):

Questo codice restituisce WriteResult({
Inserimento riuscito in MongoDB.

In questo caso, “db” si riferisce al database attualmente collegato. “Employee” è la collezione appena creata nel database aziendale.

Non abbiamo impostato una chiave primaria perché MongoDB crea automaticamente un campo chiave primaria chiamato “_id” e gli assegna un valore predefinito.

Eseguite il comando seguente per verificare la collezione in formato JSON:

db.Employee.find().forEach(printjson)

Output:

{
  "_id" : ObjectId("63151427a4dd187757d135b8"),
  "Employeename" : "Chris",
  "EmployeeDepartment" : "Sales"
}

Mentre il valore “_id” viene assegnato automaticamente, potete modificare il valore della chiave primaria predefinita. Questa volta inseriremo un altro documento nel database “Employee” con il valore “_id” pari a “1”:

db.Employee.insert(
  {  
       "_id" : 1,
       "EmployeeName" : "Ava",
       "EmployeeDepartment" : "Public Relations"
  }
)

Eseguendo il comando db.Employee.find().forEach(printjson) otterremo il seguente risultato:

L'output mostra i documenti della raccolta Employee insieme alla loro chiave primaria
Documenti nella collezione con la loro chiave primaria.

Nell’output qui sopra, il valore “_id” di “Ava” è impostato su “1” invece di essere assegnato automaticamente.

Ora che abbiamo aggiunto con successo dei valori al database, possiamo verificare se questo viene visualizzato tra i database esistenti nel nostro sistema utilizzando il seguente comando:

show dbs
L'output mostra la collezione Employee nei database esistenti nel nostro sistema..
Visualizzazione dell’elenco dei database.

E voilà! Avete creato con successo un database nel vostro sistema!

Usare la Bussola di MongoDB

Anche se possiamo lavorare con i server MongoDB dalla shell Mongo, a volte può essere noioso. Potreste riscontrare questa situazione in un ambiente di produzione.

Tuttavia, esiste uno strumento che funge da bussola (e infatti è chiamato Compass) creato da MongoDB che può semplificare le cose. Ha un’interfaccia grafica migliore e funzionalità aggiuntive come la visualizzazione dei dati, la profilazione delle prestazioni e l’accesso CRUD (acronimo di Create, Read, Update, Delete, cioè creazione, lettura, aggiornamento, cancellazione) ai dati, ai database e alle collezioni.

Potete scaricare Compass IDE per il vostro sistema operativo e installarlo con un processo semplice.

Successivamente, aprite l’applicazione e create una connessione con il server incollando la stringa di connessione. Se non riuscite a trovarla, potete fare clic su Fill in connection fields individually. Se non avete modificato il numero di porta durante l’installazione di MongoDB, fate clic sul pulsante di connessione e il gioco è fatto! In caso contrario, inserite i valori impostati e fate clic su Connect.

Questa immagine mostra la finestra New Connection, dove si può scegliere di incollare l'url della connessione.
Finestra Nuova Connessione in MongoDB… (Fonte immagine: mongodb)

Quindi fornite il nome dell’host, la porta e l’autenticazione nella finestra New Connection.

In MongoDB Compass, potete creare un database e aggiungere contemporaneamente la sua prima collezione. Ecco come fare:

  1. Fate clic su Create database per aprire il prompt.
  2. Inserite il nome del database e la sua prima collezione.
  3. Fate clic su Create database.

Potete inserire altri documenti nel vostro database facendo clic sul nome del database e poi sul nome della collezione per visualizzare la scheda Documents. Potete quindi fare clic sul pulsante Add Data per inserire uno o più documenti nella vostra collezione.

Durante l’aggiunta dei documenti, potete inserirli uno alla volta o come documenti multipli in un array. Se state aggiungendo più documenti, verificate che questi documenti separati da virgole siano racchiusi tra parentesi quadre. Per esempio:

{ _id: 1, item: { name: "apple", code: "123" }, qty: 15, tags: [ "A", "B", "C" ] },
{ _id: 2, item: { name: "banana", code: "123" }, qty: 20, tags: [ "B" ] },
{ _id: 3, item: { name: "spinach", code: "456" }, qty: 25, tags: [ "A", "B" ] },
{ _id: 4, item: { name: "lentils", code: "456" }, qty: 30, tags: [ "B", "A" ] },
{ _id: 5, item: { name: "pears", code: "000" }, qty: 20, tags: [ [ "A", "B" ], "C" ] },
{ _id: 6, item: { name: "strawberry", code: "123" }, tags: [ "B" ] }

Infine, fate clic su Insert per aggiungere i documenti alla vostra collezione. Ecco come appare il corpo di un documento:

{
  "StudentID" : 1
  "StudentName" : "JohnDoe"
}

Qui i nomi dei campi sono “StudentID” e “StudentName”. I valori dei campi sono rispettivamente “1” e “JohnDoe”.

Comandi Utili

Potete gestire queste collezioni attraverso i comandi di gestione dei ruoli e degli utenti.

Comandi per la Gestione degli Utenti

I comandi di gestione degli utenti di MongoDB contengono i comandi che riguardano gli utenti. Possiamo creare, aggiornare e cancellare gli utenti utilizzando questi comandi.

dropUser

Questo comando rimuove un singolo utente dal database specificato. La sintassi è la seguente:

db.dropUser(username, writeConcern)

Qui, username è un campo obbligatorio che specifica il nome dell’utente da rimuovere dal database. Il campo opzionale writeConcern contiene il livello di preoccupazione per la scrittura dell’operazione di rimozione. Il livello di preoccupazione per la scrittura può essere determinato dal campo opzionale writeConcern.

Prima di eliminare un utente con il ruolo userAdminAnyDatabase, verificate che ci sia almeno un altro utente con i privilegi di amministrazione.

In questo esempio, elimineremo l’utente “user26” nel database di prova:

use test
db.dropUser("user26", {w: "majority", wtimeout: 4000})

Output:

> db.dropUser("user26", {w: "majority", wtimeout: 4000});
true
createUser

Questo comando crea un nuovo utente per il database specificato come segue:

db.createUser(user, writeConcern)

Qui, user è un campo obbligatorio che contiene il documento con le informazioni di autenticazione e di accesso dell’utente da creare. Il campo facoltativo writeConcern contiene il livello di criticità di scrittura per l’operazione di creazione. Il livello di preoccupazione per la scrittura può essere determinato dal campo opzionale writeConcern.

createUser restituirà un errore di utente duplicato se l’utente esiste già nel database.

Potete creare un nuovo utente nel database di prova come segue:

use test
db.createUser(
  {
    user: "user26",
    pwd: "myuser123",
    roles: [ "readWrite" ]  
  }
);

L’output è il seguente:

Successfully added user: { "user" : "user26", "roles" : [ "readWrite", "dbAdmin" ] }
grantRolesToUser

Potete usare questo comando per assegnare ruoli aggiuntivi a un utente. Per usarlo, dovete tenere a mente la seguente sintassi:

db.runCommand(
  {
    grantRolesToUser: "<user>",
    roles: [ <roles> ],
    writeConcern: { <write concern> },
    comment: <any>
  }
)

Potete specificare sia i ruoli definiti dall’utente che quelli integrati nei ruoli sopra citati. Se volete specificare un ruolo che esiste nello stesso database in cui viene eseguito grantRolesToUser, potete specificare il ruolo con un documento, come indicato di seguito:

{ role: "<role>", db: "<database>" }

Oppure potete semplicemente specificare il ruolo con il suo nome. Per esempio:

"readWrite"

Se volete specificare il ruolo presente in un altro database, dovrete specificare il ruolo con un documento diverso.

Per assegnare un ruolo a un database, è necessaria l’azione grantRole sul database specificato.

Ecco un esempio per farvii un’idea chiara. Prendiamo un utente productUser00 nel database dei prodotti con i seguenti ruoli:

"roles" : [
  {
    "role" : "assetsWriter",
    "db" : "assets"
  }
]

L’operazione grantRolesToUser fornisce a “productUser00” il ruolo readWrite nel database delle scorte e il ruolo di lettura nel database dei prodotti:

use products
db.runCommand({
  grantRolesToUser: "productUser00",
  roles: [
    { role: "readWrite", db: "stock"},
    "read"
  ],
  writeConcern: { w: "majority" , wtimeout: 2000 }
})

L’utente productUser00 nel database dei prodotti possiede ora i seguenti ruoli:

"roles" : [
  {
    "role" : "assetsWriter",
    "db" : "assets"
  },
  {
    "role" : "readWrite",
    "db" : "stock"
  },
  {
    "role" : "read",
    "db" : "products"
  }
]
usersInfo

Potete usare il comando usersInfo per ottenere informazioni su uno o più utenti. Ecco la sintassi:

db.runCommand(
  {
    usersInfo: <various>,
    showCredentials: <Boolean>,
    showCustomData: <Boolean>,
    showPrivileges: <Boolean>,
    showAuthenticationRestrictions: <Boolean>,
    filter: <document>,
    comment: <any>
  }
)
{ usersInfo: <various> }

In termini di accesso, gli utenti possono sempre consultare le proprie informazioni. Per consultare le informazioni di un altro utente, l’utente che esegue il comando deve avere i privilegi che includono l’azione viewUser sul database dell’altro utente.

Eseguendo il comando userInfo, potete ottenere le seguenti informazioni a seconda delle opzioni specificate:

{
  "users" : [
    {
      "_id" : "<db>.<username>",
      "userId" : <UUID>, // Starting in MongoDB 4.0.9
      "user" : "<username>",
      "db" : "<db>",
      "mechanisms" : [ ... ],  // Starting in MongoDB 4.0
      "customData" : <document>,
      "roles" : [ ... ],
      "credentials": { ... }, // only if showCredentials: true
      "inheritedRoles" : [ ... ],  // only if showPrivileges: true or showAuthenticationRestrictions: true
      "inheritedPrivileges" : [ ... ], // only if showPrivileges: true or showAuthenticationRestrictions: true
      "inheritedAuthenticationRestrictions" : [ ] // only if showPrivileges: true or showAuthenticationRestrictions: true
      "authenticationRestrictions" : [ ... ] // only if showAuthenticationRestrictions: true
    },
  ],
  "ok" : 1
} 

Ora che avete un’idea generale di ciò che potete ottenere con il comando usersInfo, la domanda più ovvia che potrebbe sorgere è: quali sono i comandi utili per controllare utenti specifici o più utenti?

Ecco due esempi pratici per illustrare la stessa cosa:
Per visualizzare i privilegi e le informazioni specifiche di un utente, ma non le credenziali, per un utente “Anthony” definito nel database “office”, eseguite il seguente comando:

db.runCommand(
  {
    usersInfo:  { user: "Anthony", db: "office" },
    showPrivileges: true
  }
)

Se volete visualizzare un utente nel database corrente, potete solo citare l’utente per nome. Per esempio, se vi trovate nel database home e nel database home esiste un utente chiamato “Timothy”, potete eseguire il seguente comando:

db.getSiblingDB("home").runCommand(
  {
    usersInfo:  "Timothy",
    showPrivileges: true
  }
)

Inoltre, potete usare un array se volete visualizzare le informazioni di diversi utenti. Potete includere i campi opzionali showCredentials e showPrivileges, oppure scegliere di non includerli. Ecco come si presenterebbe il comando:

db.runCommand({
usersInfo: [ { user: "Anthony", db: "office" }, { user: "Timothy", db: "home" } ],
  showPrivileges: true
})
revokeRolesFromUser

Potete usare il comando revokeRolesFromUser per rimuovere uno o più ruoli da un utente nel database in cui sono presenti i ruoli. Il comando revokeRolesFromUser ha la seguente sintassi:

db.runCommand(
  {
    revokeRolesFromUser: "<user>",
    roles: [
      { role: "<role>", db: "<database>" } | "<role>",
    ],
    writeConcern: { <write concern> },
    comment: <any>
  }
)

Nella sintassi sopra citata, potete specificare sia i ruoli definiti dall’utente che quelli incorporati nel campo roles. Analogamente al comando grantRolesToUser, potete specificare il ruolo che volete revocare in un documento o usare il suo nome.

Per eseguire correttamente il comando revokeRolesFromUser, dovete avere l’azione revokeRole sul database specificato.

Ecco un esempio per chiarire il concetto. L’entità productUser00 nel database dei prodotti aveva i seguenti ruoli:

"roles" : [
  {
    "role" : "assetsWriter",
    "db" : "assets"
  },
  {
    "role" : "readWrite",
    "db" : "stock"
  },
  {
    "role" : "read",
    "db" : "products"
  }
]

Il seguente comando revokeRolesFromUser rimuoverà due ruoli dell’utente: il ruolo “read” da products e il ruolo assetsWriter dal database “asset”:

use products
db.runCommand( { revokeRolesFromUser: "productUser00",
  roles: [
    { role: "AssetsWriter", db: "assets" },
    "read"
  ],
  writeConcern: { w: "majority" }
} )

L’utente “productUser00” nel database dei prodotti ha ora un solo ruolo rimanente:

"roles" : [
  {
    "role" : "readWrite",
    "db" : "stock"
  }
]

Comandi di Gestione dei Ruoli

I ruoli garantiscono agli utenti l’accesso alle risorse. Gli amministratori possono usare diversi ruoli integrati per controllare l’accesso a un sistema MongoDB. Se i ruoli non coprono i privilegi desiderati, potete anche andare oltre e creare nuovi ruoli per un determinato database.

dropRole

Con il comando dropRole potete eliminare un ruolo definito dall’utente dal database su cui eseguire il comando. Per eseguire questo comando, usate la seguente sintassi:

db.runCommand(
  {
    dropRole: "<role>",
    writeConcern: { <write concern> },
    comment: <any>
  }
)

Per un’esecuzione corretta, dovete disporre dell’azione dropRole sul database specificato. Le seguenti operazioni eliminano il ruolo writeTags dal database “products”:

use products
db.runCommand(
  {
    dropRole: "writeTags",
    writeConcern: { w: "majority" }
  }
)
creaRole

Potete usare il comando createRole per creare un ruolo e specificarne i privilegi. Il ruolo si applica al database su cui si sceglie di eseguire il comando. Il comando createRole restituisce un errore di ruolo duplicato se il ruolo esiste già nel database.

Per eseguire questo comando, seguite la sintassi indicata:

db.adminCommand(
  {
    createRole: "<new role>",
    privileges: [
      { resource: { <resource> }, actions: [ "<action>", ... ] },
    ],
    roles: [
      { role: "<role>", db: "<database>" } | "<role>",
    ],
    authenticationRestrictions: [
      {
        clientSource: ["<IP>" | "<CIDR range>", ...],
        serverAddress: ["<IP>" | "<CIDR range>", ...]
      },
    ],
    writeConcern: <write concern document>,
    comment: <any>
  }
)

I privilegi di un ruolo si applicano al database in cui il ruolo è stato creato. Il ruolo può ereditare i privilegi da altri ruoli del suo database. Per esempio, un ruolo creato nel database “admin” può includere privilegi che si applicano a un cluster o a tutti i database. Può anche ereditare privilegi da ruoli presenti in altri database.

Per creare un ruolo in un database, dovete avere due elementi:

  1. L’azione grantRole su quel database indica i privilegi del nuovo ruolo e i ruoli da cui ereditare.
  2. L’azione createRole su quella risorsa del database.

Il seguente comando createRole creerà un ruolo clusterAdmin nel database degli utenti:

db.adminCommand({ createRole: "clusterAdmin",
  privileges: [
    { resource: { cluster: true }, actions: [ "addShard" ] },
    { resource: { db: "config", collection: "" }, actions: [ "find", "remove" ] },
    { resource: { db: "users", collection: "usersCollection" }, actions: [ "update", "insert" ] },
    { resource: { db: "", collection: "" }, actions: [ "find" ] }
  ],
  roles: [
    { role: "read", db: "user" }
  ],
  writeConcern: { w: "majority" , wtimeout: 5000 }
})
grantRolesToRole

Con il comando grantRolesToRole potete concedere ruoli a un ruolo definito dall’utente. Il comando grantRolesToRole ha effetto sui ruoli del database in cui viene eseguito il comando.

Il comando grantRolesToRole ha la seguente sintassi:

db.runCommand(
  {
    grantRolesToRole: "<role>",
    roles: [
     { role: "<role>", db: "<database>" },
    ],
    writeConcern: { <write concern> },
    comment: <any>
  }
)

I privilegi di accesso sono simili a quelli del comando grantRolesToRole: è necessaria un’azione grantRole su un database per la corretta esecuzione del comando.

Nell’esempio seguente, potete usare il comando grantRolesToRole per aggiornare il ruolo productsReader nel database “prodotti” affinché erediti i privilegi del ruolo productsWriter:

use products
db.runCommand(
  {
    grantRolesToRole: "productsReader",
    roles: [
      "productsWriter"
    ],
    writeConcern: { w: "majority" , wtimeout: 5000 }
  }
)
revokePrivilegesFromRole

Potete usare il comando revokePrivilegesFromRole per rimuovere i privilegi specificati dal ruolo definito dall’utente nel database in cui viene eseguito il comando. Per una corretta esecuzione, dovete tenere presente la seguente sintassi:

db.runCommand(
  {
    revokePrivilegesFromRole: "<role>",
    privileges: [
      { resource: { <resource> }, actions: [ "<action>", ... ] },
    ],
    writeConcern: <write concern document>,
    comment: <any>
  }
)

Per revocare un privilegio, il pattern “resource document” deve corrispondere al campo “resource” di quel privilegio. Il campo “actions” può essere una corrispondenza esatta o un sottoinsieme.

Per esempio, consideriamo il ruolo manageRole nel database dei prodotti con i seguenti privilegi che specificano il database “manager” come risorsa:

{
  "resource" : {
    "db" : "managers",
    "collection" : ""
  },
  "actions" : [
    "insert",
    "remove"
  ]
}

Non potete revocare le azioni “insert” o “remove” da una sola collezione nel database dei manager. Le seguenti operazioni non causano alcuna modifica al ruolo:

use managers
db.runCommand(
  {
    revokePrivilegesFromRole: "manageRole",
    privileges: [
      {
        resource : {
          db : "managers",
          collection : "kiosks"
        },
        actions : [
          "insert",
          "remove"
        ]
      }
    ]
  }
)
db.runCommand(
  {
    revokePrivilegesFromRole: "manageRole",
    privileges:
      [
        {
          resource : {
          db : "managers",
          collection : "kiosks"
        },
        actions : [
          "insert"
        ]
      }
    ]
  }
)

Per revocare le azioni “insert” e/o “remove” dal ruolo manageRole, è necessario che il documento della risorsa corrisponda in maniera esatta. Per esempio, la seguente operazione revoca solo l’azione “remove” dal privilegio esistente:

use managers
db.runCommand(
  {
    revokePrivilegesFromRole: "manageRole",
    privileges:
      [
        {
          resource : {
            db : "managers",
            collection : ""
        },
        actions : [ "remove" ]
      }
    ]
  }
)

La seguente operazione rimuoverà più privilegi dal ruolo “executive” nel database dei manager:

use managers
db.runCommand(
  {
    revokePrivilegesFromRole: "executive",
    privileges: [
      {
        resource: { db: "managers", collection: "" },
        actions: [ "insert", "remove", "find" ]
      },
      {
        resource: { db: "managers", collection: "partners" },
        actions: [ "update" ]
      }
    ],
    writeConcern: { w: "majority" }
    }
)
rolesInfo

Il comando rolesInfo restituisce informazioni sui privilegi e sull’ereditarietà dei ruoli specificati, compresi i ruoli integrati e quelli definiti dall’utente. Potete anche usare il comando rolesInfo per recuperare tutti i ruoli di un database.

Per un’esecuzione corretta, seguite la seguente sintassi:

db.runCommand(
  {
    rolesInfo: { role: <name>, db: <db> },
    showPrivileges: <Boolean>,
    showBuiltinRoles: <Boolean>,
    comment: <any>
  }
)

Per ottenere informazioni su un ruolo del database corrente, potete specificare il suo nome come segue:

{ rolesInfo: "<rolename>" }

Per ottenere informazioni su un ruolo da un altro database, potete citare il ruolo con un documento che menzioni il ruolo e il database:

{ rolesInfo: { role: "<rolename>", db: "<database>" } }

Per esempio, il seguente comando restituisce le informazioni sull’eredità del ruolo per il ruolo esecutivo definito nel database “managers”:

db.runCommand(
   {
      rolesInfo: { role: "executive", db: "managers" }
   }
)

Il prossimo comando restituirà le informazioni sull’ereditarietà del ruolo:
accountManager sul database su cui viene eseguito il comando:

db.runCommand(
   {
      rolesInfo: "accountManager"
   }
)

Il seguente comando restituirà i privilegi e l’ereditarietà del ruolo “executive” come definito nel database “manager”:

db.runCommand(
   {
     rolesInfo: { role: "executive", db: "managers" },
     showPrivileges: true
   }
)

Per menzionare più ruoli, potete usare un array. Potete anche menzionare ogni ruolo nell’array come una stringa o un documento.

Dovreste usare una stringa solo se il ruolo esiste nel database su cui viene eseguito il comando:

{
  rolesInfo: [
    "<rolename>",
    { role: "<rolename>", db: "<database>" },
  ]
}

Per esempio, il seguente comando restituirà le informazioni relative a tre ruoli su tre database diversi:

db.runCommand(
   {
    rolesInfo: [
      { role: "executive", db: "managers" },
      { role: "accounts", db: "departments" },
      { role: "administrator", db: "products" }
    ]
  }
)

Potete ottenere sia i privilegi che l’eredità dei ruoli come segue:

db.runCommand(
  {
    rolesInfo: [
      { role: "executive", db: "managers" },
      { role: "accounts", db: "departments" },
      { role: "administrator", db: "products" }
    ],
    showPrivileges: true
  }
)

Incorporare i Documenti di MongoDB per Migliorare le Prestazioni

I database di documenti come MongoDB vi permettono di definire lo schema in base alle vostre esigenze. Per creare schemi ottimali in MongoDB, potete annidare i documenti. In questo modo, invece di adattare la vostra applicazione a un modello di dati, potete costruire un modello di dati che corrisponda al vostro caso d’uso.

I documenti annidati vi permettono di memorizzare dati correlati a cui accedere insieme. Durante la progettazione di schemi per MongoDB, è consigliabile incorporare i documenti per impostazione predefinita. Usate i join e i riferimenti lato database o lato applicazione solo quando ne vale la pena.

Assicuratevi che il workload possa recuperare un documento tutte le volte che è necessario. Allo stesso tempo, il documento deve avere tutti i dati di cui ha bisogno. Questo aspetto è fondamentale per garantire prestazioni eccezionali alla vostra applicazione.

Di seguito troverete alcuni pattern per incorporare i documenti:

Pattern di Documento Incorporato

Potete usare questo pattern per incorporare anche complicate sottostrutture nei documenti con cui vengono utilizzate. L’inclusione di dati collegati in un unico documento può ridurre il numero di operazioni di lettura necessarie per ottenere i dati. In generale, dovreste strutturare il vostro schema in modo che l’applicazione riceva tutte le informazioni necessarie con un’unica operazione di lettura. Quindi, la regola da tenere a mente è che ciò che viene usato insieme deve essere memorizzato insieme.

Pattern del Sottoinsieme Incorporato

Il pattern del sottoinsieme incorporato è un caso ibrido. Si potrebbe usare per una collezione separata di un lungo elenco di elementi correlati, dove potete tenere alcuni di questi elementi a portata di mano per visualizzarli.

Ecco un esempio che elenca le recensioni di film:

> db.movie.findOne()
{   
  _id: 321475,   
  title: "The Dark Knight"
}  
> db.review.find({movie_id: 321475})
{   
  _id: 264579,   
  movie_id: 321475,   
  stars: 4   
  text: "Amazing"   
}
{   
  _id: 375684,   
  movie_id: 321475,   
  stars:5,   
  text: "Mindblowing"
}

Immaginate un migliaio di recensioni simili, ma quando mostrate un film volete visualizzare solo le due più recenti. In questo scenario, ha senso memorizzare questo sottoinsieme come un elenco all’interno del documento del film:

> db.movie.findOne({_id: 321475})   
{   
  _id: 321475,   
  title: "The Dark Knight",   
  recent_reviews: [   
    {_id: 264579, stars: 4, text: "Amazing"},   
    {_id: 375684, stars: 5, text: "Mindblowing"}   
  ]   
}

In poche parole, se accedete abitualmente a un sottoinsieme di articoli correlati, assicuratevi di incorporarlo.

Accesso Indipendente

Potreste voler archiviare i sotto-documenti nella loro collezione per separarli dalla loro collezione madre.

Per esempio, prendiamo la linea di prodotti di un’azienda. Se l’azienda vende una piccola serie di prodotti, potreste volerli archiviare all’interno del documento aziendale. Ma se volete riutilizzarli tra le varie aziende o accedervi direttamente in base alla loro unità di stoccaggio (SKU), vorrete archiviare anche questi nella loro collezione.

Se manipolate o accedete a un’entità in modo indipendente, create una collezione per memorizzarla separatamente.

Elenchi Non Vincolati

L’archiviazione di brevi elenchi di informazioni correlate nel loro documento ha un inconveniente. Se il vostro elenco continua a crescere senza limiti, non dovreste inserirlo in un unico documento. Questo perché non sareste in grado di mantenerlo a lungo.

Le ragioni sono due. Innanzitutto, MongoDB ha un limite alle dimensioni di un singolo documento. In secondo luogo, se accedete al documento con una frequenza troppo elevata, otterrete risultati negativi a causa dell’utilizzo incontrollato della memoria.

In parole povere, se un elenco inizia a crescere senza limiti, create una collezione per memorizzarlo separatamente.

Pattern di Riferimento Esteso

Il pattern di riferimento esteso è simile a quello dei sottoinsiemi. Ottimizza anche le informazioni a cui si accede regolarmente da memorizzare nel documento.

Qui, invece di un elenco, viene sfruttato quando un documento fa riferimento a un altro presente nella stessa collezione. Allo stesso tempo, vengono memorizzati anche alcuni campi di quell’altro documento per un accesso immediato.

Per esempio:

> db.movie.findOne({_id: 245434})
{   
  _id: 245434,   
  title: "Mission Impossible 4 - Ghost Protocol",   
  studio_id: 924935,   
  studio_name: "Paramount Pictures"   
}

Come potete vedere, viene memorizzato “the studio_id” in modo che possiate cercare ulteriori informazioni sullo studio che ha creato il film. Ma anche il nome dello studio viene copiato in questo documento per semplicità.

Per incorporare regolarmente le informazioni nei documenti modificati, ricordatevi di aggiornare i documenti in cui avete copiato le informazioni quando vengono modificate. In altre parole, se accedete abitualmente ad alcuni campi di un documento di riferimento, incorporateli.

Come Monitorare MongoDB

Potete usare strumenti di monitoraggio come Kinsta APM per eseguire il debug di lunghe chiamate API, query di database lente, lunghe richieste di URL esterni, per citarne alcuni. Potete anche sfruttare i comandi per migliorare le prestazioni del database o usarli per controllare salute delle istanze del vostro database.

Perché Monitorare i Database MongoDB?

Un aspetto fondamentale della pianificazione dell’amministrazione dei database è il monitoraggio delle prestazioni e dello stato di salute del cluster. MongoDB Atlas gestisce la maggior parte delle attività di amministrazione grazie alle sue capacità di fault-tolerance/scaling.

Tuttavia, gli utenti devono sapere come monitorare i cluster. Devono anche sapere come scalare o modificare qualsiasi cosa sia necessaria prima di andare in crisi.

Monitorando i database MongoDB, potete:

  • Osservare l’utilizzo delle risorse.
  • Capire la capacità attuale del vostro database.
  • Reagire e rilevare i problemi in tempo reale per migliorare il vostro stack applicativo.
  • Osservare la presenza di problemi di prestazioni e comportamenti anomali.
  • Allinearvi ai requisiti di governance/protezione dei dati e agli accordi sul livello di servizio (SLA).

Metriche Chiave da Monitorare

Durante il monitoraggio di MongoDB, ci sono quattro aspetti chiave da tenere a mente:

Metriche Hardware di MongoDB

Ecco le metriche principali per il monitoraggio dell’hardware:

Normalized Process CPU

È definita come la percentuale di tempo spesa dalla CPU per il software applicativo che gestisce il processo MongoDB.

È possibile scalare questo valore in un intervallo compreso tra 0 e 100% dividendolo per il numero di core della CPU. Include la CPU sfruttata da moduli come il kernel e l’utente.

Un’elevata CPU del kernel potrebbe indicare l’esaurimento della CPU attraverso le operazioni del sistema operativo. Ma l’utente legato alle operazioni di MongoDB potrebbe essere la causa principale dell’esaurimento della CPU.

Normalized System CPU

È la percentuale di tempo che la CPU impiega per le chiamate di sistema al servizio del processo MongoDB. È possibile scalare il valore da 0 a 100% dividendolo per il numero di core della CPU. Copre anche la CPU utilizzata da moduli come iowait, user, kernel, steal, ecc.

Una CPU utente o un kernel alto potrebbero indicare l’esaurimento della CPU a causa delle operazioni di MongoDB (software). Uno iowait elevato potrebbe essere legato all’esaurimento dello storage che causa l’esaurimento della CPU.

Disk IOPS

Disk IOPS è la media delle operazioni di IO consumate al secondo sulla partizione del disco di MongoDB.

Disk Latency o Latenza del Disco

Si tratta della latenza di lettura e scrittura della partizione disco in millisecondi in MongoDB. Valori elevati (>500ms) indicano che il livello di archiviazione potrebbe influire sulle prestazioni di MongoDB.

System Memory o Memoria di Sistema

Utilizza la memoria di sistema per descrivere i byte di memoria fisica utilizzati rispetto allo spazio libero disponibile.

La metrica disponibile indica il numero di byte di memoria di sistema disponibili. Potete usarla per eseguire nuove applicazioni senza swapping.

Disk Space Free

È definito come il totale dei byte di spazio libero sul disco della partizione di MongoDB. MongoDB Atlas offre funzionalità di autoscaling basate su questa metrica.

Swap Usage

Potete sfruttare un grafico sull’utilizzo dello swap per descrivere quanta memoria viene allocata sul dispositivo di swap. Una metrica elevata in questo grafico indica che lo swap viene utilizzato. Questo indica che la memoria è sottoprovvista per il carico di lavoro corrente.

Metriche di Connessione e Funzionamento del Cluster MongoDB

Ecco le principali metriche di funzionamento e di connessione:

Operation Execution Times

Il tempo medio delle operazioni (operazioni di scrittura e lettura) eseguite nel periodo di campionamento selezionato.

Opcounters

È il tasso medio di operazioni eseguite al secondo nel periodo di campionamento selezionato. Il grafico/metrica Opcounters mostra la ripartizione dei tipi di operazioni e la velocità dell’istanza.

Connections

Questa metrica si riferisce al numero di connessioni aperte all’istanza. Picchi o numeri elevati potrebbero indicare una strategia di connessione non ottimale da parte del server o del client che non risponde.

Query Targeting

Si tratta della velocità media al secondo nel periodo di campionamento selezionato dei documenti scansionati. Per gli esecutori di query, si tratta della valutazione del piano di query e delle query. Il Query Targeting mostra il rapporto tra il numero di documenti scansionati e il numero di documenti restituiti.

Un rapporto elevato indica operazioni non ottimali. Queste operazioni scansionano molti documenti per restituirne una parte minore.

Scan and Order

Descrive la velocità media al secondo nel periodo di campionamento scelto per le query. Restituisce risultati ordinati che non possono eseguire l’operazione di ordinamento utilizzando un indice.

Queue

Le queue possono descrivere il numero di operazioni in attesa di un blocco, sia in scrittura che in lettura. Queue elevate possono indicare l’esistenza di un design dello schema non ottimale. Potrebbero anche indicare percorsi di scrittura in conflitto, spingendo una forte competizione sulle risorse del database.

Metriche di Replica di MongoDB

Ecco le metriche principali per il monitoraggio della replica:

Replication Oplog Window

Questa metrica elenca il numero approssimativo di ore disponibili nell’oplog di replica del primario. Se un secondario ha un ritardo superiore a questo valore, significa che non è in grado di tenere il passo e dovrà effettuare una risincronizzazione completa.

Replication Lag

Il ritardo di replica è definito come il numero approssimativo di secondi in cui un nodo secondario è in ritardo rispetto al primario nelle operazioni di scrittura. Un ritardo di replica elevato indica che il secondario ha difficoltà a replicare. Potrebbe avere un impatto sulla latenza delle vostre operazioni, dato che le connessioni sono di tipo lettura/scrittura.

Replication Headroom

Questa metrica si riferisce alla differenza tra la finestra di oplog della replica primaria e il ritardo di replica della secondaria. Se questo valore arriva a zero, il secondario potrebbe entrare in modalità RECOVERING.

Opcounters – repl

Opcounters – repl è definito come il tasso medio di operazioni di replica eseguite al secondo per il periodo di campionamento scelto. Con opcounters – graph/metric potete dare un’occhiata alla velocità delle operazioni e alla suddivisione dei tipi di operazioni per l’istanza specificata.

Oplog GB/Hour

Si tratta del numero medio di gigabyte di oplog generati dal sistema primario all’ora. Un volume elevato e inaspettato di oplog potrebbe indicare un carico di lavoro di scrittura molto insufficiente o un problema di progettazione dello schema.

Strumenti di Monitoraggio delle Prestazioni di MongoDB

MongoDB dispone di strumenti di interfaccia utente integrati in Cloud Manager, Atlas e Ops Manager per il monitoraggio delle prestazioni. Offre inoltre alcuni comandi e strumenti indipendenti per analizzare i dati più grezzi. Parleremo di alcuni strumenti che potete eseguire da un host con accesso e ruoli appropriati per controllare il vostro ambiente:

mongotop

Potete usare questo comando per monitorare il tempo che un’istanza di MongoDB impiega per scrivere e leggere dati per ogni raccolta. Usate la seguente sintassi:

mongotop <options> <connection-string> <polling-interval in seconds>

rs.status()

Questo comando restituisce lo stato del set di repliche. Viene eseguito dal punto di vista del membro in cui viene eseguito il metodo.

mongostat

Potete usare il comando mongostat per avere una rapida panoramica dello stato della vostra istanza del server MongoDB. Per ottenere un risultato ottimale, potete usarlo per osservare una singola istanza per un evento specifico, in quanto offre una visione in tempo reale.

Usate questo comando per monitorare le statistiche di base del server come le code di blocco, la ripartizione delle operazioni, le statistiche sulla memoria di MongoDB e le connessioni/rete:

mongostat <options> <connection-string> <polling interval in seconds>

dbStats

Questo comando restituisce le statistiche di archiviazione per un database specifico, come il numero di indici e la loro dimensione, i dati totali della raccolta rispetto alla dimensione dell’archiviazione e le statistiche relative alla raccolta (numero di raccolte e documenti).

db.serverStatus()

Potete usare il comando db.serverStatus() per avere una panoramica dello stato del database. Il comando fornisce un documento che rappresenta i contatori metrici dell’istanza corrente. Eseguite questo comando a intervalli regolari per raccogliere le statistiche dell’istanza.

collStats

Il comando collStats raccoglie statistiche simili a quelle offerte da dbStats a livello di collezione. Il suo output consiste in un conteggio degli oggetti della collezione, la quantità di spazio su disco consumato dalla collezione, la dimensione della collezione e le informazioni relative agli indici per una determinata collezione.

Potete usare tutti questi comandi per offrire una reportistica e un monitoraggio in tempo reale del server di database che vi permette di monitorare le prestazioni e gli errori del database e di prendere decisioni informate per perfezionare un database.

Come Eliminare un Database MongoDB

Per eliminare un database creato in MongoDB, dovete connettervi ad esso attraverso la parola chiave use.

Supponiamo che abbiate creato un database chiamato “Engineers”. Per connettervi al database, userete il seguente comando:

use Engineers

Digitate quindi db.dropDatabase() per eliminare il database. Dopo l’esecuzione, questo è il risultato che potete aspettarvi:

{ "dropped"  :  "Engineers", "ok" : 1 }

Potete eseguire il comando showdbs per verificare se il database esiste ancora.

Riepilogo

Per sfruttare fino all’ultima goccia di valore di MongoDB, dovete avere una solida conoscenza dei fondamenti. È quindi importante conoscere i database MongoDB come le vostre tasche. A tal fine è necessario innanzitutto familiarizzare con i metodi di creazione di un database.

In questo articolo abbiamo fatto luce sui diversi metodi che potete usare per creare un database in MongoDB, seguiti da una descrizione dettagliata di alcuni interessanti comandi MongoDB per tenere sotto controllo i vostri database. Infine, abbiamo concluso la discussione parlando di come potete sfruttare i documenti incorporati e gli strumenti di monitoraggio delle prestazioni in MongoDB per garantire che il vostro flusso di lavoro funzioni al massimo dell’efficienza.

Cosa ne pensate di questi comandi MongoDB? Abbiamo tralasciato un aspetto o un metodo che avreste voluto vedere qui? Fatecelo sapere nei commenti!

Salman Ravoof

Salman Ravoof is a self-taught web developer, writer, creator, and a huge admirer of Free and Open Source Software (FOSS). Besides tech, he's excited by science, philosophy, photography, arts, cats, and food. Learn more about him on his website, and connect with Salman on Twitter.