У меня есть 2 коллекции: слова и фразы. Каждый документ слова имеет массив идентификаторов фраз. И каждая фраза может быть активной или неактивной.
Например :
слова:
{"word" => "hello", фразы => [1,2]}
{"word" => "table", фразы => [2]}
фразы:
{"id" => 1, "phrase" => "hello world!", "active" => 1}
{"id" => 2, "фраза" => "привет, я уже купил новую таблицу", "active" => 0}
Мне нужно получить количество активных фраз для каждого слова.
В php я делаю это так:
1. получить все слова
2. для каждого слова получить количество активных фраз с условием ['active' => 1]
Вопрос: Как я могу получить слова с активными фразами в одном запросе? Я попытался использовать MapReduce, но мне нужно сделать запрос для каждого слова, чтобы получить количество активных фраз.
UPD: В моей тестовой коллекции есть 92 000 фраз и 23 000 слов.
Я уже тестировал оба варианта: с петлей php для каждого слова, в котором я получаю фразу count и функцию aggreagation в манго.
Но я изменил конвейер агрегации в кометах ниже из-за фраз_данных. Это массив, поэтому я не могу использовать $ match на нем. Я использую $ recind после $ lookup.
[ '$unwind' => '$5'], [ '$lookup' => [ 'from' => 'phrases_926ee3bc9fa72b029e028ec90e282072ea0721d1', 'localField' => '5', 'foreignField' => '0', 'as' => 'phrases_data' ] ], [ '$unwind' => '$phrases_data'], [ '$match' => [ 'phrases_data.3' => 77] ], //phrases_data.3 => 77 it is similar to phrases_data.active => 1 [ '$group' => [ '_id' => ['word' => '$1', 'id' => '$0'], 'active_count' => [ '$sum' => 1] ] ], [ '$match' => [ 'active_count' => ['$gt' => 0]] ], [ '$sort' => [ 'active_count' => -1 ] ]
Проблема состоит в том, что команда $ group занимает 80% времени процесса. И это намного медленнее, чем петля php. Вот мои результаты для коллекции тестов:
1. Php loop (get words-> get phrases count for each word): 10 seconds 2. Aggregation function : 20 seconds
db.words.aggregate([ { "$unwind" : "$phrases"}, { "$lookup": { "from": "phrases", "localField": "phrases", "foreignField": "id", "as": "phrases_data" } }, { "$match" : { "phrases_data.active" : 1} }, { "$group" : { "_id" : "$word", "active_count" : { $sum : 1 } } } ]);
Вы можете использовать вышеуказанный конвейер агрегации:
Вы можете использовать ниже агрегатный трубопровод в 3.4.
Вам не нужно $unwind
идентификаторы массивов в версиях 3.3.4
и после.
https://stackoverflow.com/a/36647133/2683814
Следующий запрос присоединяется к words
с набором phrases
за которыми следует $filter + $size
для подсчета активных строк.
<?php $manager = new MongoDB\Driver\Manager("mongodb://localhost:27017"); $pipeline = [ [ '$lookup' => [ 'from' => 'phrases', 'localField' => 'phrases', 'foreignField' => 'id', 'as' => 'phrases' ] ], [ '$addFields' => [ 'phrases' => [ '$size'=> [ [ '$filter' => [ 'input' => '$phrases', 'as' => 'phrase', 'cond' => [ '$eq' => [ '$$phrase.active', 1, ] ], ], ], ], ], '_id' => 0 ], ], ]; $command = new \MongoDB\Driver\Command([ 'aggregate' => 'words', 'pipeline' => $pipeline ]); $cursor = $manager->executeCommand('test', $command); foreach($cursor as $key => $document) { var_dump($document); } ?>