$ expr arrayElementAt не работает в агрегации для встроенного документа - PullRequest
0 голосов
/ 13 ноября 2018

Я выполняю агрегацию mongo db, как

$cursor = $this->collection->aggregate(
            array(
                array(
                    '$project' => array(
                        'FullName' => array('$concat' => array('$first_name', ' ', '$middle_name', ' ', '$last_name')),
                        'FirstMiddle' => array('$concat' => array('$first_name', ' ', '$middle_name')),
                        'FirstLast' => array('$concat' => array('$first_name', ' ', '$last_name')),
                        'FirstName' => array('$concat' => array('$first_name')),
                        'MiddleName' => array('$concat' => array('$middle_name')),
                        'LastName' => array('$concat' => array('$last_name')),
                        'Student' => '$$ROOT'
                    )
                ),
                array(
                    '$match' =>
                    array(
                        '$or' => array(
                            array("FullName" => new MongoDB\BSON\Regex($arg, 'i')),
                            array("FirstLast" => new MongoDB\BSON\Regex($arg, 'i')),
                            array("FirstMiddle" => new MongoDB\BSON\Regex($arg, 'i')),
                            array("FirstName" => new MongoDB\BSON\Regex($arg, 'i')),
                            array("MiddleName" => new MongoDB\BSON\Regex($arg, 'i')),
                            array("LastName" => new MongoDB\BSON\Regex($arg, 'i')),
                            array("Student.registration_temp_perm_no" => $arg),
                           '$expr' => array(
                               '$eq'=> array(
                                     array('$arrayElemAt' => array('$allotment_details.room_id', -1)), $this->RoomId)),
                        ),
                       // "Student.assigned_keys" => ['$exists' => false],
                        "Student.schoolId" => new MongoDB\BSON\ObjectID($this->SchoolId)
                    )
                )
            )
 );

У меня есть коллекция, которая содержит данные, такие как

"first_name": "John",
"middle_name": "",
"last_name": "Mayor",
"allotment_details": [
 {
    "allotment_id": "ff666d55-2fcc-79b2-e4da-e165939555bb",
    "room_id": "5be2d9aad2ccda0fdc006a65",
    "bay_id": ObjectId("5be2d9aad2ccda0fdc006a61"),
     ...
}

Приведенный выше код работает для точной конкатенации трех типов имен и поиска по всемданные переданы в $arg.Обратите внимание, что я добавил array('$arrayElemAt' => array('$allotment_details.room_id', -1)), $this->RoomId)) для того, чтобы выбрать студентов на основе объединения имен, и эти студенты должны быть выбраны на основе $this->RoomId.

Приведенный выше код не выбирает студентов, назначенных комнате, ноизвлекает всех студентов на основе объединения имен.Пожалуйста, помогите !!!

1 Ответ

0 голосов
/ 13 ноября 2018

Быстрое исправление

Ваш «конвейер» здесь не работает , прежде всего , потому что в вашем начальном $project отсутствует поле, которое вы хотите использовать на более поздней стадии. Следовательно, «быстрое исправление» в основном включает это поле в «проецируемый» документ, так как работают этапы конвейера агрегации:

