MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Commenti di query MongoDB insieme alle informazioni sull'utente

Il problema/i

Come scritto prima , ci sono diversi problemi durante l'incorporamento eccessivo:

Problema 1:limite di dimensioni BSON

Al momento della stesura di questo documento, I documenti BSON sono limitati a 16 MB . Se viene raggiunto tale limite, MongoDB genererebbe un'eccezione e semplicemente non potresti aggiungere più commenti e, nel peggiore dei casi, nemmeno cambiare il nome (utente) o l'immagine se la modifica aumenterebbe le dimensioni del documento.

Problema 2:limitazioni e prestazioni delle query

Non è facile interrogare o ordinare la matrice dei commenti in determinate condizioni. Alcune cose richiederebbero un'aggregazione piuttosto costosa, altre dichiarazioni piuttosto complicate.

Mentre si potrebbe obiettare che una volta che le query sono a posto, questo non è un grosso problema, mi permetto di dissentire. Innanzitutto, più una query è complicata, più difficile sarà l'ottimizzazione, sia per lo sviluppatore che per l'ottimizzatore di query di MongoDB. Ho ottenuto i migliori risultati semplicemente semplificando i modelli di dati e le query, accelerando le risposte di un fattore 100 in un'istanza.

Durante il ridimensionamento, le risorse necessarie per query complicate e/o costose potrebbero anche essere sommate a intere macchine rispetto a un modello di dati più semplice e alle query corrispondenti.

Problema 3:manutenibilità

Ultimo ma non meno importante, potresti riscontrare problemi durante la manutenzione del tuo codice. Come semplice regola pratica

In questo contesto, "costoso" si riferisce sia al denaro (per progetti professionali) che al tempo (per progetti hobby).

(Mia!) Soluzione

È abbastanza semplice:semplifica il tuo modello di dati. Di conseguenza, le tue domande diventeranno meno complicate e (si spera) più veloci.

Fase 1:identifica i tuoi casi d'uso

Sarà un'ipotesi folle per me, ma la cosa importante qui è mostrarti il ​​metodo generale. Definirei i tuoi casi d'uso come segue:

  1. Per un determinato post, gli utenti dovrebbero poter commentare
  2. Per un determinato post, mostra l'autore e i commenti, insieme al nome utente dei commentatori e degli autori e alla loro immagine
  3. Per un determinato utente, dovrebbe essere facilmente possibile modificare il nome, il nome utente e l'immagine

Fase 2:modella i tuoi dati di conseguenza

Utenti

Prima di tutto, abbiamo un modello utente semplice

{
  _id: new ObjectId(),
  name: "Joe Average",
  username: "HotGrrrl96",
  picture: "some_link"
}

Niente di nuovo qui, aggiunto solo per completezza.

Post

{
  _id: new ObjectId()
  title: "A post",
  content: " Interesting stuff",
  picture: "some_link",
  created: new ISODate(),
  author: {
    username: "HotGrrrl96",
    picture: "some_link"
  }
}

E questo è tutto per un post. Ci sono due cose da notare qui:in primo luogo, memorizziamo i dati dell'autore di cui abbiamo immediatamente bisogno quando visualizziamo un post, poiché questo ci risparmia una query per un caso d'uso molto comune, se non onnipresente. Perché non salviamo i commenti e i dati dei commentatori di conseguenza? A causa del limite di dimensione di 16 MB , stiamo cercando di impedire la memorizzazione dei riferimenti in un unico documento. Piuttosto, memorizziamo i riferimenti nei documenti di commento:

Commenti

{
  _id: new ObjectId(),
  post: someObjectId,
  created: new ISODate(),
  commenter: {
    username: "FooBar",
    picture: "some_link"
  },
  comment: "Awesome!"
}

Come per i post, abbiamo tutti i dati necessari per visualizzare un post.

Le domande

Quello che abbiamo ottenuto ora è che abbiamo aggirato il limite di dimensione BSON e non abbiamo bisogno di fare riferimento ai dati dell'utente per poter visualizzare post e commenti, il che dovrebbe farci risparmiare molte domande. Ma torniamo ai casi d'uso e ad altre query

