人们经常询问“空结果”,而这些想法通常来自他们如何使用 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 } ]
简而言之,“数据存储”是使用nedb 创建的,它的作用基本上与任何 MongoDB 集合相同(具有精简的功能)。然后,您可以为任何结果插入预期的“键”范围和默认值。
然后运行聚合语句,它只会返回查询集合中存在的键,您只需使用聚合值“更新”在同一键处创建的数据存储。
为了提高效率,我在parallel 中运行空结果“创建”和“聚合”操作,使用"upsert" 功能和$inc 运算符作为值。这些不会冲突,这意味着创建可以在聚合运行的同时发生,因此不会出现延迟。
这很容易集成到您的 API 中,因此您可以拥有所需的所有键,包括那些在输出集合中没有用于聚合的数据。
同样的方法非常适合在 MongoDB 服务器上使用另一个实际集合来处理非常大的结果集。但如果它们非常大,那么无论如何您都应该预先聚合结果,并且只使用标准查询进行采样。