array(
  array(
    '$project' => array(
      'FullName' => array('$concat' => array('$first_name', ' ', '$middle_name', ' ', '$last_name')),
      'FirstMiddle' => array('$concat' => array('$first_name', ' ', '$middle_name')),
      'FirstLast' => array('$concat' => array('$first_name', ' ', '$last_name')),
      'FirstName' => array('$concat' => array('$first_name')),
      'MiddleName' => array('$concat' => array('$middle_name')),
      'LastName' => array('$concat' => array('$last_name')),
      'Student' => '$$ROOT',
      'allotment_details' => 1 # that's the change
    )
  ),

Или даже если вы все равно использовали $$ROOT для Student, просто укажите поле под этим путем:

'$expr' => array(
  '$eq'=> array(
    array('$arrayElemAt' => array('$Student.allotment_details.room_id', -1)),
    $this->RoomId
  )
),

однако Я бы сильно * умоляю вас сделать НЕ сделать это.

Сама концепция «конкатенации строк» ​​для последующего $match контента является действительно плохой идеей, поскольку она означает, что вся коллекция переписывается в конвейере перед любой «фильтрацией» на самом деле сделано.

Аналогичным образом, поиск соответствия для «последнего» элемента массива также является проблемой. Гораздо лучший подход - вместо этого фактически добавлять «новые элементы» в «начало» массива вместо «конца». Это именно то, что $position или, возможно, даже модификаторы $sort до $push делают для вас, изменяя место добавления предметов или отсортированный порядок элементов соответственно.

Изменение массива на «сначала новый»

Это займет немного работы, изменив способ хранения вещей, но выгода значительно улучшит скорость таких запросов, которые вы хотите, без необходимости оценивать аргумент $expr.

Основная концепция состоит в том, чтобы «предварительно ожидать» новые элементы массива с синтаксисом, подобным:

$this->collection->updateOne(
  $query,
  [ '$push' => [ 'allotment_details' => [ '$each' => $allotments, '$position' => 0 ] ] ]
)

Где $alloments должен быть массивом, как требуется для $each и $position используется для 0 для добавления новый элемент массива "first".

С другой стороны, если у вас действительно есть что-то вроде created_date в качестве свойства внутри каждого из объектов в массиве, тогда вы "можете" использовать что-то вроде $sort в качестве модификатора.

$this->collection->updateOne(
  $query,
  [ '$push' => [
      'allotment_details' => [ '$each' => $allotments, '$sort' => [ 'created_date' => -1 ] ]
  ]]
)

Это действительно зависит от того, зависят ли ваши «запрос» и другие требования доступа от «последнего добавления» или «последней даты», а также, как правило, от того, хотите ли вы изменить такое свойство created_date или другое свойство «sort» в способ, который будет влиять на порядок элементов массива при "сортировке".

Причина, по которой вы делаете это, заключается в том, что элемент «последний» (который теперь является «первым») в массиве просто становится:

$this->collection->find([
 'allotment_details.0.room_id': $this->RoomId
])

MongoDB позволяет указывать «первый» индекс массива с помощью «Точечная нотация» , используя индекс 0. То, что вы не можете сделать, это указать «отрицательный» индекс, т.е.:

$this->collection->find([
 'allotment_details.-1.room_id': $this->RoomId  # not allowed :(
])

Это причина, по которой вы делаете то, что было показано выше при «обновлении», чтобы «переупорядочить» ваш массив в работоспособную форму.

Конкатенация плохая

Другая основная проблема - это конкатенация строк. Как уже упоминалось, это создает ненужные накладные расходы только для того, чтобы выполнить необходимое сравнение. Это также «ненужно», поскольку вы можете избежать этого, используя $or с условиями для каждого из полей, поскольку они уже существуют в фактическом документе:

 $this->collection->find([
   '$or' => [
       [ 'first_name' => new MongoDB\BSON\Regex($arg, 'i') ],
       [ 'last_name' => new MongoDB\BSON\Regex($arg, 'i') ],
       [ 'middle_name' => new MongoDB\BSON\Regex($arg, 'i') ],
       [ 'registration_temp_perm_no' => $arg ]
   ],
   'schoolId' => new MongoDB\BSON\ObjectID($this->SchoolId),
   'allotment_details.0.room_id': $this->RoomId
 ])

И, конечно, какими бы ни были «полные» условия запроса, но вы должны понять основную идею.

Также, если вы на самом деле не ищете «частичные слова», тогда «текстовый поиск» определен над полями с «именами». После создания индекса это будет:

 $this->collection->find([
   '$text' => [ '$search' => $arg ],
   'schoolId' => new MongoDB\BSON\ObjectID($this->SchoolId),
   'allotment_details.0.room_id': $this->RoomId
 ])

В целом, я действительно рекомендовал бы присмотреться ко всем остальным параметрам, а не вносить одно небольшое изменение в существующий код. С небольшой тщательной реструктуризацией того, как вы храните вещи и действительно «индексируете» вещи, вы получаете огромные преимущества в производительности, которые просто не может обеспечить ваш обширный $concat подход «грубой силы».

N.B Современные версии PHP обычно поддерживают [] как гораздо более краткое представление array(). Это намного чище и намного легче читать. Поэтому, пожалуйста, используйте его.

...