MongoDB分组查询

时间:2015-07-20 16:06:41

标签: mongodb meteor

我正在研究Meteor应用程序,我有每周时间表格式的数据 -

events:
    event:
       day: "Monday"
       time: "9am"
       location: "A"
    event:
       day: "Monday"
       time: "10am"
       location: "B"

每天都有很多条目。我可以运行一个返回格式对象的查询 -

day: "Monday"
   events:
      event:
         time: "9am"
         location: "A"
      event:
         time: "10am"
         location: "B"

我可以将对象存储为第二种格式,但更喜欢第一种格式,以便于删除和更新单个事件。

如果有一个很好的方法,我也希望按星期几的顺序返回它们。

2 个答案:

答案 0 :(得分:0)

要获得所需的结果,请使用 aggregation framework ,其中 $group 管道运算符对所有输入文档进行分组并应用累加器表达式< strong> $push 到该组以获取事件数组。

你的管道看起来像这样:

var Events = new Mongo.Collection('events');
var pipeline = [        
    {
        "$group": {
            "_id": "$day",
            "events": {
                "$push": {
                    "time": "$time"
                    "location": "$location"
                }
            }
        }
    },
    {
        "$project": {
            "_id": 0, "day": "$_id", "events": 1
        }
    }
];
var result = Events.aggregate(pipeline);
console.log(result)

您可以添加 meteorhacks:aggregate package 来实现Meteor中的聚合:

使用

添加到您的应用中
meteor add meteorhacks:aggregate

由于此程序包在Mongo.Collection实例上公开 .aggregate 方法,因此您可以定义获取聚合结果数组的方法。例如

if (Meteor.isServer) {
    var Events = new Mongo.Collection('events');
    Meteor.methods({
        getGroupedDailyEvents: function () {
            var pipeline = [        
                {
                    "$group": {
                        "_id": "$day",
                        "events": {
                            "$push": {
                                "time": "$time"
                                "location": "$location"
                            }
                        }
                    }
                },
                {
                    "$project": {
                        "_id": 0, "day": "$_id", "events": 1
                    }
                }
            ];
            var result = Events.aggregate(pipeline);
            return result;
        }
    }); 
}

if (Meteor.isClient) {
    Meteor.call('getGroupedDailyEvents', logResult);

    function logResult(err, res) {
        console.log("Result: ", res)
    }
}

答案 1 :(得分:0)

有几个选择:

  1. 您可以使用聚合命令但要注意,您将失去反应性:这意味着除非您重新加载模板,否则您将无法获得外部更新。您还需要使用a package to add the aggregation command to Meteor才能实现这一目标。

  2. 我个人的最爱:您不需要聚合(和松散的反应性)来实现数据转换。您可以使用简单的Collection.find()查询,并使用cursor.Observe()和条件修改的巧妙组合来扩展/缩小/修改它。看看这个答案,它为我做了伎俩(我需要一些黑色列表中的一些字段,但你可以轻松地将它调整到你的组/排序案例):https://stackoverflow.com/a/30813050/3793161。如果您需要有关此

  3. 的更多详细信息,请发表评论
  4. 如果您计划拥有多台服务器,请注意每台服务器都必须遵守,这样可能会导致不必要的负载。所以我的第三个解决方案是使用集合钩子或方法来更新包含每天/用户的每个事件的附加字段(无论你需要什么)。参见@David Weldon在这里回答:https://stackoverflow.com/a/31190896/3793161。在您的情况下,它可能意味着重新考虑您的数据库结构以满足您的需求(即添加更多字段,以便您在insert更新它们。
  5. 编辑以下是您对评论的一些看法:

    如果你坚持你在问题中描述的内容,你需要七个文件,每天一个,events字段,你可以放置所有事件。如果您需要在发送之前重新设计集合结构,那么我的第二个解决方案是好的。但是,在您的情况下,您只需要一个对象week,其中有7个子对象与星期几匹配。

    我建议你采取可能的方法:

    • 在方法中使用聚合,如@chridam所述。请注意,您将无法直接获取已排序的数组,如mongo $group文档中所述
      

    $ group未订购其输出文件

    因此,在返回方法结果之前,您需要使用例如_.sortBy()对它们进行排序(按天按天计算)。顺便说一句,如果您想知道方法调用中发生了什么, clientside ,这就是您应该如何编写调用:

    Meteor.call("getGroupedDailyEvents", userId, function(error, result){ 
        if(error){ 
            console.log("error", error); 
        } 
        if(result){ 
             //do whatever you need
        } 
    });
    
    • 在客户端进行数据排序。您正在寻找一种矫枉过正的解决方案,因为afaik,您不需要过滤任何数据以使其不受用户影响,并且您仍将发送数据(仅使用其他结构)。在模板中制作一个简单的帮助器要容易得多:

      Template.displaySchedule.helpers({
      
          "monday_events": function() {
              return _.sortBy (events.find({day:"Monday"}).fetch(), "time")
          },
          //add other days
      );
      

    它假设您的time字段的格式可以这种方式排序。如果没有,您只需要创建一个函数来根据它们的格式对它们进行排序,或者将原始格式更改为更适合的格式。

    其余的(HTML)只是使用{{#each monday_events}}

    迭代周一事件