Сортировать по условию соответствия результатов вверху

Я написал запрос агрегации mongodb на php, как показано ниже, в строках кода.

         $orrollno= array('$or' => array(array("student.roll_no" => new MongoRegex("/$arg/i"))));

            $orlastname= array('$or' => array(array("student.last_name" => new MongoRegex("/$arg/i"))));

            $oremail= array('$or' => array(array("student.email" => new MongoRegex("/$arg/i"))));

            $orguardian= array('$or' => array(array("student.guardian_name" => new MongoRegex("/$arg/i"))));

            $orphone= array('$or' => array(array("student.phone1" => new MongoRegex("/$arg/i"))));

            $orfullname= array('$or' => array(array("fullname" => new MongoRegex("/$arg/i"))));

            $orfirstmiddle= array('$or' => array(array("firstmiddle" => new MongoRegex("/$arg/i"))));

            $orfirstlast= array('$or' => array(array("firstlast" => new MongoRegex("/$arg/i"))));

            $query = array( '$or' => array($orrollno,$orlastname,$oremail,$orguardian,$orphone,$orfullname,$orfirstmiddle,$orfirstlast));


         $outputTotalResults= $this->db->studentTbl->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')),
                      'student' => '$$ROOT'
                       )
                ),
                  array(
                      '$match' => $query
                       ),
                    )
                 );

Я пытаюсь отсортировать результаты, полученные из $ match => $ query. Например, $ arg содержит "William David", тогда результаты должны сначала содержать записи с именами Willian David, а затем остальные результаты.

Любая помощь будет принята с благодарностью !!!

Основываясь на предложении ur, я попробовал следующее

            $outputTotalResults= $this->db->studentTbl->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')),
                      'student' => '$$ROOT',
                       'weight' => array(
                          '$cond' => array(
                                       array( 
                                         '$or' => array( 
                                          array('$eq' => array('$fullname' => $arg )),
                                          array('$eq' => array('$firstmiddle' => $arg)),
                                          array('$eq' => array('$firstlast' => $arg)),
                                          )
                                        ),
                                       10,
                                        0
                                      )
                                   ),
                        array(
                             '$sort' => array( 'weight'=> -1 )
                              ),
                        array(
                             '$match' => $query
                             ),
                         )
                      )
                   )
               );

person Community    schedule 18.05.2017    source источник
comment
Я думаю, вы здесь делаете совершенно неправильную операцию. Полностью забыв свой код, действительно ли вы намерены поместить результаты для Уиллама в first_name и Дэвида в last_name в начало ваших результатов, с любыми другими результатами после этого? Если так, то есть способ сделать это, но $group не тот оператор, который это сделает.   -  person Neil Lunn    schedule 18.05.2017
comment
какой оператор подходит для выполнения этого требования. Я очень новичок в mongodb ... Пожалуйста, помогите   -  person    schedule 18.05.2017


Ответы (1)


Здесь вы хотите добиться «взвешенной сортировки», когда вы, по сути, хотите вычислить поле на основе условий, а затем применить _ 1_ этап конвейера к этому результату.

Общий случай - применить $cond с логическим условием и либо возвращать значение или нет, возможно, для более чем одного условия каскадным способом.

В идеале с MongoDB 3.4 и выше используйте $addFields:

array(
  array(
    '$addFields' => array(
      'weight' => array(
        '$cond => array(
          array( 
            '$and' => array( 
              array( '$eq' => array( '$first_name', 'Willam' ) )
              array( '$eq' => array( '$last_name', 'David' ) )
            )
          ),
          10,
          0
        )
      )
    )
  ),
  array(
    '$sort' => array( 'weight'=> -1 )
  )
)

Или в предыдущих версиях, где вы не можете просто «добавить» новое поле к существующей структуре документа, вы используете _ 5_, указав каждое нужное поле или изменив структуру, возвращающуюся в одном свойстве через $$ROOT:

array(
  array(
    '$project' => array(
      'first_name' => 1,
      'last_name' => 1,
      'weight' => array(
        '$cond => array(
          array( 
            '$and' => array( 
              array( '$eq' => array( '$first_name', 'Willam' ) )
              array( '$eq' => array( '$last_name', 'David' ) )
            )
          ),
          10,
          0
        )
      )
    )
  ),
  array(
    '$sort' => array( 'weight'=> -1 )
  )
)

Итак, в этом простом случае, когда "оба" условия (через _8 _), свойству weight присваивается значение 10, в противном случае ему присваивается 0. Последующая сортировка по свойству weight выполняется в «убывающем» порядке, поэтому все значения 10, в которых найдено условие, будут «наверху», в то время как все остальные результаты будут получены после всех совпадений.


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

