Как это должно быть написано в 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», который, кажется, взял все продукты и сосчитал их вместе.
Как и во многих реализациях 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 с агрегацией в основной документации.
Других решений пока нет …