2010-11-15 9 views
11

Angenommen, Sie haben eine große Anzahl von Benutzern (M) und eine große Anzahl von Dokumenten (N) und möchten, dass jeder Benutzer jedes Dokument als markieren kann gelesen oder ungelesen (wie jedes E-Mail-System). Was ist der beste Weg, dies in MongoDB darzustellen? Oder irgendeine andere Dokumentendatenbank?MongoDB/NOSQL: Beste Lösung für den Umgang mit Lese-/Lesestatus von Nachrichten

Es gibt mehrere Fragen auf Stackoverflow diese Frage für relationale Datenbanken zu fragen, aber ich habe nicht mit Empfehlungen für Dokument-Datenbanken finden Sie unter:

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

Implementing an efficient system of "unread comments" counters

Typischerweise sind die Antworten beinhalten eine tabellarische Auflistung alles, was ein Benutzer gelesen hat: (dh Tupel von Benutzer-ID, Dokument-ID) mit einigen möglichen Optimierungen für ein Cut-Off-Datum, das es ermöglicht, die Datenbank zu löschen und erneut zu wissen, dass alles vor diesem Datum gelesen wird ".

Also, MongoDB/NOSQL Experten, welche Ansätze haben Sie in der Praxis für dieses Problem gesehen und wie haben sie funktioniert?

Antwort

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] 
} 

Angenommen, Sie haben drei Nachrichten, die Sie Einstellungen abrufen wollen, können Sie sie über so etwas wie zu bekommen:

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

Wenn alles, was Sie brauchen/ungelesen gelesen wird man dies mit MongoDB der Upsert Fähigkeiten nutzen könnten Sie erstellen also keine Voreinstellungen für jede Nachricht, es sei denn, der Benutzer liest sie tatsächlich. Dann erstellen Sie im Grunde das Präferenzobjekt mit Ihrer eigenen eindeutigen ID und fügen es in MongoDB ein. Wenn Sie mehr Flexibilität wünschen (zum Beispiel Tags oder Ordner), sollten Sie die Präferenz für jeden Empfänger der Nachricht festlegen. Zum Beispiel könnten Sie hinzufügen:

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

den Prefs-Objekt und dann alle prefs aller mit ‚Techmaterial‘ getaggt Nachrichten bekommen Sie so etwas wie gehen würde:

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

konnte Sie verwenden Sie dann die messageIds Sie innerhalb der prefs finden abzufragen und alle Nachrichten zu finden, die entsprechen:

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

Es könnte ein wenig schwierig sein, wenn Sie so etwas wie zu zählen, wie viele Nachrichten tun wollen Jedes 'Tag' enthält effizient. Wenn es sich nur um eine Handvoll Tags handelt, können Sie einfach .count() am Ende Ihrer Abfrage für jede Abfrage hinzufügen. Wenn es Hunderte oder Tausende sind, können Sie besser mit einem map/reduce serverseitigen Skript arbeiten oder mit einem Objekt, das die Anzahl der Nachrichten pro Tag pro Benutzer protokolliert.

+1

Danke, also ist Ihre Empfehlung im Wesentlichen die gleiche Art von "Tuple/Join" -Tabelle wie der relationale Fall, oder? Warum speichern Sie sowohl die Nachrichten als auch die Einstellungen in derselben Sammlung? –

+0

Die Sache mit MongoDB ist, dass in der Regel desto flacher Sie Ihr Objekt zum Besseren machen können. Während es geschachtelte Strukturen speichern kann, ist es nicht das Beste, diese Strukturen später zu fragen oder in diese Strukturen zu gelangen, um sie zu ändern. Viele Dinge können also ähnlich aussehen wie relationale, aber mit weniger Abstraktion, da keine Tabellen verwendet werden. Es gibt auch keinen Grund, warum ich sie in der gleichen Kollektion aufbewahre, außer dass ich keine bazillion Sammlungen habe. Wenn Sie planen, Millionen von Nachrichten zu haben, ist es ratsam, verschiedene Sammlungen zu verwenden, damit Sie die Indizes für jedes Objekt besser anpassen können. – Klinky

3

Wenn Sie nur einen einfachen booleschen Wert wie read/unread speichern, besteht eine andere Methode darin, ein Array in jedes Dokument einzubetten, das eine Liste der Benutzer enthält, die es gelesen haben.

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

Sie sollten dann das Feld Index der Lage sein, für schnelle Abfragen machen für Dokumente ablesbare-by-User und Benutzer-die-lesen-Dokument.

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

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

Allerdings finde ich, dass ich in der Regel für alle Dokumente, die ich Abfragen nicht von einem bestimmten Benutzer gelesen wurden, und ich kann nicht jeder Lösung vorstellen, die Verwendung des Index in diesem machen kann Fall.Ich vermute, dass es nicht möglich ist, dies schnell zu machen, ohne sowohl read_by als auch unread_by Arrays zu haben, so dass jeder Benutzer in jedem Dokument (oder Join-Tabelle) enthalten ist, aber das hätte große Speicherkosten.

+0

In Bezug auf den letzten Punkt über die Abfrage von * ungelesenen * Nachrichten, aber mit einem * read_by * -Feld, korrigieren Sie mich, wenn ich falsch liege, aber nicht eine ** $ not ** -Klausel, wie in $ nicht: {$ in: [{id: 'Benutzer # 83'}]} '? – bigp