【问题标题】:mongodb/meteor: how to I get the value of one field corresponding to the $max value of another field?mongodb/meteor:如何获取一个字段的值对应于另一个字段的 $max 值?
【发布时间】:2013-10-04 22:40:06
【问题描述】:

我有一个包含以下字段的消息集合:_idsenderIdreceiverIddateSubmittedMsmessage,对于给定的用户,我想向他返回来自所有人的最新消息其他用户。因此,例如,如果有用户 Alex、Barb、Chuck、Dora,我想返回 Alex 与 Barb、Chuck 和 Dora 之间的最新消息。做这个的最好方式是什么?我可以使用聚合一步完成吗?

官方在线文档 (http://docs.mongodb.org/manual/reference/aggregation/min/) 中的聚合示例展示了如何在集合中找到各组的最低年龄,但我需要的是类似于在各组中找到最年轻的人的名字。

这是我目前的做法:

第 1 步:在 Alex 发送和接收的所有消息中找出 dateSubmitted 的最高值,并将其他用户分组:

var M = Messages.aggregate(
{$match: 
    {$or: [{senderId: 'Alex'}, {receiverId: 'Alex'}]}
}, 
{$group: {_id: "$receiverId", lastestSubmitted: {$max: "$submitted"} }}).fetch();

第 2 步:创建一个包含这些最高 dateSubmitted 值的数组:

var MIds = _.pluck(M,'lastestSubmitted');

第 3 步:按 senderId、receiverId 和 latestSubmitted 查找这些消息:

return Messages.find(
    {submitted: {$in: MIds}, $or: [{senderId: 'Alex'}, {receiverId: 'Alex'}]},
    {$sort: {submitted: 1}}
});

这样做有两个问题:

  1. 可以一步完成而不是三步吗?也许通过 mapReduce 或 Aggregate 命令?
  2. 除了只对receiverId: 'Alex' 进行分组外,有没有办法对类似$or [{receiverId: 'Alex', senderId: 'Barb'}, {senderId: 'Alex', receiverId: 'Barb'}] 的内容进行分组? (但对于其他每个用户)这将允许我在 Alex 与之交谈的任何两个参与者之间的对话中获得最新消息。比如:

有什么建议吗?

【问题讨论】:

    标签: mongodb meteor


    【解决方案1】:

    您唯一需要更改的是分组阶段中的 group._id。虽然您可以为此提议使用文档,而不仅仅是一个字段,但您可以在同一管道中应用排序。但是,如果您仅使用 receiverId 进行分组,则不会更改。

    var M = Messages.aggregate(
    {$match: 
        {$or: [{senderId: 'Alex'}, {receiverId: 'Alex'}]}
    }, 
    {$group: 
        {_id: {
              receiverId: "$receiverId", 
              senderId: "$senderId"}, 
          lastestSubmitted: {$max: "$submitted"} }
     },
     {$sort: {submitted: -1}
     },
     {$limit: 1}
     ).fetch();
    

    上面的这个例子只增加了检查第二个最近被 ping 的连接或第三个连接的可能性,如果你只可能得到你甚至不需要分组的最新消息。运行这个:

    var M = Messages.aggregate(
    {$match: 
        {$or: [{senderId: 'Alex'}, {receiverId: 'Alex'}]}
    }, 
     {$sort: {submitted: -1}
     },
     {$limit: 1}
     ).fetch();
    

    上面的答案是获取单个用户和其他人之间的最新消息,以获取涉及用户的所有对之间的最新消息阅读下>>>

    基于 cmets,我误解了这个问题,但上面的部分无论如何都是有用的。您的问题的正确解决方案如下。在这种情况下,困难在于获得用于分组的密钥并识别切换的对(bob -> tom == tom -> bob)。你可以使用条件和排序来识别掉期。 (这肯定是一个更难的问题)代码如下所示:

    var M = Messages.aggregate(
    {$match: 
        {$or: [{senderId: 'Alex'}, {receiverId: 'Alex'}]}
    },
    {$project:
        {'part1':{$cond:[{$gt:['$senderId','$receiverId']},'$senderId','$receiverId']},
         'part2':{$cond:[{$gt:['$senderId','$receiverId']},'$receiverId','$senderId']},
         'message':1,
         'submitted':1
        }
    },
    {$sort: {submitted: -1}},
    {$group: 
        {_id: {
              part1: "$part1", 
              part2: "$part2"}, 
          lastestSubmitted: {$first: "$submitted"},
          message: {$first: "$message"} }
     }
     ).fetch();
    

    如果您不熟悉上面使用的某些运算符,例如 $cond 或 $first,请查看this

    【讨论】:

    • 谢谢。我尝试实现它,如果 Meteor 支持聚合客户端,它可能会起作用,但正如我刚刚发现的 (stackoverflow.com/questions/11266977/…),meteor 还没有。有一个解决方法 (github.com/meteor/meteor/pull/644),但它不是被动的。我将在 mongodb shell 中尝试这个,看看我是否可以让它在那里工作,以确保。
    • 我在 shell 中进行了尝试,并将我在那里开发的代码复制粘贴到您的示例中。老实说,我没有在 Meteor 中尝试过,而我认为问题中的代码正在运行。我对你的测试很好奇,如果你能提供结果就太好了。无论如何,我希望这会有所帮助。
    • 您好@attish,感谢您的跟进。我尝试在 mongodb shell 中实现你的两个解决方案,但它们总体上只返回 1 个结果,而他们应该在每个对话中返回一个结果(如果 Alex 与 Barb 和 Chuck 有对话,那么应该返回两个结果:最新消息Barb 和 Chuck 的最新消息)。您是否能够获得一个以上的总体结果?
    • 对不起,我对你的问题有点误解,但相应地更新了我的答案
    • 非常感谢 - 我测试了它并且它有效!现在我将研究在客户端实施的解决方法。干杯。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-27
    相关资源
    最近更新 更多