У меня возникли проблемы с использованием структуры агрегации MongoDB для подсчета типов событий в моей базе данных. Как рассчитать сумму value.count
поле для каждого уникального третьего индекса _id.val
поле?
Основная структура моих данных выглядит так:
{ _id: { evt: "click", val: [ "default", "125", "311", "1" ] }, value: { count: 1 } }
{ _id: { evt: "click", val: [ "default", "154", "321", "2" ] }, value: { count: 2 } }
{ _id: { evt: "click", val: [ "default", "192", "263", "1" ] }, value: { count: 4 } }
Значения в val
поле обозначает ["type","x","y","time"]
соответственно.
Я пытаюсь извлечь третий индекс, или time
значение _id.val
ключ. Результат, который я ищу для достижения:
1: 5
2: 2
Я пытался сделать это через этот PHP:
$ops2 = array(
array(
'$match' => $q2
),
array(
'$group' => array(
'_id' => array(
'evt' => '$_id.evt',
'time' => '$_id.val.3'
),
'count' => array('$sum' => '$value.count' )
)
)
);
Но это не похоже на 3
индекс в групповом массиве
Во-первых, я думаю, что у вас может быть что-то не так в вашем понимании Монго … Потому что каждый документ в монго должен иметь свой уникальный _id, чтобы идентифицировать себя с другими. Поэтому я добавил _id к каждому объекту и изменил поле вашего происхождения «_id» на «data». Теперь структура:
/* 1 */
{
"_id" : "ubLrDptWvJE7LZqDF",
"data" : {
"evt" : "click",
"val" : [ "default", "125", "311", "1" ]
},
"value" : {
"count" : 1
}
}
/* 2 */
{
"_id" : "C2QCEhvCsp3xG6EKZ",
"data" : {
"evt" : "click",
"val" : [ "default", "154", "321", "2" ]
},
"value" : {
"count" : 2
}
}
/* 3 */
{
"_id" : "bT72z7gMKoyX5JfHL",
"data" : {
"evt" : "click",
"val" : [ "default", "192", "263", "1" ]
},
"value" : {
"count" : 4
}
}
Я не уверен, как сделать этот запрос в PHP, потому что я немного знаю PHP …… Но я мог бы привести пример использования агрегации в Javascript, его код и вывод выглядят следующим образом:
Вот несколько полезных ссылок: используя монго в PHP
Я хотел бы, чтобы это помогло вам решить вашу проблему отлично 🙂
Данные, с которыми вы работаете, выглядят так, как будто они уже являются результатом операции mapReduce, поскольку они имеют ту конкретную структуру «_id» и «value», которую выполняет mapReduce. Таким образом, вам лучше вернуться к логике реализации этого процесса и следовать тому же, чтобы просто извлечь и суммировать то, что вы хотите, или, по крайней мере, изменить форму вывода на следующую:
{
_id: {
evt: "click",
val: { "type": "default", "x": "125", "y": "311", "time": "1" }
},
value: { count: 1 }
},
{
_id: {
evt: "click",
val: { "type": "default", "x": "154", "y": "321", "time": "2" }
},
value: { count: 2 }
},
{
_id: {
evt: "click",
val: { "type": "default", "x": "192", "y": "263", "time": "1" }
},
value: { count: 4 }
}
Поскольку проблема заключается в том, что инфраструктуре агрегирования «в настоящее время» не хватает возможности обратиться к «индексированной» позиции массива (реальный «неассоциативный» массив, а не массив PHP) и всегда будет возвращать null
когда ты пытаешься сделать это.
Не имея возможности вернуться к исходной операции source или mapReduce, вы можете написать операцию mapReduce для этих данных, чтобы получить ожидаемые результаты (представление оболочки, так как в любом случае это будет JavaScript):
db.collection.mapReduce(
function() {
emit({ evt: this._id.evt, time: this._id.val[3] }, this.value.count)
},
function(key,values) {
return Array.sum(values)
},
{ out: { inline: 1 } }
)
Который возвращает типичный вывод mapReduce следующим образом:
{
"_id" : {
"evt" : "click",
"time" : "1"},
"value" : 5
},
{
"_id" : {
"evt" : "click",
"time" : "2"},
"value" : 2
}
Если бы вы смогли по крайней мере преобразовать текущую коллекцию выходных данных в форму, предложенную вначале выше, вы бы вместо этого работали с такой структурой агрегирования (снова общее представление):
{ "$group": {
"_id": {
"evt": "$_id.evt",
"time": "$_id.val.time"},
"count": { "$sum": "$value.count" }
}}
Что, конечно, получится из измененных данных:
{ "_id" : { "evt" : "click", "time" : "2" }, "count" : 2 }
{ "_id" : { "evt" : "click", "time" : "1" }, "count" : 5 }
В будущих выпусках MongoDB будет $slice
оператор, который разрешает обработку массива, поэтому с вашей текущей структурой вы можете сделать это вместо этого:
{ "$group": {
"_id": {
"evt": "$_id.evt",
"time": { "$slice": [ "$_id.val", 3,1 ] }
},
"count": { "$sum": "$value.count" }
}}
Что позволяет выбирать «третий» индексный элемент из массива, хотя это, конечно, все равно будет возвращать «массив» в качестве элемента, подобного этому:
{ "_id" : { "evt" : "click", "time" : [ "2" ] }, "count" : 2 }
{ "_id" : { "evt" : "click", "time" : [ "1" ] }, "count" : 5 }
Итак, прямо сейчас, если вы можете изменить исходный вывод mapReduce, сделайте это. Либо в форму, как показано здесь, либо просто работайте с изменениями исходного запроса, чтобы получить конечный результат, который вы хотите здесь. Изменение к рекомендованной форме, по крайней мере, позволит .aggregate()
Команда для работы, как показано во втором примере здесь.
Если нет, то mapReduce в настоящее время остается единственным способом записи, как показано в «первом» примере.