Вместо того, чтобы заставить базу данных для возвращения результатов для данных, не существует, это лучшая практика для создания пустых данных внешних по отношению к запросу и сливаться результаты в них. Таким образом, у вас есть свои записи «0», где нет данных, и разрешить базе данных возвращать то, что есть.
Слияние - это основной процесс создания хеш-таблицы уникальных ключей и просто замена любых значений, найденных в результатах агрегации, в этой хэш-таблице. В JavaScript базовый объект подходит, так как все ключи уникальны.
Я также предпочитаю фактически возвращать объект Date
из результатов агрегирования, используя математику даты, чтобы манипулировать и «округлять» дату до требуемого интервала, а не использовать агенты агрегации даты. Вы можете манипулировать датами, используя $subtract
, чтобы превратить значение в числовое представление временной метки путем вычитания из другой даты с использованием значения даты эпохи и оператора $mod
, чтобы получить остаток и округлить дату до требуемого интервала.
В отличие от этого, используя $add
с аналогичным объектом даты эпохи, значение integer возвращается в дату BSON. И, конечно, гораздо эффективнее обрабатывать непосредственно $group
, а не использовать отдельный этап $project
, так как вы можете просто обрабатывать измененные даты непосредственно в значение группировки _id
.
Как оболочки, например:
var sample = 30,
Days = 30,
OneDay = (1000 * 60 * 60 * 24),
now = Date.now(),
Today = now - (now % OneDay) ,
nDaysAgo = Today - (OneDay * Days),
startDate = new Date(nDaysAgo),
endDate = new Date(Today + OneDay),
store = {};
var thisDay = new Date(nDaysAgo);
while (thisDay < endDate) {
store[thisDay] = 0;
thisDay = new Date(thisDay.valueOf() + OneDay);
}
db.datejunk.aggregate([
{ "$match": { "when": { "$gte": startDate } }},
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$subtract": [ "$when", new Date(0) ] },
{ "$mod": [
{ "$subtract": [ "$when", new Date(0) ] },
OneDay
]}
]},
new Date(0)
]
},
"count": { "$sum": 1 }
}}
]).forEach(function(result){
store[result._id] = result.count;
});
Object.keys(store).forEach(function(k) {
printjson({ "date": k, "count": store[k] })
});
который будет возвращать все дни в интервале включая 0
значений, где не существует никаких данных, как:
{ "date" : "Tue Sep 22 2015 10:00:00 GMT+1000 (AEST)", "count" : 0 }
{ "date" : "Wed Sep 23 2015 10:00:00 GMT+1000 (AEST)", "count" : 1 }
{ "date" : "Thu Sep 24 2015 10:00:00 GMT+1000 (AEST)", "count" : 0 }
{ "date" : "Fri Sep 25 2015 10:00:00 GMT+1000 (AEST)", "count" : 1 }
{ "date" : "Sat Sep 26 2015 10:00:00 GMT+1000 (AEST)", "count" : 1 }
{ "date" : "Sun Sep 27 2015 10:00:00 GMT+1000 (AEST)", "count" : 0 }
{ "date" : "Mon Sep 28 2015 10:00:00 GMT+1000 (AEST)", "count" : 1 }
{ "date" : "Tue Sep 29 2015 10:00:00 GMT+1000 (AEST)", "count" : 1 }
{ "date" : "Wed Sep 30 2015 10:00:00 GMT+1000 (AEST)", "count" : 0 }
{ "date" : "Thu Oct 01 2015 10:00:00 GMT+1000 (AEST)", "count" : 1 }
{ "date" : "Fri Oct 02 2015 10:00:00 GMT+1000 (AEST)", "count" : 2 }
{ "date" : "Sat Oct 03 2015 10:00:00 GMT+1000 (AEST)", "count" : 0 }
{ "date" : "Sun Oct 04 2015 11:00:00 GMT+1100 (AEST)", "count" : 1 }
{ "date" : "Mon Oct 05 2015 11:00:00 GMT+1100 (AEDT)", "count" : 0 }
{ "date" : "Tue Oct 06 2015 11:00:00 GMT+1100 (AEDT)", "count" : 1 }
{ "date" : "Wed Oct 07 2015 11:00:00 GMT+1100 (AEDT)", "count" : 2 }
{ "date" : "Thu Oct 08 2015 11:00:00 GMT+1100 (AEDT)", "count" : 2 }
{ "date" : "Fri Oct 09 2015 11:00:00 GMT+1100 (AEDT)", "count" : 1 }
{ "date" : "Sat Oct 10 2015 11:00:00 GMT+1100 (AEDT)", "count" : 1 }
{ "date" : "Sun Oct 11 2015 11:00:00 GMT+1100 (AEDT)", "count" : 1 }
{ "date" : "Mon Oct 12 2015 11:00:00 GMT+1100 (AEDT)", "count" : 0 }
{ "date" : "Tue Oct 13 2015 11:00:00 GMT+1100 (AEDT)", "count" : 3 }
{ "date" : "Wed Oct 14 2015 11:00:00 GMT+1100 (AEDT)", "count" : 2 }
{ "date" : "Thu Oct 15 2015 11:00:00 GMT+1100 (AEDT)", "count" : 2 }
{ "date" : "Fri Oct 16 2015 11:00:00 GMT+1100 (AEDT)", "count" : 0 }
{ "date" : "Sat Oct 17 2015 11:00:00 GMT+1100 (AEDT)", "count" : 3 }
{ "date" : "Sun Oct 18 2015 11:00:00 GMT+1100 (AEDT)", "count" : 0 }
{ "date" : "Mon Oct 19 2015 11:00:00 GMT+1100 (AEDT)", "count" : 0 }
{ "date" : "Tue Oct 20 2015 11:00:00 GMT+1100 (AEDT)", "count" : 0 }
{ "date" : "Wed Oct 21 2015 11:00:00 GMT+1100 (AEDT)", "count" : 2 }
{ "date" : "Thu Oct 22 2015 11:00:00 GMT+1100 (AEDT)", "count" : 1 }
отмечая, что все значения «дата» на самом деле все еще даты BSON, но просто строят так, как в выводе от .printjson()
в качестве метода оболочки.
немного более кратким примером может быть показано с помощью nodejs
, где вы можете использовать операции, такие как async.parallel
обрабатывать как построение хэш и запрос агрегации в то же время, а также другую полезную утилиту в nedb
, который реализует «хэш» используя функции, знакомые с использованием коллекции MongoDB. Он также показывает, как это можно масштабировать для больших результатов, используя реальную коллекцию MongoDB, если вы изменили обработку для потоковой обработки возвращаемого курсора из .aggregate()
:
var async = require('async'),
mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient,
nedb = require('nedb'),
DataStore = new nedb();
// Setup vars
var sample = 30,
Days = 30,
OneDay = (1000 * 60 * 60 * 24),
now = Date.now(),
Today = now - (now % OneDay) ,
nDaysAgo = Today - (OneDay * Days),
startDate = new Date(nDaysAgo),
endDate = new Date(Today + OneDay);
MongoClient.connect('mongodb://localhost/test',function(err,db) {
var coll = db.collection('datejunk');
async.series(
[
// Clear test collection
function(callback) {
coll.remove({},callback)
},
// Generate a random sample
function(callback) {
var bulk = coll.initializeUnorderedBulkOp();
while (sample--) {
bulk.insert({
"when": new Date(
Math.floor(
Math.random()*(Today-nDaysAgo+OneDay)+nDaysAgo
)
)
});
}
bulk.execute(callback);
},
// Aggregate data and dummy data
function(callback) {
console.log("generated");
async.parallel(
[
// Dummy data per day
function(callback) {
var thisDay = new Date(nDaysAgo);
async.whilst(
function() { return thisDay < endDate },
function(callback) {
DataStore.update(
{ "date": thisDay },
{ "$inc": { "count": 0 } },
{ "upsert": true },
function(err) {
thisDay = new Date(thisDay.valueOf() + OneDay);
callback(err);
}
);
},
callback
);
},
// Aggregate data in collection
function(callback) {
coll.aggregate(
[
{ "$match": { "when": { "$gte": startDate } } },
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$subtract": [ "$when", new Date(0) ] },
{ "$mod": [
{ "$subtract": [ "$when", new Date(0) ] },
OneDay
]}
]},
new Date(0)
]
},
"count": { "$sum": 1 }
}}
],
function(err,results) {
if (err) callback(err);
async.each(results,function(result,callback) {
DataStore.update(
{ "date": result._id },
{ "$inc": { "count": result.count } },
{ "upsert": true },
callback
);
},callback);
}
);
}
],
callback
);
}
],
// Return result or error
function(err) {
if (err) throw err;
DataStore.find({},{ "_id": 0 })
.sort({ "date": 1 })
.exec(function(err,results) {
if (err) throw err;
console.log(results);
db.close();
});
}
);
});
Это очень подходит для данных для диаграмм и графиков.Основная процедура одинакова для любой реализации языка и идеально выполняется в параллельной обработке для максимальной производительности, поэтому асинхронные или потоковые среды дают вам реальный бонус, хотя для небольшого образца, подобного этому, базовая хеш-таблица может быть сгенерирована в памяти очень быстро вашей среды требует последовательных операций.
Так что не пытайтесь заставить базу данных сделать это. Конечно, есть примеры SQL-запросов, которые делают это «слияние» на сервере базы данных, но это никогда не было действительно замечательной идеей и должно быть действительно обработано с помощью подобного процесса слияния «клиент», поскольку это просто создает накладные расходы на базу данных, t требуется.
Это очень эффективная и практичная цель, и, конечно же, она не требует обработки отдельного запроса на агрегирование для каждого дня в периоде, что было бы неэффективным вообще.
Я не думаю, что это было бы возможно. Возможно, вы захотите повторить все дни в месяц, а затем поместите счеты из mongodb. –
Можете ли вы снова объяснить этот вопрос? для меня оба выхода одинаковы, но у второго есть больше результатов. Что ты хочешь делать? – sergiuz
@SergiuZaharie Я хочу включить объекты в результирующий набор, где поле счетчика равно 0. – Andrew