Затем вы $project поле со сравнениями на предмет того, была ли фраза соответствия в предпочтительных полях, и, наконец, $sort в этом вычисляемом поле.

array(
  array( '$match' => $query ),
  array(
    '$addFields' => array(
      'weight' => array(
        '$cond => array(
          array( 
            '$or' => array( 
              array( 
                '$eq' => array( 
                  array('$concat' => array('$first_name',  ' ',  '$middle_name', ' ', '$last_name')),
                  $arg
                )
              ),
              array( 
                '$eq' => array(
                  array('$concat' => array('$first_name',  ' ',  '$middle_name')), 
                  $arg 
                )
              ),
              array(
                '$eq' => array(
                  array('$concat' => array('$first_name',  ' ',  '$last_name')),
                  $arg 
                )
              )
            )
          ),
          10,
          0
        )
      )
    )
  ),
  array(
    '$sort' => array( 'weight'=> -1 )
  )
)

Поэтому всегда $match первым или иным образом используйте этап конвейера, который будет использовать индекс и «оптимизировать» ваш результат. Затем манипулируйте и помните, что вы не можете использовать вычисляемые поля для сравнения на «одной» $project фазе. Если вам это действительно нужно, вы либо дублируете расчеты, либо выполняете расчеты на одном этапе, а затем сравниваете значения на следующем этапе.

Честно говоря, если вы дойдете до этого, вы в основном воспроизведете то, что текстовый поиск , в котором вы можете:

  1. Распределите индекс по всем полям, в которых вы хотите искать. Это устраняет массивное условие $or в простую операцию запроса.

  2. Укажите вес в тех полях, где совпадение будет более важным.

Единственный случай, когда «текстовый поиск» не будет лучшим решением, - это если поля, которым вы хотите «увеличить вес», меняются на регулярной основе. Поскольку текстовые индексы имеют "заданные" значения для взвешивания, и у вас может быть только одно для каждой коллекции, вы не сможете легко изменить комбинацию полей, чтобы присвоить больший вес. С показанным процессом агрегирования довольно легко изменить поля и присвоить вес.

person Neil Lunn    schedule 18.05.2017
comment
наши требования: $ arg может содержать полное имя (имя + отчество + фамилия) или комбинацию имени и отчества или даже имени и фамилии. и я не могу разделить поля имени на два отдельных массива и выполнять с ними операции. Вот почему я объединил их в $ project .. В переменной $ query так много других условий. Мой код работает нормально, но сначала не дает более близких результатов. Также мне не разрешено добавлять файлы в базу данных ... пожалуйста, измените указанное выше решение. - person ; 18.05.2017
comment
@ user2179026 Ваш код не работает, потому что $group не делает то, что вы думаете. Все, что я здесь демонстрирую, - это присвоение веса условию, в котором первое имя соответствует заданному аргументу, а второе соответствует заданному аргументу. Вы можете так же легко сделать условие тестом различных комбинаций конкатенации для данной строки и соответственно применить вес. Опять же, не помогло то, что ваша попытка была так далека от истины. - person Neil Lunn; 18.05.2017
comment
Я обновил свой фактический код, который работает и без группы - person ; 18.05.2017
comment
@ user2179026 Итак, глядя на ваше редактирование, вы хотите сопоставить документы, которые содержат значение в одном из многих возможных полей. Но вы хотите поместить те, в которых комбинация определенных полей соответствует значению. Это означает, что просто замените условие в $cond любым выражением, составляющим строку, в соответствии с аргументом балансировки. - person Neil Lunn; 18.05.2017
comment
@ user2179026 И уже в третий раз. Это потому, что $group не имеет к этому никакого отношения. Если вам нужны веса, то их $project по условию, как показано. - person Neil Lunn; 18.05.2017
comment
Я буду реализовывать то же самое на моем фактическом и сообщу вам, сработает ли это - person ; 18.05.2017
comment
Выдает сообщение об ошибке: неверный оператор '$ fullname' 'Пожалуйста, помогите - person ; 18.05.2017
comment
Давайте продолжим это обсуждение в чате. - person ; 18.05.2017
comment
@ user2179026 Кажется, у вас возникли проблемы с реализацией. Хотя я не остаюсь здесь на весь день, я включил список, соответствующий тому, чего вы пытаетесь достичь, и некоторые другие примечания, которые вы должны учесть. - person Neil Lunn; 18.05.2017