Corso
Quindi vuoi creare il tuo content management system (CMS), noto anche come blog? È un classico esempio quando impari a usare un database, che si tratti di un sistema di gestione di database relazionali (RDBMS) o di un database NoSQL, perché esplora una quantità potenzialmente grande di dati e le relazioni tra quei dati. L'esempio di un'app di blog si traduce bene anche per altre esigenze di modellazione dei dati.
In questo articolo esploreremo alcuni pro e contro quando si tratta di progettare i documenti NoSQL in MongoDB. Tuttavia, non svilupperemo realmente un'app di blog: analizzeremo le cose solo da una prospettiva di dati.
I componenti di un'app di blog
Prima di tentare di progettare un modello di documento per un blog, è una buona idea fare un passo indietro e pensare a ogni componente che potrebbe avere dati associati.
Un blog tipico potrebbe avere le seguenti funzionalità:
- Molti utenti o autori
- Molti post per ciascun autore
- Molti commenti per ciascun post
Ovviamente, quell'elenco di funzionalità può diventare più lungo e complesso a seconda delle esigenze del tuo blog. Per semplicità, modelleremo i dati sulla base di quanto sopra.
Cosa puoi fare, ma non dovresti
La tua prima idea guardando le funzionalità sopra potrebbe essere trattare ogni elemento dell'elenco puntato come un proprio documento. Questo è l'approccio tipico che useresti in un database relazionale come Postgres. In MongoDB, potrebbe apparire più o meno così:
{
"_id": "author-1",
"name": "Nic Raboy",
"description": "I'm some dude named Nic."
}
Quanto sopra potrebbe essere una rappresentazione molto ridotta di un autore. Ovviamente potresti avere molti altri campi, come email e altro, ma qui non è davvero importante.
Poi, potremmo avere quanto segue per i post del blog:
{
"_id": "blog-1",
"author_id": "author-1",
"title": "MongoDB is Awesome!",
"content": "This is some blog content..."
}
Proprio come il precedente, il documento del blog potrebbe avere altri campi come tags e qualsiasi altra cosa ti venga in mente. La cosa importante qui è che c'è un riferimento id tra i due documenti. Ma su questo torneremo tra poco.
Il modello di documento finale in questo scenario riguarderebbe i commenti:
{
"_id": "comment-1",
"blog_id": "blog-1",
"username": "Anonymous User",
"content": "Hey great article, it really helped a lot!"
}
Anche in questo caso, il documento del commento ha una relazione di riferimento basata su un valore id tra il documento del commento e quello del blog. Tutto questo è molto comune in un database relazionale, e la verità è che funzionerebbe bene anche in MongoDB. Tuttavia, non ti offrirà la migliore esperienza con MongoDB.
Un motivo per cui non vorresti farlo in MongoDB è legato alle prestazioni.
Per unire ciascuno di questi tre documenti, useresti un'operazione $lookup all'interno di una pipeline di aggregazione. Queste operazioni $lookup non sono economiche e potrebbero influire negativamente sulle prestazioni man mano che il database cresce, ancor di più quando hai più di una $lookup nella tua pipeline.
Ci sono modi migliori per svolgere il lavoro in MongoDB.
Un approccio migliore e più adatto a MongoDB per la modellazione dei dati
Una delle prime regole di MongoDB è che i dati a cui si accede insieme dovrebbero essere archiviati insieme. Archiviare ogni funzionalità in un documento o in una collezione separati infrange questa regola perché non stiamo più effettuando una singola operazione di fetch per i dati presentati all'utente.
Riproviamo ad affrontare il problema, modellando i documenti per il nostro blog in questo modo:
{
"_id": "author-1",
"name": "Nic Raboy",
"description": "I'm some dude named Nic.",
"posts": [
{
"title": "MongoDB is Awesome!",
"content": "This is some blog content...",
"comments": [
{
"username": "Anonymous User",
"content": "Hey great article, it really helped a lot!"
}
]
}
]
}
Ok, quindi una di quelle regole principali di MongoDB è soddisfatta nel modello sopra. Abbiamo un singolo documento che usa l'annidamento per archiviare tutti i post del blog e tutti i commenti insieme all'autore.
Sebbene potrebbe funzionare, probabilmente non è una buona idea modellare i documenti della tua app di blog in quel modo.
MongoDB ha limiti di dimensione dei documenti e, quando hai array non limitati in quei documenti, rischi di superare quei limiti. Senza contare che potresti incorrere in problemi di prestazioni man mano che gli array crescono. In questo caso, potremmo avere un numero illimitato di post e un numero illimitato di commenti per post. A un certo punto diventerà ingestibile.
Potrebbe funzionare meglio in questo esempio un mix delle due strategie.
Ricorda, autori, post e commenti possono avere un numero qualsiasi di campi associati. In questo esempio siamo rimasti sul minimo. Tenendo a mente questo, sappiamo che i dati a cui si accederà più spesso saranno quelli del blog. Considera il nuovo design:
{
"_id": "blog-1",
"author": {
"_id": "author-1",
"name": "Nic Raboy",
},
"title": "MongoDB is Awesome!",
"content": "This is some blog content...",
"comments": [
{
"username": "Anonymous User",
"content": "Hey great article, it really helped a lot!"
}
]
}
Il secondo documento che abbiamo in questo esempio sarebbe comunque il documento originale con le informazioni sull'autore:
{
"_id": "author-1",
"name": "Nic Raboy",
"description": "I'm some dude named Nic."
}
Sappiamo che alcune informazioni sull'autore dovrebbero essere incluse quando si visualizza un articolo del blog, ma non necessariamente tutte le informazioni. Possiamo incorporare le informazioni sull'autore necessarie insieme all'_id all'interno del post e, se l'utente ha bisogno di saperne di più sull'autore, può approfondire nella tua applicazione. L'operazione $lookup non avviene ogni singola volta, il che migliora le prestazioni.
Ora abbiamo il problema della quantità potenzialmente infinita di commenti per un dato articolo, attualmente collegati come array.
Ci sono diversi approcci per gestirlo e, alla fine, la scelta spetta a te:
- Puoi scegliere l'approccio ingenuo e mantenerli come array, il che probabilmente non è una grande idea.
- Puoi riportare i commenti al design originale di un commento per documento, tutti con un id di riferimento all'articolo del blog e usare operazioni
$lookupper includerli. - Puoi usare una strategia di archiviazione, memorizzando un numero fisso di commenti in un array all'interno dell'articolo del blog e creando anche un documento separato per ciascun commento, in modo da dover interrogare eventuali commenti extra solo quando servono.
- Puoi esplorare il bucket pattern, raggruppando i commenti in "bucket" di circa 100 commenti per documento, interrogando di volta in volta quanti bucket di commenti ti servono. Il bucketing evita il limite di dimensione del documento e aiuta anche con la paginazione. Puoi anche aumentare le prestazioni creando un indice su
blog_idper recuperare rapidamente i commenti di un determinato articolo. Ulteriori informazioni sull'indicizzazione sono disponibili nella documentazione di MongoDB. - Mescola, abbina e altro ancora…
Nel caso del bucket pattern, i documenti dei commenti potrebbero essere simili ai seguenti:
{
"_id": "comment-bucket-1",
"blog_id": "blog-1",
"comments": [
{
"username": "Anonymous User",
"content": "Hey great article, it really helped a lot!"
},
// More comments here...
],
"comment_count": 57
}
In questo scenario, quando vengono creati nuovi commenti, interroghi un bucket che non è pieno. Inserisci il nuovo commento nell'array e incrementi il valore del contatore. Sebbene questa strategia dipenda maggiormente dalla logica a livello di applicazione per mantenere i bucket, porterà a prestazioni migliori. Se sul tuo blog hai un meccanismo di scroll infinito, ancora meglio, perché puoi paginare in modo più efficace usando i bucket.
Anche se è fuori dall'ambito di questo articolo, se vuoi un assaggio di ciò che serve per aggiungere un nuovo commento, filtreresti la collezione dei bucket di commenti (vedi la query di esempio qui sotto) per i bucket che corrispondono al blog_id e che hanno anche un comment_count inferiore al valore scelto.
Per la corrispondenza, useresti l'operatore $push nei criteri di aggiornamento e l'operatore $inc per aumentare il valore del conteggio dei commenti. Tutto questo può essere fatto in un'unica operazione e, se usi il flag upsert, puoi creare un nuovo bucket di commenti se non ne esiste già uno che soddisfi i tuoi criteri.
Esempio di query di aggiornamento:
db.commentBuckets.updateOne(
{ blog_id: "blog-1", comment_count: { $lt: 100 } },
{ $push: { comments: newComment }, $inc: { comment_count: 1 } },
{ upsert: true }
)
Conclusione
Per quanto sarebbe bello dire che dovresti archiviare tutto del tuo blog in un unico documento annidato, probabilmente non sarebbe una grande idea. Così come probabilmente non sarebbe una grande idea implementare un modello di dati che funziona bene con un database relazionale, ma non necessariamente con un database a documenti come MongoDB.
Con MongoDB, vuoi ridurre il più possibile le relazioni tramite riferimenti per ottenere le migliori prestazioni possibili.
Tieni a mente quanto segue man mano che scali:
- Se ti aspetti un volume elevato di commenti, considera il bucket pattern o una sua combinazione insieme a un embedding parziale nel documento del blog.
- Quali sono le tue esigenze di paginazione?
- Indicizza le collezioni in base a come prevedi di interrogarle nella tua applicazione.
Per ribadire, pensa a come presenterai i dati del blog nel tuo frontend. Ti servono davvero tutte le informazioni sull'autore con la richiesta o ne bastano alcune? Vuoi davvero caricare un milione di commenti con il tuo articolo o va bene caricarne una piccola manciata? Se ci pensi bene, rimarrai sorpreso da quante operazioni di join non ti servono.
Se sei nuovo su MongoDB, ti consiglio di dare un'occhiata al corso Introduction to MongoDB in Python.
FAQ
Posso trasferire il modello dati della mia app di blog da un database relazionale a MongoDB?
Puoi creare documenti piatti con riferimenti tramite id e funzionerebbe, ma non sfrutteresti al massimo le prestazioni che potresti ottenere con MongoDB.
Perché non memorizzare semplicemente tutto in un unico documento?
Ci sono limiti di dimensione in MongoDB e, quando hai array non limitati, puoi raggiungere facilmente quei limiti.
Si è parlato del bucket pattern, ma ce ne sono altri?
Esistono altri pattern che potrebbero funzionare, ma il bucket pattern è una buona opzione quando si tratta di grandi array.
Quali sono i limiti di dimensione dei documenti di MongoDB?
I documenti MongoDB hanno una dimensione massima di 16 MB.
Come gestisco gli aggiornamenti ai dati annidati (ad es. modifica di un commento)?
Per i dati incorporati puoi usare operatori di aggiornamento come $set, e per i commenti raggruppati puoi individuare il bucket e aggiornare lo specifico commento al suo interno.

Nic Raboy è un Responsabile Developer Relations presso MongoDB, dove guida un team di sviluppatori Python, Java, C# e PHP che creano contenuti straordinari per aiutare gli sviluppatori a integrare con successo MongoDB nei loro progetti. Ha esperienza con Golang e JavaScript e spesso scrive delle sue molte avventure nello sviluppo.

