Агрегатный запрос MongoDB, эквивалентный PostgreSQL

Этот вопрос состоит из двух частей. Структура коллекции:

_id: MongoID,
agent_id: строка,
результат: строка,
created_on: ISO DATE,
... другие поля ...

Часть первая:
Желаемый результат: Один результат для каждого agent_id и комбинации результатов со счетчиком: представление TUPLE с эквивалентным SQL с использованием PostgreSQL.

( "1234", "Success", 4 ),
( "1234", "Failure", 4 ),
( "4567", "Success", 3 ),
( "7896", "Failure", 2 ),
.....

SELECT agent_id, result, count(*)
FROM table
GROUP BY agent_id, result
HAVING created_on >= now()::date;

Я придумал следующий запрос mongo .... Я думаю, что у меня концептуальная или синтаксическая ошибка. В документах говорится, что нужно использовать $ match в начале конвейера:, но хотя $ match ограничивает запрос, когда я запускаю его сам по себе, как только я добавляю группу $, я получаю много результатов. Также я не могу понять, как группировать более чем по одному полю. Как я могу отредактировать приведенный ниже запрос, чтобы получить результаты, подобные приведенному выше SQL-запросу?

db.collection.aggregate(
  { $match : 
    { created_on: 
        { $gte: new Date('08-13-2012') //some arbitrary date
    }
  }, $group:
    { _id:"$agent_id" }, 
   $project:
  {_id:0, agent_id:1, result:1}
})

Часть 2) Первый набор результатов был бы адекватным, но не оптимальным. С PostgreSQL я могу получить такой результат, как:

( "1234", { "Success", "Failure" }, { 4, 3 } ),
( "4567", { "Success", "Failure" }, { 3, 0 } ),
( "7896", { "Success", "Failure" }, { 0, 2 } )

Я могу сделать это в Postgresql с типом данных массива и функцией set_to_array (настраиваемая функция). Специфический SQL для Pg:

SELECT agent_id, set_to_array(result), set_to_array( count(*) )
FROM table
GROUP BY agent_id, result
HAVING created_on >= now()::date;

Я считаю, что эквивалентная структура данных в mongodb будет выглядеть так:

[
   { "1234", [ { "success": 4 }, { "failure": 4 } ] },
   { "4567", [ { "success": 3 }, { "failure": 0 } ] },
   { "7896", [ { "success": 0 }, { "failure": 0 } ] }
]

Можно ли достичь этих желаемых сжатых результатов с помощью агрегатной структуры mongodb?


person Ketema    schedule 03.10.2012    source источник


Ответы (1)


Ну вот:

Созданы некоторые тестовые данные:

db.test.insert ({agent_id: "1234", результат: "Failure", created_on: new Date ()}); db.test.insert ({agent_id: "1234", результат: "Успех", created_on: new Date ()}); db.test.insert ({agent_id: "1234", результат: "Failure", created_on: new Date ()}); db.test.insert ({agent_id: "1234", результат: "Успех", created_on: new Date ()}); db.test.insert ({agent_id: "1234", результат: "Failure", created_on: new Date ()}); db.test.insert ({agent_id: "1234", результат: "Успех", created_on: new Date ()}); db.test.insert ({agent_id: "1234", результат: "Успех", created_on: new Date ()}); db.test.insert ({agent_id: "1324", результат: "Успех", created_on: new Date ()}); db.test.insert ({agent_id: "1324", результат: "Успех", created_on: new Date ()}); db.test.insert ({agent_id: "1324", результат: "Успех", created_on: new Date ()}); db.test.insert ({agent_id: "1324", результат: "Успех", created_on: new Date ()}); db.test.insert ({agent_id: "1324", результат: "Failure", created_on: new Date ()}); db.test.insert ({agent_id: "1324", результат: "Failure", created_on: new Date ()}); db.test.insert ({agent_id: "1324", результат: "Failure", created_on: new Date ()}); db.test.insert ({agent_id: "1324", результат: "Failure", created_on: new Date ()}); db.test.insert ({agent_id: "1324", результат: "Failure", created_on: new Date ()}); db.test.insert ({agent_id: "1324", результат: "Failure", created_on: new Date ()}); db.test.insert ({agent_id: "1324", результат: "Failure", created_on: new Date ()});

db.test.aggregate(
  {
    $match:{ /* filter out the things you want to aggregate */
      created_on:{$gte:new Date(1000000)}
    }
  }, 
  {
    $group: {_
      _id: { /* the things you want to group on go in the _id */
        agent_id:"$agent_id", 
        result:"$result"
      }, 
      count:{$sum:1} /* simple count */
    }
  }, 
  {
    $project: { /* take the id out into the separate fields for your tuple. */
      _id:0, 
      agent_id:"$_id.agent_id", 
      result:"$_id.result", 
      count:"$count"
    }
  });

Дает:

{
"result" : [
    {
        "count" : 7,
        "agent_id" : "1324",
        "result" : "Failure"
    },
    {
        "count" : 4,
        "agent_id" : "1324",
        "result" : "Success"
    },
    {
        "count" : 4,
        "agent_id" : "1234",
        "result" : "Success"
    },
    {
        "count" : 3,
        "agent_id" : "1234",
        "result" : "Failure"
    }
],
"ok" : 1
} 

Добавление части 2 - очень похоже на часть 1, но подсчет немного сложнее; в основном вы считаете, только если он соответствует тому, что вы хотите подсчитать:

db.test.aggregate(
  {
    $match: { 
      created_on: {$gte:new Date(1000000)}
    }
  }, 
  {
    $group: {
      _id: { 
        agent_id:"$agent_id"
      }, 
      failure: {
        $sum:{
          $cond:[
            {$eq:["$result","Failure"]}, 
            1, 
            0
          ]
        }
      }, 
      success: {
        $sum: { 
          $cond:[
            {$eq:["$result","Success"]}, 
            1, 
            0
          ]
        }
      } 
    } 
  }, 
  {
    $project: {
      _id: 0, 
      agent_id: "$_id.agent_id", 
      failure: "$failure", 
      success: "$success"
    }
  });

Дает:

{
"result" : [
    {
        "failure" : 7,
        "success" : 4,
        "agent_id" : "1324"
    },
    {
        "failure" : 3,
        "success" : 4,
        "agent_id" : "1234"
    }
],
"ok" : 1
}
person Eve Freeman    schedule 03.10.2012
comment
Можно ли расслабиться, чтобы получить общий итог? Мне удалось добавить промежуточные итоги к каждому результату (сумма неудач и успехов), но я не уверен, возможно ли создание записи, которая суммирует все успехи и неудачи агента в самом запросе. - person Ketema; 30.10.2012
comment
Вы имеете в виду в том же запросе или в новом запросе? - person Eve Freeman; 30.10.2012
comment
Было бы интересно, если бы результат мог иметь общую сумму в том же запросе, хотя не уверен, возможно ли это. - person Ketema; 06.11.2012
comment
Не думаю, что это сработает в том же запросе. Однако как только вы получите результаты, вы можете довольно легко вычислить его за пределами mongo. - person Eve Freeman; 10.11.2012