Aggiunta di un commento

Adesso è tutto molto semplice.

Ricevere tutti o alcuni commenti per un determinato post

Per tutti i commenti

db.comments.find({post:objectIdOfPost})

Per gli ultimi 3 commenti

db.comments.find({post:objectIdOfPost}).sort({created:-1}).limit(3)

Quindi, per visualizzare un post e tutti (o alcuni) dei suoi commenti, inclusi i nomi utente e le immagini, siamo a due domande. Più del necessario prima, ma abbiamo aggirato il limite delle dimensioni e in pratica puoi avere un numero indefinito di commenti per ogni post. Ma veniamo a qualcosa di reale

Ricevere gli ultimi 5 post e i loro ultimi 3 commenti

Questo è un processo in due fasi. Tuttavia, con un'indicizzazione adeguata (ci tornerò più avanti) questo dovrebbe comunque essere veloce (e quindi risparmiare risorse):

var posts = db.posts.find().sort({created:-1}).limit(5)
posts.forEach(
  function(post) {
    doSomethingWith(post);
    var comments = db.comments.find({"post":post._id}).sort("created":-1).limit(3);
    doSomethingElseWith(comments);
  }
)

Ottieni tutti i post di un determinato utente ordinati dal più recente al meno recente e i relativi commenti

var posts = db.posts.find({"author.username": "HotGrrrl96"},{_id:1}).sort({"created":-1});
var postIds = [];
posts.forEach(
  function(post){
    postIds.push(post._id);
  }
)
var comments = db.comments.find({post: {$in: postIds}}).sort({post:1, created:-1});

Nota che abbiamo solo due domande qui. Anche se devi "manualmente" stabilire la connessione tra i post e i rispettivi commenti, dovrebbe essere abbastanza semplice.

Cambia un nome utente

Questo presumibilmente è un caso d'uso raro eseguito. Tuttavia, non è molto complicato con detto modello di dati

Innanzitutto, cambiamo il documento utente

db.users.update(
  { username: "HotGrrrl96"},
  {
    $set: { username: "Joe Cool"},
    $push: {oldUsernames: "HotGrrrl96" }
  },
  {
    writeConcern: {w: "majority"}
  }
);

Inseriamo il vecchio nome utente in un array corrispondente. Questa è una misura di sicurezza nel caso qualcosa vada storto con le seguenti operazioni. Inoltre, impostiamo il problema di scrittura su un livello piuttosto alto per assicurarci che i dati siano durevoli.

db.posts.update(
  { "author.username": "HotGrrrl96"},
  { $set:{ "author.username": "Joe Cool"} },
  {
    multi:true,
    writeConcern: {w:"majority"}
  }
)

Niente di speciale qui. La dichiarazione di aggiornamento per i commenti sembra praticamente la stessa. Sebbene queste query richiedano del tempo, vengono eseguite raramente.

Gli indici

Come regola generale, si può dire che MongoDB può utilizzare solo un indice per query. Anche se questo non è del tutto vero poiché ci sono intersezioni di indici, è facile da gestire. Un'altra cosa è che i singoli campi in un indice composto possono essere usati indipendentemente. Quindi un approccio semplice all'ottimizzazione dell'indice consiste nel trovare la query con il maggior numero di campi utilizzati nelle operazioni che utilizzano gli indici e creare un indice composto di essi. Si noti che l'ordine di occorrenza nella query è importante. Allora, andiamo avanti.

Post

db.posts.createIndex({"author.username":1,"created":-1})

Commenti

db.comments.createIndex({"post":1, "created":-1})

Conclusione

È vero che un documento completamente incorporato per post è il modo più veloce per caricarlo e i suoi commenti. Tuttavia, non si adatta bene e, a causa della natura delle query eventualmente complesse necessarie per gestirlo, questo vantaggio in termini di prestazioni può essere sfruttato o addirittura eliminato.

Con la soluzione di cui sopra, scambi una certa velocità (se!) Con una scalabilità praticamente illimitata e un modo molto più semplice di gestire i dati.

Hth.