获取MongoDB中具有最高值的文档/子文档

3
我有一个像这样的comment集合。
{
  _id: 'c1',
  text: 'comment 1',
  votes: 1,
  replies: [
    {
      _id: 'r1',
      text: 'reply 1',
      isReply: true,
      votes: 3
    },
    {
      _id: 'r2',
      text: 'reply 2',
      isReply: true,
      votes: 0
    }
  ]
},
{
  _id: 'c2',
  text: 'comment2',
  votes: 2,
  replies: []
}

这个想法是一个评论可以有许多回复。所有评论和回复都有idtextvotes。如何获取最多votes的前两个评论或回复。在给定的情况下,这将是“回复1”和“评论2”。

我可以向mongodb发送1个请求以获取最佳的2条评论,然后再发送另一个请求以获取最佳的2条回复,然后进行比较以获得所需结果。

但是,能否只通过1个向mongodb的请求,就可以获得这样的结果呢?

 [
   {
     _id: 'r1',
     text: 'reply 1',
     isReply: true,
     votes: 3
   },
   {
     _id: 'c2',
     text: 'comment 2',
     votes: 2
   }
 ]

或者,我可以将评论/回复展平为按votes排序的评论或回复列表吗? 在这种情况下,它将是[r1,c2,c1,r2],分别具有其属性
谢谢。
更新: 我尝试了aggregate({$unwind:'$replies'}),但我仍然有评论和回复在2个不同的级别上,我无法使用聚合框架进行比较。也许有一种方法可以展平这两个级别,我对mongodb非常陌生。

请问您能否编辑您的问题,以包含您已尝试的聚合管道的详细信息? 我想您可能需要像这样做一些事情:http://docs.mongodb.org/manual/tutorial/aggregation-with-user-preference-data/#return-the-five-most-common-likes - WiredPrairie
你需要所有的属性吗?还是只需要它的ID和投票数就足够了?例如:[{id:'r1',votes:X}, 等等]? - Asya Kamsky
@AsyaKamsky 我还需要结果示例中提到的其他属性。 - lastid
2个回答

4
稍微改变架构以去规范化投票将使排序更容易。如果评论文档中嵌入了另一个投票数组,例如:
votes: [ 
  { "type" : "c", "_id": "c1", v: 1},
  { "type" : "r", "_id": "r1", v: 3},
  { "type" : "r", "_id": "r2", v: 2}
]

查询和排序可能很直观。
db.playground.aggregate(
[
  {$project: { votes: 1 }},
  {$unwind: "$votes"},
  {$sort: {"votes.v": -1}},
  {$limit: 2}
])

它会产生以下结果。
{
  "result" : [
    {
      "_id" : "c1",
      "votes" : {
        "type" : "c",
        "_id" : "c1",
        "v" : 3
      }
    },
    {
      "_id" : "c1",
      "votes" : {
        "type" : "r",
        "_id" : "r1",
        "v" : 2
      }
    }
  ],
  "ok" : 1
}

需要在“votes.v”上建立索引,因为这似乎是一种以读取为主的使用情况。更新评论时,只需在同一更新请求中更新投票数组即可。


如果我理解正确的话,这就是你要做的事情,因为我无法在评论中格式化代码,所以我创建了一个Github Gist 在这里。对我来说,请求2个最佳评论或回复似乎不是很直接。你能给我一个例子吗? - lastid
@lastid,你的代码片段看起来不错。我改进了我的答案,给出了一个聚合查询的示例。 - visualzhou
如我在问题和要点中所述,我需要2个最佳评论或回复及其内容(_text_)。通过您的解决方案,您可以获得最佳的2个ID,然后从这2个ID获取它们的内容,对吗?因此,您将至少有2个请求,也许是3个。您的数据结构也很棘手,如果您只需要1个回复及其投票,则需要从数组_replies_中获取回复以及_votes_属性,然后进行循环以了解哪个投票值适用于所讨论的回复。我已经在我的问题中提出了一个使用2个请求的解决方案,并且我更喜欢这种方式,因为_votes_与每个回复或评论相关联。 - lastid
关于获取它们的内容,是的,你说得对。实际上,你的解决方案很有道理,但它涉及客户端合并。现在我们正在讨论数据库端的解决方案。我的第一个想法是在评论和回复中投票,并在另一个数组中复制它们。这样查询回复就像往常一样。根本问题是您希望在检索评论和回复时将它们视为层次结构,并在投票它们时将它们视为相同的事物。因此,数据模型必须是混合的。对吧? - visualzhou
如果您最关心的是投票,将reply作为顶级文档存储在评论中,并将回复的_id存储在comment的数组中。因此,获取评论需要两个请求。顺便说一句,我认为使用当前的设计无法轻松地获取文档中数组中的单个项目,因为文档是处理的基本单位。 - visualzhou
我还没有测试过,但根据这个文档,如果我想的话,我可以只获取一个回复。 - lastid

0

我建议您不要在评论对象中放置回复。DbObject 最大大小为 16mb。如果一个评论有很多回复,这种结构将失败。相反,您可以将回复保存在评论集合中,并将父评论 id 放入回复对象中(您可能想要添加像 {type: 'reply'} 或 {type: 'comment'} 这样的字段,但 parentCommentId 的存在将提供哪个属于哪个类型)。这样,您可以轻松地查询评论和回复。

我还想补充说,聚合查询对 UI 响应不好。我不知道您的用例,但请不要忘记这一点。


如果我这样做,将需要2个查询来显示前10条评论及其回复。然后,由于我正在利用Meteor中的反应性,更容易将“回复”嵌入到每个“评论”中。我不知道聚合查询很慢... - lastid
聚合查询可能会很慢,这取决于是否有索引来支持它们以及它们所执行的工作量大小。 - Asya Kamsky
UI 操作很慢,尤其是分组。 - Mustafa Genç

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接