sql >> Base de Datos >  >> NoSQL >> MongoDB

$expr arrayElementAt no funciona en agregación para documento incrustado

Solución rápida

Su "tubería" no funciona aquí principalmente porque su inicial $project carece del campo que desea utilizar en una etapa posterior. La "solución rápida" por lo tanto, es básicamente incluir ese campo en el documento "proyectado", ya que así es como funcionan las etapas de canalización de agregación:

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
    )
  ),

O incluso desde que usaste $$ROOT para Student de todos modos, simplemente califique el campo bajo esa ruta:

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

sin embargo Lo haría fuertemente* imploro que NO haz eso.

Todo el concepto de "concatenar cadenas" para hacer un $match en el contenido es una muy mala idea, ya que significa que toda la colección se reescribe en la canalización antes de que realmente se realice cualquier "filtrado".

Del mismo modo, buscar la coincidencia en el "último" elemento de la matriz también es un problema. Un enfoque mucho mejor es, en cambio, agregar "elementos nuevos" al "comienzo" de la matriz, en lugar del "final". Esto es realmente lo que $position o posiblemente incluso el $sort modificadores de $push hacer por usted, cambiando el lugar donde se agregan los elementos o el orden de clasificación de los elementos, respectivamente.

Cambiar la matriz a "más reciente primero"

Esto requiere un poco de trabajo al cambiar la forma en que almacena las cosas, pero los beneficios son una gran velocidad mejorada de consultas como las que desea sin necesidad de un $expr evaluado. argumento.

Los conceptos básicos son "preestablecer" nuevos elementos de matriz con una sintaxis como:

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

Donde $alloments debe ser una matriz según lo requiera $each y $position se usa para 0 para agregar el nuevo elemento de matriz "primero".

Alternativamente, si realmente tiene algo como created_date como una propiedad dentro de cada uno de los objetos en la matriz, entonces "podría" usar algo como $sort como un modificador en su lugar.

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

Realmente depende de si su "consulta" y otros requisitos de acceso se basan en "último agregado" o "fecha más reciente", y también, por lo general, si tiene la intención de modificar tal created_date u otra propiedad de "clasificación" de una manera que afecte el orden de los elementos de la matriz cuando se "clasifiquen".

La razón por la que hace esto es que la coincidencia del elemento "más reciente" (que ahora es el "primero") en la matriz simplemente se convierte en:

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

MongoDB permite especificar el "primer" índice de matriz con "Dot Notation" , usando el 0 índice. Lo que no puedes hacer es especificar un índice "negativo", es decir:

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

Esa es la razón por la que hace las cosas que se muestran arriba en "actualizar" para "reordenar" su matriz a la forma viable.

La concatenación es mala

El otro problema principal es la concatenación de cadenas. Como ya se mencionó, esto crea una sobrecarga innecesaria solo para hacer la coincidencia que desea. También es "innecesario" ya que puede evitar esto usando $or con las condiciones en cada uno de los campos tal como ya existen en el 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
 ])

Y, por supuesto, sean cuales sean las condiciones de consulta "completas" que realmente deben ser, pero debería tener una idea básica.

Además, si en realidad no está buscando "palabras parciales", entonces "text-search" definido sobre los campos con los "nombres". Después de crear el índice que sería:

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

En general, realmente recomendaría mirar de cerca todas las otras opciones en lugar de hacer un pequeño cambio en su código existente. Con un poco de reestructuración cuidadosa de cómo almacena las cosas y, de hecho, "indexa" las cosas, obtiene enormes beneficios de rendimiento que su extenso $concat El enfoque de "fuerza bruta" simplemente no puede cumplir.