MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

$ expr arrayElementAt não está funcionando na agregação para documento incorporado

Correção rápida


Seu "pipeline" não funciona aqui principalmente porque seu $project inicial não possui o campo que você deseja usar em um estágio posterior. A "solução rápida" é, portanto, basicamente incluir esse campo no documento "projetado", pois é assim que os estágios do pipeline de agregação funcionam:
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
    )
  ),

Ou mesmo desde que você usou $$ROOT para Aluno de qualquer forma, basta qualificar o campo nesse caminho:
'$expr' => array(
  '$eq'=> array(
    array('$arrayElemAt' => array('$Student.allotment_details.room_id', -1)),
    $this->RoomId
  )
),

entretanto Eu fortemente* implorar que você NÃO faça isso.

Todo o conceito de "concatenar strings" para fazer um $match no conteúdo é uma ideia muito ruim, pois significa que toda a coleção é reescrita no pipeline antes que qualquer "filtragem" seja realmente feita.

Da mesma forma, procurar corresponder no elemento "último" da matriz também é um problema. Uma abordagem muito melhor é adicionar "novos itens" ao "início" da matriz, em vez do "fim". Na verdade, é isso que o $position ou possivelmente até o $sort modificadores para $push fazer por você, alterando onde os itens são adicionados ou a ordem de classificação dos itens, respectivamente.

Mudando o Array para "o mais novo primeiro"


Isso dá um pouco de trabalho alterando a maneira como você armazena as coisas, mas os benefícios são a velocidade muito melhorada de tais consultas, como você deseja, sem precisar de um $expr avaliado argumento.

Os conceitos principais são "pré-anexar" novos itens de matriz com sintaxe como:
$this->collection->updateOne(
  $query,
  [ '$push' => [ 'allotment_details' => [ '$each' => $allotments, '$position' => 0 ] ] ]
)

Onde $alloments deve ser uma matriz conforme exigido por $each e $position é usado para 0 para adicionar o novo item da matriz "primeiro".

Alternativamente, se você realmente tiver algo como created_date como uma propriedade dentro de cada um dos objetos na matriz, então você "poderia" usar algo como $sort como um modificador em vez disso.
$this->collection->updateOne(
  $query,
  [ '$push' => [
      'allotment_details' => [ '$each' => $allotments, '$sort' => [ 'created_date' => -1 ] ]
  ]]
)

Realmente depende se sua "consulta" e outros requisitos de acesso dependem de "última adição" ou "última data", e também se você pretende possivelmente alterar tal created_date ou outra propriedade "sort" de uma forma que afetaria a ordem dos elementos da matriz quando "classificados".

A razão pela qual você faz isso é corresponder ao item "latest" (que agora é o "first" ) na matriz simplesmente se torna:
$this->collection->find([
 'allotment_details.0.room_id': $this->RoomId
])

O MongoDB permite que o "primeiro" índice de array seja especificado com "Dot Notation" , usando o 0 índice. O que você não pode fazer é especificar um índice "negativo", ou seja:
$this->collection->find([
 'allotment_details.-1.room_id': $this->RoomId  # not allowed :(
])

Essa é a razão pela qual você faz as coisas mostradas acima em "atualizar" para "reordenar" sua matriz para a forma viável.

A concatenação é ruim


A outra questão principal é a concatenação de strings. Como já mencionado, isso cria uma sobrecarga desnecessária apenas para fazer a correspondência desejada. Também é "desnecessário", pois você pode evitar isso usando $or com as condições em cada um dos campos conforme já existem no documento real:
 $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
 ])

E, claro, o que quer que as condições de consulta "completas" realmente precisem ser, mas você deve ter a ideia básica.

Além disso, se você não estiver realmente procurando por "palavras parciais", então uma "text search" definido sobre os campos com os "nomes". Depois de criar o índice que seria:
 $this->collection->find([
   '$text' => [ '$search' => $arg ],
   'schoolId' => new MongoDB\BSON\ObjectID($this->SchoolId),
   'allotment_details.0.room_id': $this->RoomId
 ])

No geral, eu realmente recomendo olhar atentamente para todas as outras opções, em vez de fazer uma pequena alteração no seu código existente. Com uma pequena reestruturação cuidadosa de como você armazena as coisas e, de fato, "indexa" as coisas, você obtém enormes benefícios de desempenho que seu extenso $concat abordagem de "força bruta" simplesmente não pode entregar.