我尝试查询集合并检索除当前日期之外的最近7天中的每一天的平均值。在某些或所有日子里可能没有平均值。
这是我到目前为止所拥有的:
var dateTill = moment({hour:0,minute:0}).subtract(1, 'days')._d
var dateSevenDaysAgo = moment({hour:0,minute:0}).subtract(7, 'days')._d;
Rating.aggregate([
{
$match:{
userTo:facebookId,
timestamp:{$gt:dateSevenDaysAgo,$lt:dateTill}
}
},
{
$group:{
_id:{day:{'$dayOfMonth':'$timestamp'}},
average:{$avg:'$rating'}
}
},
{
$sort:{
'_id.day':1
}
}
]
这给了我
[ { _id: { day: 20 }, average: 1 },
{ _id: { day: 22 }, average: 3 },
{ _id: { day: 24 }, average: 5 } ]
我想要获得的是:
[1,,3,,5,,]
代表平均最后7天的顺序,并且有一个空元素,当天没有平均值。
我可以尝试制作一个能够检测出差距在哪里的功能,但是当平均值分布在两个不同的月份时,这种功能不会起作用。例如(8月28,29,30,31,8月1日) - 八月的日子将被分类到我想要的阵列的前面。
有更简单的方法吗?
谢谢!
答案 0 :(得分:4)
人们询问"空结果"很多时候,这种想法通常来自于他们如何通过SQL查询来解决问题。
但尽管它可能"可能"抛出一组"空结果"对于不包含分组键的项目,这是一个困难的过程,就像人们使用的SQL方法一样,它只是人为地将这些值放在语句中,它实际上不是一个性能驱动的替代方案。想想加入"用一组制造的钥匙。效率不高。
更智能的方法是直接在客户端API中准备好这些结果,而无需发送到服务器。然后聚合输出可以"合并"用这些结果来创建一套完整的。
但是你想要存储要合并的集合取决于你,它只需要一个基本的"哈希表"和查找。但这是一个使用nedb的示例,它允许您维护MongoDB的查询和更新思路:
var async = require('async'),
mongoose = require('mongoose'),
DataStore = require('nedb'),
Schema = mongoose.Schema,
db = new DataStore();
mongoose.connect('mongodb://localhost/test');
var Test = mongoose.model(
'Test',
new Schema({},{ strict: false }),
"testdata"
);
var testdata = [
{ "createDate": new Date("2015-07-20"), "value": 2 },
{ "createDate": new Date("2015-07-20"), "value": 4 },
{ "createDate": new Date("2015-07-22"), "value": 4 },
{ "createDate": new Date("2015-07-22"), "value": 6 },
{ "createDate": new Date("2015-07-24"), "value": 6 },
{ "createDate": new Date("2015-07-24"), "value": 8 }
];
var startDate = new Date("2015-07-20"),
endDate = new Date("2015-07-27"),
oneDay = 1000 * 60 * 60 * 24;
async.series(
[
function(callback) {
Test.remove({},callback);
},
function(callback) {
async.each(testdata,function(data,callback) {
Test.create(data,callback);
},callback);
},
function(callback) {
async.parallel(
[
function(callback) {
var tempDate = new Date( startDate.valueOf() );
async.whilst(
function() {
return tempDate.valueOf() <= endDate.valueOf();
},
function(callback) {
var day = tempDate.getUTCDate();
db.update(
{ "day": day },
{ "$inc": { "average": 0 } },
{ "upsert": true },
function(err) {
tempDate = new Date(
tempDate.valueOf() + oneDay
);
callback(err);
}
);
},
callback
);
},
function(callback) {
Test.aggregate(
[
{ "$match": {
"createDate": {
"$gte": startDate,
"$lt": new Date( endDate.valueOf() + oneDay )
}
}},
{ "$group": {
"_id": { "$dayOfMonth": "$createDate" },
"average": { "$avg": "$value" }
}}
],
function(err,results) {
if (err) callback(err);
async.each(results,function(result,callback) {
db.update(
{ "day": result._id },
{ "$inc": { "average": result.average } },
{ "upsert": true },
callback
)
},callback);
}
);
}
],
callback
);
}
],
function(err) {
if (err) throw err;
db.find({},{ "_id": 0 }).sort({ "day": 1 }).exec(function(err,result) {
console.log(result);
mongoose.disconnect();
});
}
);
这给出了这个输出:
[ { day: 20, average: 3 },
{ day: 21, average: 0 },
{ day: 22, average: 5 },
{ day: 23, average: 0 },
{ day: 24, average: 7 },
{ day: 25, average: 0 },
{ day: 26, average: 0 },
{ day: 27, average: 0 } ]
简而言之,&#34;数据存储&#34;是使用nedb
创建的,它基本上与任何MongoDB集合相同(具有精简功能)。然后,您插入一系列&#34;键&#34;任何结果的预期值和默认值。
然后运行您的聚合语句,它只返回查询集合中存在的键,您只需&#34;更新&#34;创建的数据存储区在具有聚合值的同一个密钥上。
为了提高效率,我正在运行空结果&#34;创建&#34;和#34;聚合&#34; parallel中的操作,使用"upsert" functionallity和值$inc
运算符。这些不会发生冲突,这意味着创建可以在聚合运行的同时发生,因此没有延迟。
这很容易集成到您的API中,因此您可以拥有所需的所有密钥,包括那些在集合中没有数据聚合的输出密钥。
同样的方法很适合在MongoDB服务器上使用另一个实际集合来处理非常大的结果集。但是如果它们非常大,那么你应该预先聚合结果,并且只使用标准查询进行采样。