在Meteor集合上进行“group by”查询

6

我的数据mongoDB

>db.CUSTOMER.find()

{"Name": "A", "CreatedDate": "Wed Jan 29 2014"}

{"Name": "B", "CreatedDate": "Fri Jan 31 2014"}

{"Name": "C", "CreatedDate": "Sat Feb 01 2014"}

{"Name": "D", "CreatedDate": "Sat Feb 01 2014"}

在Meteor中:
Customer = new Meteor.Collection("CUSTOMER");

我正在尝试将它们按日期(星期一、星期二、星期三等)分组到Meteor集合中,并与数据的总和一起显示。应该像这样:
{"Date": "Wed Jan 29 2014", "Total" 1}

{"Date": "Fri Jan 31 2014", "Total" 1}

{"Date": "Sat Feb 01 2014", "Total" 2}

在MongoDB中,我会选择使用http://docs.mongodb.org/manual/reference/method/db.collection.group/。但是在Meteor中似乎不可能实现,因为它不支持findAndModify、upsert、aggregate函数和map/reduce。
有没有任何解决方法的示例可以让它正常工作?
谢谢

你提到的 Meteor 支持不足是来自浏览器端的客户端问题。你可以在服务器端执行操作。请参考答案。 - Neil Lunn
4个回答

11
您需要手动将它们分组。有许多方法可以做到这一点,但这里是一个(无可否认难以阅读的)示例:
var customers = Customer.find().fetch();

var groupedDates = _.groupBy(_.pluck(customers, 'CreatedDate'));

_.each(_.values(groupedDates), function(dates) {
  console.log({Date: dates[0], Total: dates.length});
});

谢谢,它运行得非常好。这也引出了另一个问题:似乎这个答案是基于CreatedDate的差异对数据进行分组的。如果我想按周、月或年(例如)进行分组,是否可能? - shihandrana
当然,你可以向 groupBy 提供第二个参数,例如 function(date){ return Date.split()[1]; }(这假设你的日期是字符串 - 如有必要,请进行修改以适应日期对象)。还请注意 Flying Fisher 的答案,使用 countBy - 同样的思路。 - David Weldon
这个解决方案不是很慢吗?您是否建议将整个集合订阅到客户端,然后执行 _.groupBy? - Jaro
1
请记住,一旦返回一个数组,您就失去了直接迭代游标的巨大优势:每当数据发生更改时,整个模板都会重新渲染,而不仅仅是更改的数据。如果您在模板中显示大量数据,则需要进行查询级别的聚合。 - Loupax

7
你可以在Meteor中使用那些你所说的函数,或者找到描述客户端处理的链接,但从服务器端来看是不同的。
大多数情况下,你应该使用aggregate而不是mapReduce或group函数。整个管道是用C++实现的,而不是传递给JavaScript引擎。在所有情况下,聚合管道中的等效函数将更快。
> db.dates.aggregate([
    {$group: { _id: {$dayOfWeek: "$CreatedDate"}, Total: {$sum: 1} }},
    {$project: { _id: 0, day: "$_id", Total: 1 } },
    {$sort: { day: 1 }}
  ])


  {
    "result" : [
            {
                    "Total" : 1,
                    "day" : 4
            },
            {
                    "Total" : 1,
                    "day" : 6
            },
            {
                    "Total" : 2,
                    "day" : 7
            }
    ],
    "ok" : 1
  }

Meteor支持

Meteor在服务器端使用node-mongodb-native驱动程序进行所有处理。它由MongoDB维护并维护所有功能。

客户端是一个名为"mini-mongo"的库。这实际上不是一个驱动程序,而是一个小层,实际上使用套接字连接将详细信息传递到服务器。它使您的客户端javascript“看起来”与服务器端代码相同,但您实际上并没有直接与mongo通信。

使用在“发布”到client的服务器端函数调用您的自定义代码在server上没有问题。如前所述,它只是您可以在其他nodejs应用程序中使用的标准驱动程序。


3

我想添加评论,但我的声望太低了。 只需使用_.countBy可能更易读。

_.countBy(customers,function(customer){return customer.CreatedDate}) 这应该返回一个字典([CreatedDate]:count)

希望这能帮到你


到目前为止,我无法获取或打印该值。有什么建议可以获取数据吗? - shihandrana
var dict = _.countBy(customers,function(customer){return customer.CreatedDate});Object.keys(dict).forEach(function(key){console.log("key",key,"count",dict[key])}) - Flying Fisher

2

补充以上回答:

  • 你不能在客户端使用所有的mongo函数,因为meteor依赖于mini-mongo,它只实现了mongo函数的子集,并且不是特别高效。因此,在客户端,你最好选择一些underscore函数,如@david weldon所述。据我所知,mini-mongo也只是一堆underscore函数,并且只对id进行索引。
  • 你仍然可以在服务器上使用所有的mongo函数,但这可能会感觉有点hacky,因为Meteor试图将它们隐藏在其包装后面。不过,这可能会更加高效。以下是它的样子:

.

mongoCustomer = Customer._collection.rawCollection() // you're now on the mongo realm

mongoCustomer.aggregate([
  {
    $group: {
      _id: "$CreatedDate",
      Total: { $sum: 1 }
    }
  }
], function(err, agg) {
  if (err) {
    ...
    return;
  }
  // agg an array of { CreatedDate, Total }, do what you want with it
});

这很好,但您也会失去 Meteor 提供的同步查询语法 Customer.find(...).fetch()。现在,您可以将 mongo 聚合操作包装在 Promise 中,并使用 await/async 如果想要恢复该语法。


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