Проблема с датами — попытка построить данные MongoDB в Highcharts через переполнение стека

У меня есть эти данные в MongoDB:

{
"_id": ObjectId("542bxxxxxxxxxxxx"),
"TEMP_C": 13,
"time": ISODate("2014-08-21T05:30:00Z")
}

Я хочу сгруппировать его по дням и скормить в Highcharts, чтобы отображать среднее значение за день.
Как это: http://jsfiddle.net/tw7n6wxb/3/

Используя конвейер агрегации MongoDB, я смог выполнить группировку, основываясь на некоторых других примерах и этом замечательном посте: http://www.kamsky.org/stupid-tricks-with-mongodb/stupid-date-tricks-with-aggregation-framework

Дополнительный вопрос: почему так сложно группировать по дате в MongoDB ??? Самая раздражающая часть заключается в том, что необходимо заново скомпоновать объект даты после разбиения его на «$ dayOfMonth», «$ month» и «$ year».
Есть ли более простой способ сделать это?

В любом случае, я получил эту часть работы (я думаю). Это результат:

{
"_id" : {"sec":1409346800,"usec":0},
"avg" : 12
},
{
"_id" : {"sec":1409356800,"usec":0},
"avg" : 15
},

Но ряд Highcharts принимает массивы пар значений в качестве входных данных:
Пример: данные: [[5, 2], [6, 3], [8, 2]].
Первым значением в каждой паре является значение X, и это значение должно быть числом (когда ось X настроена как дата-время, значения X указываются в миллисекундах).

ПРОБЛЕМА, с которой я столкнулся, заключается в том, что MongoDB возвращает дату в виде объекта MongoDate с двумя значениями внутри: «sec» и «usec», в то время как Highcharts ожидает одно число.

Есть ли способ конвертировать объект MongoDate в целое число в конвейере? например, используя $ project?
Я использую PHP, но я бы хотел избежать постобработки в приложении (например, форматирование даты в PHP).

Или какие-нибудь другие идеи о том, как решить эту проблему?

Спасибо,

1

Решение

Кажется, вы просто хотите, чтобы значения меток времени возвращались из результата. Существует действительно простой способ сделать это в структуре агрегирования без использования операторы агрегации дат. Вместо этого вы можете использовать базовую «математику даты» и хитрость, с помощью которой можно извлечь значение «отметки времени» из объекта даты и манипулировать им:

db.collection.aggregate([
{ "$group": {
"_id": {
"$subtract": [
{ "$subtract": [ "$time", new Date("1970-01-01") ] },
{ "$mod": [
{ "$subtract": [ "$time", new Date("1970-01-01") ] },
1000 * 60 * 60 * 24
]}
]
},
"avg": { "$avg": "$TEMP_C" }
}}
])

Таким образом, основная «хитрость» заключается в том, что при вычитании одного объекта даты из другого (или аналогичной операции) возвращаемый результат представляет собой число в «миллисекундах» разницы во времени между ними. Таким образом, используя дату «эпоха» «1970-01-01», вы получаете значение «отметка времени эпохи» для объекта даты в виде числа.

Затем применяется базовая математика по дате, вычитая из этого значения модуль (или остаток) из миллисекунд в день. Это «округляет» значение для представления «дня», в который записана запись.

Мне нравится публиковать JSON, потому что он анализируется везде, но более PHP-способом, тогда вот так:

$collection->aggregate(array(
array( '$group' => array(
'_id' => array(
'$subtract' => array(
array( '$subtract' =>  array(
'$time', new MongoDate(strtotime("1970-01-01 00:00:00"))
) ),
array(  '$mod' => array(
array( '$subtract' =>  array(
'$time', new MongoDate(strtotime("1970-01-01 00:00:00"))
) ),
1000 * 60 * 60 * 24
))
)
),
"avg" => array( '$avg' => '$TEMP_C' )
))
))

Так что это немного чище, чем использовать операторы агрегации дат, чтобы получить желаемый результат. Конечно, это еще не «полный путь» к тому, как вы хотите, чтобы данные были представлены, где вы можете использовать их в клиенте.

Реальная вещь, которую нужно сделать здесь — это манипулировать результатом, чтобы получить желаемый формат вывода. Вероятно, это лучше подходит для того, чтобы ваш серверный код выполнял манипуляции перед возвратом ответа, но если у вас MongoDB 2.6 или выше, это «возможно» сделать в самом конвейере агрегации:

db.collection.aggregate([
{ "$group": {
"_id": {
"$subtract": [
{ "$subtract": [ "$time", new Date("1970-01-01") ] },
{ "$mod": [
{ "$subtract": [ "$time", new Date("1970-01-01") ] },
1000 * 60 * 60 * 24
]}
]
},
"avg": { "$avg": "$TEMP_C" }
}},
{ "$group": {
"_id": null,
"data": {
"$push": {
"$map": {
"input": { "$literal": [ 1,2 ] },
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el", 1 ] },
"$$_id",
"$avg"]
}
}
}
}
}}
])

Так что это действительно подлый на самом деле. После первоначальной «группировки», чтобы определить средние значения для каждого дня, вы получаете два поля в ваших документах результатов в день для _id а также avg, Что за $map оператор делает здесь принимает и массив в качестве входных данных (в данном случае, просто нумерованный шаблон с парой значений для определения позиции) и обрабатывает каждый элемент, чтобы вернуть массив, равный элементам, присутствующим в оригинале.

$cond Здесь оператор позволяет вам посмотреть на значение текущего элемента этого массива и «заменить его» другим значением, присутствующим в текущем документе. Таким образом, для каждого документа результаты содержат что-то вроде парного массива, например:

[ 1409346800000, 12 ]

Затем все, что происходит, — все результаты помещаются в один документ с массивом «data», который выглядит следующим образом:

{ "_id": null, "data": [ [..,..], [..,..], (...) ] }

Теперь ваш элемент данных в этом одном результате — это массив пар массивов, представляющих нужные вам точки.

Конечно, операторы любят $map доступны только из MongoDB 2.6 и более поздних версий, поэтому, если они у вас есть, вы можете использовать их, а в противном случае просто обработать результаты в коде с помощью аналогичной операции «map»:

function my_combine($v) {
return array($v["_id"],$v["avg"])
}

$newresult = array_map( "my_combine", $result )

Таким образом, это действительно сводится к манипулированию массивами, независимо от того, как вы к нему подходите, но уловка манипулирования датами также должна сэкономить вам некоторую работу по получению результатов в виде ожидаемых значений меток времени.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]