Перевести с SQL на монго

Как это должно быть написано в MongoDB?

SELECT field1, field2, product, COUNT(product) as counter
FROM table
GROUP BY product
HAVING counter > 0

Я попытался со следующим, который является почти копией из руководства Доктрины.

$query = $this->createQueryBuilder('AcmeBundle:Product')
->group(array('product'), array('count' => 0))
->reduce('function (obj, prev) { prev.count++; }')
->field('product, field1, field2, count')
;

Это только дает мне 1 элемент «count = 21», который, кажется, взял все продукты и сосчитал их вместе.

0

Решение

Как и во многих реализациях ODM, ядро ​​доктрины (оберточная часть mongodDB) построено «поверх» основного интерфейса встроенного драйвера для MongoDB. Это позволяет получить доступ к этим объектам драйвера, чтобы вы могли реализовать собственные методы MongoDB для выполнения ваших запросов.

Я говорю это в целом, так как лучший вариант для вашего запроса структура агрегации MongoDB. Это высокопроизводительная реализация в нативном коде на сервере, поэтому она не использует интерпретацию JavaScript для методов типа «уменьшить», как это реализовано другими методами. Вообще говоря, DSL плохо сопоставляется с менеджерами, которые пытаются быть «всем» и, следовательно, генерировать SQL. Концепции совершенно разные, и поэтому оптимальны.

Оказывается, что есть Коллекция класс-обертка, который вы можете использовать, не углубляясь прямо в объект родного драйвера. У него есть свой .aggregate() метод обертки. Отсутствует подробная информация о том, как получить доступ к этому объекту, но вы можете проследить по ссылке из оригинальный коммит.

Но получить базовый объект драйвера довольно просто:

$mongoCollection = $dm->getConnection()->getMongo()
->selectCollection('collectionName');

$result = $mongoCollection->aggregate(
array(
array( '$group' => array(
'_id' => array(
'field1' => '$field1',
'field2' => '$field2',
'product' => '$product'
),
'counter' => array( '$sum' => 1 )
)),
array( '$match' => array(
'counter' => array( '$gt' => 0 )
)),
array( '$project' => array(
'field1' => '$_id.field1',
'field2' => '$_id.field2,
'product' => '$_id.product',
'counter' => 1
))
)
);

Который использует $group оператор трубопровода, чтобы сделать фактическую «группировку» через _id ключ указан. Это может быть «составной ключ», как и в этом случае, поэтому все поля взяты в комбинации. Любые поля, которые вы не хотите «группировать по», используются с такими операторами группировки, как $sum здесь, который поставляется со статическим значением 1 представлять «количество» матчей.

Если вы просто хотите «сгруппировать» значения полей «product», то другие поля в вашем результате также должны быть под оператор группировки. Может быть что-то вроде $first:

$result = $mongoCollection->aggregate(
array(
array( '$group' => array(
'_id' => '$product',
'field1' => array( '$first' => '$field1' ),
'field2' => array( '$first' => '$field2' ),
'counter' => array( '$sum' => 1 )
)),
array( '$match' => array(
'counter' => array( '$gt' => 0 )
)),
array( '$project' => array(
'_id' => 0,
'product' => '$_id',
'field1' => 1,
'field2' => 1,
'counter' => 1
))
)
);

Но любые поля, которые вы хотите, должны быть предметом «оператор группировки» или являются частью заявления «group by». То же самое верно для SQL.

Другие этапы «трубопровода» здесь в основном $match что делается «группировка постов», чтобы иметь тот же эффект, что и предложение «HAVING», и, наконец, $project который просто очищает выходные данные, чтобы получить нужные имена полей, а не объединяться в требуемые _id («группировать по»).

Вот как вы делаете агрегацию в MongoDB.

Для получения дополнительной информации об общих операциях SQL см. Диаграмма сопоставления SQL с агрегацией в основной документации.

0

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

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