【问题标题】:MongoDB / NOSQL: Best approach to handling read/unread status on messagesMongoDB / NOSQL:处理消息的已读/未读状态的最佳方法
【发布时间】:2011-05-10 00:16:15
【问题描述】:

假设您有大量用户 (M) 和大量文档 (N),并且您希望每个用户能够将每个文档标记为已读或未读(就像任何电子邮件系统一样)。在 MongoDB 中表示这一点的最佳方式是什么?还是其他任何文档数据库?

StackOverflow 上有几个问题针对关系数据库提出了这个问题,但我没有看到任何有关文档数据库的建议:

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

Implementing an efficient system of "unread comments" counters

通常,答案涉及一个表格,其中列出了用户已阅读的所有内容:(即用户 ID 的元组,文档 ID)以及一些可能的截止日期优化,允许标记为已读以擦除数据库并重新开始知道该日期之前的任何内容都是“已读”。

那么,MongoDB / NOSQL 专家,您在实践中看到了哪些解决此问题的方法以及它们的表现如何?

【问题讨论】:

    标签: mongodb database-design non-relational-database nosql


    【解决方案1】:
    {
    _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]
    }
    

    假设您有 3 条消息要检索首选项,您可以通过以下方式获取它们:

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

    如果您只需要已读/未读,则可以将其与 MongoDB 的 upsert 功能一起使用,因此除非用户实际阅读它,否则您不会为每条消息创建首选项,那么基本上您使用自己的唯一 id 和 upsert 创建首选项对象它进入MongoDB。如果您想要更多的灵活性(例如标签或文件夹),您可能希望为消息的每个收件人设置首选项。例如,您可以添加:

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

    到 prefs 对象,然后获取所有标记为“tech stuff”的消息的所有 prefs,你可以这样做:

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

    然后您可以使用在首选项中找到的 messageId 来查询并找到所有对应的消息:

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

    如果您想做一些事情,比如有效地计算每个“标签”包含多少消息,这可能会有点棘手。如果只有少数标签,您可以将.count() 添加到每个查询的查询末尾。如果它是数百或数千,那么您可能会使用 map/reduce 服务器端脚本或跟踪每个用户每个标签的消息计数的对象做得更好。

    【讨论】:

    • 谢谢,所以您的建议本质上是与关系案例相同的“元组/连接”表,对吧?您将消息和首选项存储在同一个集合中的任何特殊原因?
    • MongoDB 的特点是,通常你可以让你的对象变得更好。虽然它可以存储嵌套结构,但它并不是最好的查询或稍后进入这些结构以更改它们。所以很多东西最终可能看起来类似于关系,但由于不使用表而抽象程度较低。除了不喜欢拥有大量收藏之外,我真的没有理由将它们存储在同一个收藏中。如果您确实计划拥有数百万条消息,那么使用不同的集合可能是明智之举,这样您就可以设置索引以更好地适应每个对象。
    【解决方案2】:

    如果您只存储一个简单的布尔值,例如已读/未读,另一种方法是在每个 Document 中嵌入一个数组,该数组包含一个已阅读用户列表。

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

    然后,您应该能够对该字段进行索引,从而快速查询 Documents-read-by-User 和 Users-who-read-Document。

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

    但是,我发现我通常会查询特定用户读取的所有文档,并且我想不出任何可以利用索引的解决方案这个案例。我怀疑如果不同时拥有 read_byunread_by 数组就不可能做到这一点,这样每个用户都包含在每个文档(或连接表)中,但这会产生很大的存储成本。

    【讨论】:

    • 关于查询 unread 消息但使用 read_by 字段的最后一点,如果我错了,请纠正我,但不能 $not 子句实现这一点,如$not: {$in: [{id: 'user#83'}]}?
    • 我会将该计数器保存在单独的集合中,原因是您只有在真正需要它时才能到达它,并且当它成为消息文档的一部分时,即使在并不需要。当然,如果我们假设用户只从数据库中获取他需要的东西,那么这种方法很好,但根据我的经验,最好将它们分开。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-20
    • 1970-01-01
    • 1970-01-01
    • 2011-11-16
    • 2015-03-05
    • 1970-01-01
    相关资源
    最近更新 更多