Meteor 中的平均聚合查询 [英] Average Aggregation Queries in Meteor

查看:22
本文介绍了Meteor 中的平均聚合查询的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,仍然在我的玩具应用程序中,我想找出一组车主里程表的平均里程数.这在客户端上很容易,但不能扩展.对?但是在服务器上,我不知道如何完成它.

问题:

  1. 你如何在服务器上实现一些东西然后在客户端上使用它?
  2. 您如何使用 mongo 的 $avg 聚合函数来利用其优化的聚合函数?
  3. 或者替代 (2) 您如何在服务器上执行映射/缩减并使其可供客户端使用?

@HubertOG 的建议是使用 Meteor.call,这是有道理的,我这样做了:

# 客户端Template.mileage.average_miles = ->答案 = 空Meteor.call "average_mileage", (error, result) ->console.log得到平均里程结果#{result}"答案=结果console.log "但是等等,answer = #{answer}"回答# 服务器端Meteor.methods average_mileage:->console.log调用的服务器里程"总数 = 计数 = 0r = Mileage.find({}).forEach(里程)->总计 += 里程.里程计数 += 1console.log 服务器即将返回 #{total/count}"总数

这似乎工作正常,但事实并非如此,因为据我所知 Meteor.call 是一个异步调用,而 answer 将始终为空返回.在服务器上处理东西似乎是一个足够常见的用例,我一定是忽略了一些东西.那会是什么?

谢谢!

解决方案

从 Meteor 0.6.5 开始,集合 API 尚不支持聚合查询,因为没有(直接)方式对它们进行实时更新.但是,您仍然可以自己编写它们,并在 Meteor.publish 中提供它们,尽管结果将是静态的.在我看来,这样做仍然更可取,因为您可以合并多个聚合并使用客户端集合 API.

Meteor.publish("someAggregation", function (args) {var sub = this;//这适用于 Meteor 0.6.5var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;//您对 Mongo 聚合的参数.随心所欲地制作这些.var 管道 = [{ $match: doSomethingWith(args) },{ $组:{_id: whatWeAreGroupingWith(args),计数:{ $sum:1}}}];db.collection("server_collection_name").aggregate(管道,//需要包装回调,以便在 Fiber 中调用它.Meteor.bindEnvironment(功能(错误,结果){//将每个结果添加到订阅中._.each(结果,函数(e){//为聚合文档生成一个随机的一次性 idsub.addition("client_collection_name", Random.id(), {关键:e._id.somethingOfInterest,计数:e.count});});子准备好();},功能(错误){Meteor._debug("聚合时出错:" + error);}));});

以上是分组/计数聚合的示例.一些注意事项:

  • 执行此操作时,您自然会在 server_collection_name 上进行聚合,并将结果推送到名为 client_collection_name 的不同集合.
  • 此订阅不会生效,并且可能会在参数更改时更新,因此我们使用了一个非常简单的循环,将所有结果推送出去.
  • 聚合的结果没有 Mongo ObjectID,因此我们生成了一些我们自己的任意对象.
  • 对聚合的回调需要包含在 Fiber 中.我在这里使用 Meteor.bindEnvironment 但也可以使用 Future 进行更底层的控制.

如果您开始合并这些发布的结果,您需要仔细考虑随机生成的 ID 如何影响合并框.然而,一个简单的实现只是一个标准的数据库查询,除了它更方便地与 Meteor API 客户端一起使用.

TL;DR 版本:几乎任何时候您将数据从服务器中推出,publish 都比 method 更可取.>

有关不同聚合方式的更多信息,检查出这个帖子.

Ok, still in my toy app, I want to find out the average mileage on a group of car owners' odometers. This is pretty easy on the client but doesn't scale. Right? But on the server, I don't exactly see how to accomplish it.

Questions:

  1. How do you implement something on the server then use it on the client?
  2. How do you use the $avg aggregation function of mongo to leverage its optimized aggregation function?
  3. Or alternatively to (2) how do you do a map/reduce on the server and make it available to the client?

The suggestion by @HubertOG was to use Meteor.call, which makes sense and I did this:

# Client side
Template.mileage.average_miles = ->
  answer = null
  Meteor.call "average_mileage", (error, result) ->
    console.log "got average mileage result #{result}"
    answer = result
  console.log "but wait, answer = #{answer}"
  answer

# Server side
Meteor.methods average_mileage: ->
  console.log "server mileage called"
  total = count = 0
  r = Mileage.find({}).forEach (mileage) ->
    total += mileage.mileage
    count += 1
  console.log "server about to return #{total / count}"
  total / count

That would seem to work fine, but it doesn't because as near as I can tell Meteor.call is an asynchronous call and answer will always be a null return. Handling stuff on the server seems like a common enough use case that I must have just overlooked something. What would that be?

Thanks!

解决方案

As of Meteor 0.6.5, the collection API doesn't support aggregation queries yet because there's no (straightforward) way to do live updates on them. However, you can still write them yourself, and make them available in a Meteor.publish, although the result will be static. In my opinion, doing it this way is still preferable because you can merge multiple aggregations and use the client-side collection API.

Meteor.publish("someAggregation", function (args) {
    var sub = this;
    // This works for Meteor 0.6.5
    var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;

    // Your arguments to Mongo's aggregation. Make these however you want.
    var pipeline = [
        { $match: doSomethingWith(args) },
        { $group: {
            _id: whatWeAreGroupingWith(args),
            count: { $sum: 1 }
        }}
    ];

    db.collection("server_collection_name").aggregate(        
        pipeline,
        // Need to wrap the callback so it gets called in a Fiber.
        Meteor.bindEnvironment(
            function(err, result) {
                // Add each of the results to the subscription.
                _.each(result, function(e) {
                    // Generate a random disposable id for aggregated documents
                    sub.added("client_collection_name", Random.id(), {
                        key: e._id.somethingOfInterest,                        
                        count: e.count
                    });
                });
                sub.ready();
            },
            function(error) {
                Meteor._debug( "Error doing aggregation: " + error);
            }
        )
    );
});

The above is an example grouping/count aggregation. Some things of note:

  • When you do this, you'll naturally be doing an aggregation on server_collection_name and pushing the results to a different collection called client_collection_name.
  • This subscription isn't going to be live, and will probably be updated whenever the arguments change, so we use a really simple loop that just pushes all the results out.
  • The results of the aggregation don't have Mongo ObjectIDs, so we generate some arbitrary ones of our own.
  • The callback to the aggregation needs to be wrapped in a Fiber. I use Meteor.bindEnvironment here but one can also use a Future for more low-level control.

If you start combining the results of publications like these, you'll need to carefully consider how the randomly generated ids impact the merge box. However, a straightforward implementation of this is just a standard database query, except it is more convenient to use with Meteor APIs client-side.

TL;DR version: Almost anytime you are pushing data out from the server, a publish is preferable to a method.

For more information about different ways to do aggregation, check out this post.

这篇关于Meteor 中的平均聚合查询的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