好的,仍然在我的玩具应用程序中,我想找出一组车主的里程表的平均里程数。这在客户端非常简单,但不能扩展。对?但是在服务器上,我并没有完全看到如何实现它。
问题:
@HubertOG的建议是使用Meteor.call,这是有意义的,我这样做了:
# 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
这似乎工作正常,但它并不是因为我可以告诉Meteor.call
是一个异步调用,而answer
将始终是一个空返回。处理服务器上的东西似乎是一个常见的用例,我必须忽略一些东西。那会是什么?
谢谢!
答案 0 :(得分:29)
从Meteor 0.6.5开始,集合API还不支持聚合查询,因为没有(直接)方式对它们进行实时更新。但是,您仍然可以自己编写它们,并在Meteor.publish
中使它们可用,但结果将是静态的。在我看来,这样做仍然是可取的,因为您可以合并多个聚合并使用客户端集合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);
}
)
);
});
以上是分组/计数聚合的示例。有些注意事项:
server_collection_name
上进行聚合,并将结果推送到名为client_collection_name
的其他集合。Meteor.bindEnvironment
,但也可以使用Future
进行更低级别的控制。如果您开始合并这些出版物的结果,则需要仔细考虑随机生成的ID如何影响合并框。但是,直接实现这个只是一个标准的数据库查询,除了使用Meteor API客户端更方便。
TL; DR版:几乎在您从服务器推送数据的任何时候,publish
都优于method
。
有关不同聚合方式的详情, check out this post 。
答案 1 :(得分:2)
我使用'聚合'方法。 (版本0.7.x)
if(Meteor.isServer){
Future = Npm.require('fibers/future');
Meteor.methods({
'aggregate' : function(param){
var fut = new Future();
MongoInternals.defaultRemoteCollectionDriver().mongo._getCollection(param.collection).aggregate(param.pipe,function(err, result){
fut.return(result);
});
return fut.wait();
}
,'test':function(param){
var _param = {
pipe : [
{ $unwind:'$data' },
{ $match:{
'data.y':"2031",
'data.m':'01',
'data.d':'01'
}},
{ $project : {
'_id':0
,'project_id' : "$project_id"
,'idx' : "$data.idx"
,'y' : '$data.y'
,'m' : '$data.m'
,'d' : '$data.d'
}}
],
collection:"yourCollection"
}
Meteor.call('aggregate',_param);
}
});
}
答案 2 :(得分:1)
您可以使用Meteor.methods。
// server
Meteor.methods({
average: function() {
...
return something;
},
});
// client
var _avg = { /* Create an object to store value and dependency */
dep: new Deps.Dependency();
};
Template.mileage.rendered = function() {
_avg.init = true;
};
Template.mileage.averageMiles = function() {
_avg.dep.depend(); /* Make the function rerun when _avg.dep is touched */
if(_avg.init) { /* Fetch the value from the server if not yet done */
_avg.init = false;
Meteor.call('average', function(error, result) {
_avg.val = result;
_avg.dep.changed(); /* Rerun the helper */
});
}
return _avg.val;
});
答案 3 :(得分:1)
如果您想要反应,请使用Meteor.publish
代替Meteor.call
。在docs中有一个例子,他们在那里发布给定房间中的消息数量(就在this.userId
的文档上方),你应该可以做类似的事情。