Impara da zero il database NoSQL più popolare al mondo: documenti JSON, CRUD, aggregation pipeline, indici, schema validation e integrazione con Node.js. Con esempi pratici ed esercizi.
Introduzione a MongoDB
MongoDB è il database NoSQL più diffuso al mondo: invece di tabelle e righe usa documenti JSON (chiamati BSON internamente) organizzati in collezioni. È schemaless, orizzontalmente scalabile e progettato per dati flessibili e ad alta velocità.
A differenza di MySQL o PostgreSQL, in MongoDB non devi definire uno schema rigido in anticipo: ogni documento può avere campi diversi, ideale per dati variabili, log, cataloghi di prodotti e applicazioni real-time.
Perché usare MongoDB?
Flessibilità — nessuno schema obbligatorio, i documenti evolvono nel tempo
Performance — letture e scritture molto veloci su grandi volumi di dati
Scalabilità orizzontale — sharding nativo per distribuire i dati su più server
Dati annidati — array e oggetti dentro i documenti, senza JOIN
Cloud-native — MongoDB Atlas disponibile su AWS, Azure e GCP
Puoi usare MongoDB in tre modi: localmente (installa MongoDB Community), nel cloud (MongoDB Atlas, cluster gratuito M0), o via Docker. Per chi inizia, Atlas è la scelta più rapida.
# Avvia MongoDB in un container
docker run -d \
--name mongodb \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=secret \
mongo:7.0
# Accedi alla shell MongoDB
docker exec -it mongodb mongosh -u admin -p secret
Primi comandi in mongosh
Primi passi con mongosh
// Mostra tutti i database
show dbs
// Usa (o crea) un database
use negozio
// Mostra le collezioni
show collections
// Inserisci un documento
db.prodotti.insertOne({ nome: "Laptop", prezzo: 999, stock: 50 })
// Leggi tutti i documenti
db.prodotti.find()
// Leggi con formattazione
db.prodotti.find().pretty()
🚀 Scorciatoia: use nomedb crea automaticamente il database la prima volta che inserisci un documento — non serve un comando separato "CREATE DATABASE".
MongoDB Query API
La Query API di MongoDB è il linguaggio con cui interroghi, filtri e trasformi i documenti. Tutte le operazioni si esprimono con oggetti JavaScript/JSON passati ai metodi della collezione.
// Include solo nome ed email (1 = includi, 0 = escludi)
db.utenti.find(
{ attivo: true },
{ nome: 1, email: 1, _id: 0 } // _id escluso esplicitamente
)
// Escludi solo un campo
db.utenti.find({}, { password: 0 })
Cursori e metodi di cursore
Metodi del cursore
// Limita i risultati
db.prodotti.find().limit(10)
// Salta i primi N documenti (paginazione)
db.prodotti.find().skip(20).limit(10) // pagina 3 da 10
// Ordina: 1 = crescente, -1 = decrescente
db.prodotti.find().sort({ prezzo: -1 }) // più caro prima
// Conta i documenti
db.prodotti.countDocuments({ stock: { $gt: 0 } })
// Conta approssimato (velocissimo)
db.prodotti.estimatedDocumentCount()
Metodi utili
Metodo
Descrizione
find(filtro)
Restituisce cursore con tutti i match
findOne(filtro)
Restituisce il primo documento match
countDocuments(filtro)
Conta i documenti con il filtro
distinct(campo)
Valori unici di un campo
find().limit(n)
Limita a N risultati
find().sort(ord)
Ordina i risultati
find().skip(n)
Salta i primi N documenti
📌 find() restituisce un cursore (non tutti i documenti in memoria). Itera con forEach(), toArray() o in un loop for...of in Node.js per lavorare con i risultati.
MongoDB — Creare un Database
In MongoDB i database e le collezioni sono creati implicitamente al primo inserimento di un documento. Non esiste un comando CREATE DATABASE separato come in SQL.
Creare e selezionare un database
use — crea o seleziona un database
// Seleziona il database (lo crea se non esiste)
use scuola
// Il database viene creato fisicamente solo dopo il primo insert
db.studenti.insertOne({ nome: "Luca", eta: 20 })
// Ora il database "scuola" esiste
show dbs // mostra scuola nell'elenco
// Vedi il database corrente
db
// Seleziona il database da eliminare
use testdb
// Elimina il database corrente (IRREVERSIBILE)
db.dropDatabase()
⚠️ dropDatabase() elimina tutti i dati del database corrente in modo irreversibile. In produzione proteggi questa operazione con permessi appropriati.
💡 Convenzione: i nomi dei database MongoDB sono case-sensitive su Linux ma non su Windows. Usa sempre nomi in minuscolo con trattini o underscore per coerenza cross-platform.
MongoDB Collections
Una collection (collezione) è il contenitore dei documenti, equivalente a una tabella in SQL. A differenza delle tabelle, non richiede uno schema: ogni documento nella stessa collezione può avere campi completamente diversi.
Operazioni sulle collezioni
Gestione collezioni
// Crea collezione implicitamente
db.articoli.insertOne({ titolo: "Primo articolo" })
// Lista collezioni del database corrente
show collections
// oppure
db.getCollectionNames()
// Info su una collezione
db.articoli.stats()
// Rinomina una collezione
db.articoli.renameCollection("posts")
// Elimina una collezione e tutti i suoi dati
db.posts.drop()
Capped Collections
Le capped collections hanno dimensione fissa: quando piena, sovrascrive i documenti più vecchi (circolare). Perfette per log e dati temporali.
Capped collection per log
db.createCollection("access_log", {
capped: true,
size: 5242880, // 5 MB
max: 10000 // max 10.000 documenti
})
// I documenti vengono inseriti in ordine naturale (velocissimo)
db.access_log.insertOne({
ip: "192.168.1.1",
url: "/api/users",
timestamp: new Date()
})
Time Series Collections
Time Series (MongoDB 5.0+)
// Ottimizzate per dati temporali (IoT, metriche)
db.createCollection("temperatura", {
timeseries: {
timeField: "timestamp", // campo data obbligatorio
metaField: "sensore", // campo metadati (facoltativo)
granularity: "minutes" // seconds | minutes | hours
}
})
db.temperatura.insertOne({
timestamp: new Date(),
sensore: "sala-server",
valore: 22.5
})
📌 MongoDB gestisce automaticamente l'_id di ogni documento: un ObjectId da 12 byte che include timestamp, machine ID e un contatore. Puoi sovrascriverlo con un valore custom: { _id: "custom-id", ... }
MongoDB — Insert
Per inserire documenti in una collezione si usano insertOne() (un documento) e insertMany() (array di documenti). Entrambi restituiscono i nuovi _id generati.
// Per default insertMany è "ordered": si ferma al primo errore
// Con ordered:false continua anche in caso di errori parziali
db.prodotti.insertMany(
[
{ _id: 1, nome: "A" },
{ _id: 1, nome: "B" }, // _id duplicato → errore
{ _id: 2, nome: "C" }
],
{ ordered: false } // inserisce A e C, salta B
)
⚡ insertMany() con ordered: false è più veloce in bulk insert perché MongoDB non deve aspettare la conferma di ogni documento prima di inserire il successivo.
MongoDB — Find
I metodi find() e findOne() sono usati per interrogare i documenti. Il primo argomento è il filtro (oggetto query), il secondo è la projection (campi da includere/escludere).
Find base
find() e findOne()
// Tutti i documenti
db.prodotti.find()
// Filtro esatto
db.prodotti.find({ marca: "Apple" })
// findOne() — restituisce un solo documento (o null)
db.prodotti.findOne({ _id: ObjectId("64a1f2c3...") })
// Filtro su campo annidato (dot notation)
db.prodotti.find({ "indirizzo.citta": "Milano" })
// Filtro su elemento di array
db.prodotti.find({ categorie: "elettronica" })
Operatori di confronto nei filtri
Operatore
Significato
Esempio
$eq
Uguale
{ eta: { $eq: 25 } }
$ne
Diverso
{ stato: { $ne: "inattivo" } }
$gt
Maggiore
{ prezzo: { $gt: 100 } }
$gte
Maggiore o uguale
{ voto: { $gte: 18 } }
$lt
Minore
{ eta: { $lt: 30 } }
$lte
Minore o uguale
{ eta: { $lte: 65 } }
$in
In un insieme
{ citta: { $in: ["Roma","Milano"] } }
$nin
Non in un insieme
{ stato: { $nin: ["banned","deleted"] } }
Esempi con operatori di confronto
// Prodotti tra 50 e 200 euro
db.prodotti.find({ prezzo: { $gte: 50, $lte: 200 } })
// Utenti di Roma o Milano
db.utenti.find({ citta: { $in: ["Roma", "Milano"] } })
// Studenti con voto 28, 29 o 30
db.studenti.find({ voto: { $in: [28, 29, 30] } })
// Tutto tranne gli eliminati
db.utenti.find({ stato: { $nin: ["eliminato", "bannato"] } })
Per modificare documenti si usano updateOne(), updateMany() e replaceOne(). Le modifiche si specificano con update operators che iniziano con $.
updateOne() e updateMany()
Aggiorna documenti
// updateOne — modifica il primo documento che matcha
db.prodotti.updateOne(
{ nome: "Laptop Pro" }, // filtro
{ $set: { prezzo: 1199.99 } } // aggiornamento
)
// updateMany — modifica tutti i documenti che matchano
db.prodotti.updateMany(
{ disponibile: false },
{ $set: { stock: 0 } }
)
// Risultato
// { matchedCount: 3, modifiedCount: 3 }
Operatori $set, $unset, $inc
Operatori di update comuni
// $set — imposta il valore di un campo
db.utenti.updateOne(
{ email: "mario@example.com" },
{ $set: { citta: "Torino", aggiornato: new Date() } }
)
// $unset — rimuove un campo dal documento
db.utenti.updateOne(
{ email: "mario@example.com" },
{ $unset: { vecchioIndirizzo: "" } }
)
// $inc — incrementa/decrementa un valore numerico
db.prodotti.updateOne(
{ nome: "Tastiera" },
{ $inc: { stock: -1, vendite: 1 } } // -1 stock, +1 vendite
)
// $rename — rinomina un campo
db.utenti.updateMany({}, { $rename: { "nome": "firstName" } })
upsert — aggiorna o inserisci
upsert: true
// Se il documento non esiste lo crea (insert + update)
db.impostazioni.updateOne(
{ chiave: "tema" },
{ $set: { valore: "scuro", aggiornato: new Date() } },
{ upsert: true } // crea se non trovato
)
replaceOne()
Sostituisci un documento intero
// Sostituisce l'INTERO documento (mantiene solo _id)
db.prodotti.replaceOne(
{ _id: ObjectId("64a1...") },
{
nome: "Laptop Pro Max",
prezzo: 1499,
marca: "Apple",
aggiornato: new Date()
}
)
📌 Differenza chiave: updateOne/Many modifica solo i campi specificati con gli operatori $; replaceOne sostituisce l'intero documento (eccetto _id).
MongoDB — Delete
Per eliminare documenti si usano deleteOne() (il primo match) e deleteMany() (tutti i match). Usa sempre un filtro preciso in produzione per evitare eliminazioni accidentali.
deleteOne() e deleteMany()
// Elimina il primo documento che matcha
db.prodotti.deleteOne({ nome: "Prodotto Obsoleto" })
// Elimina tutti i documenti che matchano
db.prodotti.deleteMany({ disponibile: false, stock: 0 })
// Elimina per _id (modo più sicuro)
db.ordini.deleteOne({ _id: ObjectId("64a1f2c3e8b7a3d0c9f12345") })
// ATTENZIONE: elimina TUTTI i documenti della collezione
db.temp_data.deleteMany({})
// Risultato
// { deletedCount: 3 }
findOneAndDelete()
Cancella e restituisce il documento
// Elimina e restituisce il documento eliminato in una sola operazione
const eliminato = db.sessioni.findOneAndDelete(
{ scaduta: true },
{ sort: { creata: 1 } } // elimina la più vecchia
)
print(eliminato) // il documento eliminato
Svuotare una collezione vs eliminarla
Drop vs deleteMany
// Elimina tutti i documenti (mantiene la collezione e gli indici)
db.cache.deleteMany({})
// Elimina la collezione e tutti i suoi indici (più veloce)
db.cache.drop()
⚠️ Non esiste un ROLLBACK in MongoDB per le delete (a meno di usare transazioni). Prima di un deleteMany in produzione, fai sempre un find() con lo stesso filtro per verificare cosa stai cancellando.
🛡️ Best practice: in applicazioni reali usa la tecnica del soft delete — aggiungi un campo { eliminato: true, eliminatoIl: new Date() } invece di cancellare fisicamente i dati.
MongoDB Query Operators
Gli operatori di query permettono di costruire filtri complessi: confronti, logica booleana, ricerca in array, espressioni regolari e molto altro.
Operatori su elementi
$exists, $type
// $exists — il campo esiste
db.utenti.find({ telefono: { $exists: true } })
db.utenti.find({ telefono: { $exists: false } })
// $type — tipo BSON del campo
db.dati.find({ valore: { $type: "string" } })
db.dati.find({ valore: { $type: "number" } })
db.dati.find({ valore: { $type: ["string", "null"] } })
Operatori su array
$all, $elemMatch, $size
// $all — contiene TUTTI gli elementi specificati
db.prodotti.find({ tags: { $all: ["sale", "nuovo"] } })
// $elemMatch — almeno un elemento soddisfa tutte le condizioni
db.studenti.find({
voti: { $elemMatch: { $gte: 28, $lte: 30 } }
})
// $size — array di esattamente N elementi
db.ordini.find({ prodotti: { $size: 3 } })
// Accesso per indice (primo elemento dell'array)
db.studenti.find({ "voti.0": { $gte: 27 } })
// $push — aggiunge tag
db.articoli.updateOne(
{ _id: id },
{ $push: { tags: "mongodb" } }
)
// $addToSet — aggiunge solo se non esiste già
db.utenti.updateOne(
{ email: "user@example.com" },
{ $addToSet: { ruoli: "admin" } }
)
// $pull — rimuove elementi specifici
db.articoli.updateOne(
{ _id: id },
{ $pull: { tags: "bozza" } }
)
// $push + $each + $slice — mantieni solo ultimi 5 log
db.sessioni.updateOne(
{ userId: uid },
{
$push: {
log: {
$each: [{ evento: "login", ts: new Date() }],
$sort: { ts: -1 },
$slice: 5
}
}
}
)
MongoDB Aggregations
L'Aggregation Pipeline è la funzionalità più potente di MongoDB: permette di trasformare e analizzare documenti attraverso una sequenza di stage (fasi), ognuna con un compito specifico.
// Unisce utenti con i loro ordini
db.utenti.aggregate([
{
$lookup: {
from: "ordini", // collezione esterna
localField: "_id", // campo locale
foreignField: "userId", // campo in ordini
as: "ordiniUtente" // nome del campo risultante
}
},
// Filtra utenti con almeno un ordine
{ $match: { "ordiniUtente.0": { $exists: true } } }
])
$unwind — espandi array
$unwind
// Ogni prodotto di ogni ordine diventa un documento separato
db.ordini.aggregate([
{ $unwind: "$prodotti" },
{ $group: {
_id: "$prodotti.nome",
totQty: { $sum: "$prodotti.quantita" }
}},
{ $sort: { totQty: -1 } },
{ $limit: 5 } // top 5 prodotti più venduti
])
MongoDB Indexing & Search
Gli indici sono fondamentali per le performance: senza di essi MongoDB fa una collection scan (legge tutti i documenti). Con un indice su un campo, le query su quel campo diventano logaritmicamente veloci.
Tipi di indice
Tipo
Uso
Single field
Indice su un singolo campo
Compound
Indice su più campi (ordine importante)
Multikey
Indice su campo array (automatico)
Text
Ricerca full-text su stringhe
2dsphere
Query geospaziali su coordinate GPS
Hashed
Sharding su hash del campo
Wildcard
Indice su tutti i campi (schema flessibile)
Partial
Indice solo su documenti che rispettano un filtro
TTL
Scadenza automatica dei documenti
Creare e gestire indici
// Indice singolo campo (1 = asc, -1 = desc)
db.utenti.createIndex({ email: 1 })
// Indice unico (nessun duplicato)
db.utenti.createIndex({ email: 1 }, { unique: true })
// Indice composto
db.prodotti.createIndex({ categoria: 1, prezzo: -1 })
// Indice text per ricerca full-text
db.articoli.createIndex({ titolo: "text", contenuto: "text" })
// TTL — elimina documenti dopo 3600 secondi
db.sessioni.createIndex(
{ creata: 1 },
{ expireAfterSeconds: 3600 }
)
// Lista indici
db.utenti.getIndexes()
// Elimina un indice
db.utenti.dropIndex({ email: 1 })
EXPLAIN — analizza le query
explain() per ottimizzare
// Analizza come MongoDB esegue la query
db.prodotti.find({ categoria: "elettronica" }).explain("executionStats")
// Cerca "IXSCAN" (usa indice) vs "COLLSCAN" (scansione totale)
// totalDocsExamined deve essere vicino a nReturned per essere efficiente
⚡ Regola d'oro: crea indici sui campi usati in find(), sort(), $match e $lookup. Un indice composto { a: 1, b: 1 } copre anche query su solo a, ma non su solo b.
MongoDB Validation
MongoDB è schemaless per natura, ma puoi definire schema validation per imporre regole sui documenti: tipi obbligatori, valori consentiti, range numerici. Si usa il formato JSON Schema.
// Aggiorna la validazione di una collezione esistente
db.runCommand({
collMod: "utenti",
validator: {
$jsonSchema: {
bsonType: "object",
required: ["nome", "email"],
properties: {
nome: { bsonType: "string" },
email: { bsonType: "string" }
}
}
},
validationLevel: "moderate" // valida solo i nuovi documenti
})
Tipi BSON supportati
bsonType
Descrizione
string
Stringa di testo
int
Intero 32-bit
long
Intero 64-bit
double
Numero decimale
decimal
Decimale ad alta precisione
bool
true / false
date
Data/ora
objectId
ObjectId MongoDB
array
Array
object
Oggetto/documento annidato
null
Null
MongoDB Data API
La MongoDB Data API (disponibile su Atlas) permette di interagire con i dati via HTTP REST senza installare driver. Ideale per applicazioni serverless, edge functions e webhook.
Abilitare la Data API su Atlas
In Atlas, vai su App Services → Data API
Abilita la Data API per il cluster desiderato
Genera una API Key dalla scheda Auth
Usa l'endpoint base: https://data.mongodb-api.com/app/<app-id>/endpoint/data/v1
📌 La Data API è ottima per prototipazione rapida e ambienti serverless. Per applicazioni ad alto volume (>1000 req/s) usa i driver nativi: sono significativamente più veloci grazie al connection pooling.
MongoDB Drivers
I driver ufficiali MongoDB sono disponibili per tutti i principali linguaggi di programmazione. Gestiscono automaticamente connection pooling, riconnessioni e conversione dei tipi BSON.
Driver disponibili
Linguaggio
Pacchetto
Installazione
Node.js
mongodb
npm install mongodb
Python
pymongo
pip install pymongo
Java
mongodb-driver-sync
Maven/Gradle
C#/.NET
MongoDB.Driver
dotnet add package MongoDB.Driver
Go
mongo-driver
go get go.mongodb.org/mongo-driver
PHP
mongodb
composer require mongodb/mongodb
Rust
mongodb
cargo add mongodb
Python — PyMongo
PyMongo — operazioni base
from pymongo import MongoClient
from datetime import datetime
# Connessione
client = MongoClient("mongodb+srv://user:pass@cluster.mongodb.net/")
db = client["negozio"]
col = db["prodotti"]
# Insert
col.insert_one({"nome": "Mouse", "prezzo": 29.99, "stock": 100})
# Find
for doc in col.find({"prezzo": {"$lt": 50}}, {"_id": 0, "nome": 1, "prezzo": 1}):
print(doc)
# Update
col.update_one({"nome": "Mouse"}, {"$inc": {"stock": -1}})
# Delete
col.delete_one({"stock": 0})
client.close()
C# — MongoDB.Driver
C# Driver — operazioni base
using MongoDB.Driver;
using MongoDB.Bson;
var client = new MongoClient("mongodb+srv://user:pass@cluster/");
var db = client.GetDatabase("negozio");
var col = db.GetCollection<BsonDocument>("prodotti");
// Insert
await col.InsertOneAsync(new BsonDocument {
{ "nome", "Tastiera" },
{ "prezzo", 49.99 },
{ "stock", 75 }
});
// Find
var filtro = Builders<BsonDocument>.Filter.Gt("prezzo", 20);
var docs = await col.Find(filtro).ToListAsync();
// Update
var update = Builders<BsonDocument>.Update.Inc("stock", -1);
await col.UpdateOneAsync(
Builders<BsonDocument>.Filter.Eq("nome", "Tastiera"), update);
MongoDB Node.js Driver
Il driver Node.js ufficiale di MongoDB è mongodb. È la base di Mongoose (ODM più popolare) ma può essere usato direttamente per massime performance e controllo.
📌 Mongoose vs driver nativo: Mongoose aggiunge validazione schema lato app, middleware (pre/post hooks), populate (equivalent $lookup semplificato) e metodi virtuali. Il driver nativo è più veloce ma più verboso.
MongoDB Charts
MongoDB Charts è lo strumento di visualizzazione dati integrato in Atlas: crea dashboard e grafici direttamente dai tuoi dati MongoDB, senza esportare o trasformare nulla.
📊 MongoDB Charts si aggiorna in tempo reale (o con refresh configurabile). È ideale per dashboard operative, monitoraggio vendite, analytics di app mobile e report aziendali senza dover usare strumenti BI separati.
Esercizi MongoDB
Metti alla prova le tue conoscenze con questi esercizi pratici. Usa MongoDB Atlas o un ambiente locale con mongosh per eseguirli.
Esercizio 1 — Libreria digitale
📚 Base
Crea un database libreria con una collezione libri. Inserisci almeno 5 libri con i campi: titolo, autore, anno, genere (array), prezzo, disponibile (bool).
Trova tutti i libri disponibili
Trova i libri pubblicati dopo il 2000 ordinati per anno decrescente
Trova i libri del genere "fantasy" che costano meno di 20€
Aggiorna il prezzo di un libro con $inc
Aggiungi un nuovo genere a un libro con $addToSet
Esercizio 2 — Aggregation
📊 Intermedio
Usando la collezione libri dell'esercizio precedente, scrivi pipeline di aggregazione per:
Conta quanti libri ci sono per ogni genere
Calcola il prezzo medio per autore
Trova i 3 generi con il prezzo medio più alto
Elenca tutti i generi unici presenti (usa $unwind)
Esercizio 3 — E-commerce
🛒 Avanzato
Progetta un mini e-commerce con le collezioni: prodotti, utenti, ordini.
MongoDB Atlas — cluster cloud gratuito per praticare
Conclusione — E ora?
Complimenti! 🎉 Hai completato il corso MongoDB completo e gratuito in italiano: dall'installazione e i primi passi con mongosh, al CRUD completo, agli operatori di query e update, all'Aggregation Pipeline, agli indici, alla validation, alle transazioni ACID e all'integrazione con Node.js.
MongoDB è oggi uno dei database più richiesti nel mondo del lavoro: ogni azienda che gestisce dati flessibili, applicazioni real-time o grandi volumi di informazioni lo usa. Ora hai le basi per costruire applicazioni professionali con MongoDB.
E ora?
Hai già lavorato con Node.js e MongoDB: il passo successivo naturale è imparare PostgreSQL per confrontare il mondo NoSQL con un database relazionale potente, oppure approfondire il backend con Node.js per costruire API REST complete. Se vuoi un percorso guidato personalizzato, posso seguirti con ripetizioni di informatica in presenza a Roma.
🎯 La tua sfida finale
Costruisci un'API REST con Node.js + Express + MongoDB: un sistema di gestione libri con endpoint per creare, leggere, aggiornare ed eliminare libri. Aggiungi paginazione, ricerca full-text e uno schema Mongoose con validation. Deployala su Railway o Render con MongoDB Atlas come database. Sarà il progetto portfolio perfetto per dimostrare le tue competenze backend.