2010-11-15 5 views
11

Supponiamo che tu abbia un numero elevato di utenti (M) e un numero elevato di documenti (N) e desideri che ciascun utente sia in grado di contrassegnare ciascun documento come leggere o non letto (proprio come qualsiasi sistema di posta elettronica). Qual è il modo migliore per rappresentarlo in MongoDB? O qualsiasi altro database di documenti?MongoDB/NOSQL: approccio migliore alla gestione dello stato letto/non letto sui messaggi

Ci sono diverse domande su StackOverflow questa domanda per i database relazionali, ma non ho visto alcuna con le raccomandazioni per i database di documenti:

What's the most efficient way to remember read/unread status across multiple items?

Implementing an efficient system of "unread comments" counters

In genere le risposte coinvolgono un elenco tavolo tutto ciò che un utente ha letto: (vale a dire tuple di id utente, id documento) con alcune ottimizzazioni possibili per una data limite che consente di contrassegnare tutto il testo per cancellare il database e ricominciare da capo sapendo che qualsiasi cosa prima di tale data è 'letta '.

Quindi, esperti MongoDB/NOSQL, quali approcci avete visto in pratica a questo problema e come hanno funzionato?

risposta

4
{ 
_id: messagePrefs_uniqueId, 
type: 'prefs', 
timestamp: unix_timestamp 
ownerId: receipientId, 
messageId: messageId, 
read: true/false, 
} 

{ 
_id: message_uniqueId, 
timestamp: unix_timestamp 
type: 'message', 
contents: 'this is the message', 
senderId: senderId, 
recipients: [receipientId1,receipientId2] 
} 

Diciamo che avete 3 messaggi che si desidera recuperare le preferenze per, si possono ottenere via qualcosa come:

db.messages.find({ 
messageId : { $in : [messageId1,messageId2,messageId3]}, 
ownerId: receipientId, 
type:'prefs' 
}) 

Se tutto ciò che serve è letto/non letto si potrebbe usare questo con funzionalità upsert di MongoDB , quindi non stai creando prefs per ogni messaggio a meno che l'utente non lo legga effettivamente, quindi in pratica crei l'oggetto prefs con il tuo ID univoco e lo mandi in MongoDB. Se vuoi una maggiore flessibilità (come dire tag o cartelle) probabilmente vorrai fare il pref per ogni destinatario del messaggio. Per esempio si potrebbe aggiungere:

tags: ['inbox','tech stuff'] 

alle preferenze oggetto e quindi di ottenere tutte le preferenze di tutti i messaggi taggati con 'roba di tecnologia' devi andare qualcosa come:

db.messages.find({type: 'prefs', ownerId: recipientId, tags: 'tech stuff'}) 

Si potrebbe quindi utilizzare i messageIds puoi trovare all'interno del prefs per interrogare e trovare tutti i messaggi che corrispondono:

db.messages.find((type:'message', _id: { $in : [array of messageIds from prefs]}}) 

potrebbe essere un po 'difficile se si vuole fare qualcosa di simile a contare il numero di messaggi ogni 'etichetta' contiene efficientemente. Se si tratta solo di una manciata di tag, è sufficiente aggiungere .count() alla fine della query per ogni query. Se sono centinaia o migliaia, allora si potrebbe fare meglio con una mappa/ridurre lo script lato server o magari un oggetto che tiene traccia dei conteggi dei messaggi per tag per utente.

+1

Grazie, quindi la tua raccomandazione è essenzialmente lo stesso tipo di tabella 'tuple/join' come caso relazionale, giusto? Qualche ragione particolare per cui memorizzi sia i messaggi che i prefs nella stessa collezione? –

+0

La cosa con MongoDB è che solitamente il più piatto è possibile rendere il tuo oggetto migliore. Mentre può immagazzinare strutture nidificate, non è il migliore per interrogare o entrare in quelle strutture in seguito per modificarle. Quindi molte cose potrebbero sembrare simili a quelle relazionali, ma con meno astrazioni a causa del non utilizzo delle tabelle. Inoltre, non c'è davvero alcun motivo per archiviarli nella stessa collezione, a parte il fatto che non mi piace avere una collezione di miliardi. Se pianifichi di avere milioni di messaggi, potrebbe essere saggio utilizzare raccolte diverse in modo da poter impostare gli indici per adattarli meglio a ciascun oggetto. – Klinky

3

Se si memorizza solo un valore booleano semplice, come letto/non letto, un altro metodo consiste nell'incastonare un array in ciascun documento che contiene un elenco di utenti che lo hanno letto.

{ 
    _id: 'document#42', 
    ... 
    read_by: ['user#83', 'user#2702'] 
} 

Si dovrebbe quindi essere in grado di indicizzare quel campo, rendendo per le query veloci per i documenti-read-by-user e utenti-che-read-Documento.

db.documents.find({read_by: 'user#83'}) 

db.documents.find({_id: 'document#42}, {read_by: 1}) 

Tuttavia, trovo che sto di solito l'esecuzione di query per tutti i documenti che sono non stati letti da un particolare utente, e non riesco a pensare di qualsiasi soluzione che può fare utilizzo dell'indice in questo Astuccio.Sospetto che non sia possibile farlo velocemente senza disporre sia degli array read_by e unread_by, in modo che ogni utente sia incluso in ogni documento (o tabella di join), ma ciò comporterebbe un notevole costo di archiviazione.

+0

Per quanto riguarda l'ultimo punto sull'interrogazione dei messaggi * non letti * ma usando un campo * read_by *, correggimi se ho torto ma non è possibile ottenere una clausola ** $ not **, come in '$ not: {$ in: [{id: 'utente # 83'}]} '? – bigp