MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

$expr arrayElementAt non funziona nell'aggregazione per il documento incorporato

Risoluzione rapida

La tua "pipeline" non funziona qui principalmente perché il tuo $project manca il campo che si desidera utilizzare in una fase successiva. La "soluzione rapida" è quindi sostanzialmente includere quel campo nel documento "proiettato", poiché è così che funzionano le fasi della pipeline di aggregazione:

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 anche da quando hai usato $$ROOT per Student in ogni caso, qualifica semplicemente il campo in quel percorso:

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

tuttavia Lo farei fortemente* implora di NON fallo.

L'intero concetto di "stringhe concatenate" per fare un $match sul contenuto è davvero una pessima idea poiché significa che l'intera raccolta viene riscritta in fase di elaborazione prima che qualsiasi "filtraggio" venga effettivamente eseguito.

Allo stesso modo, anche la ricerca della corrispondenza sull'elemento dell'array "ultimo" è un problema. Un approccio molto migliore consiste invece nell'aggiungere effettivamente "nuovi elementi" all'"inizio" dell'array, invece che alla "fine". Questo è in realtà ciò che il $position o forse anche il $sort modificatori a $push fai per te, modificando rispettivamente dove vengono aggiunti gli elementi o l'ordine degli elementi.

Modifica dell'array in "prima il più recente"

Questo richiede un po' di lavoro cambiando il modo in cui memorizzi le cose, ma i vantaggi sono una velocità notevolmente migliorata di tali query come desideri senza la necessità di un $expr valutato argomento.

I concetti di base sono "anteporre" i nuovi elementi dell'array con una sintassi come:

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

Dove $alloments deve essere un array come richiesto da $each e $position viene utilizzato per 0 per aggiungere il nuovo elemento dell'array "first".

In alternativa, se hai effettivamente qualcosa come created_date come proprietà all'interno di ciascuno degli oggetti nell'array, allora "potresti" usare qualcosa come $sort come modificatore invece.

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

Dipende davvero dal fatto che la tua "query" e altri requisiti di accesso si basino su "ultima aggiunta" o "ultima data", e quindi in genere anche se intendi eventualmente modificare tale created_date o un'altra proprietà "sort" in modo da influenzare l'ordine degli elementi dell'array quando "ordinati".

Il motivo per cui lo fai è quindi abbinare l'elemento "latest" (che ora è il "first" ) nell'array diventa semplicemente:

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

MongoDB consente di specificare il "primo" indice dell'array con "Dot Notation" , utilizzando 0 indice. Cosa non puoi do è specificare un indice "negativo", ad esempio:

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

Questo è il motivo per cui fai le cose mostrate sopra su "aggiornamento" per "riordinare" il tuo array nella forma praticabile.

La concatenazione è negativa

L'altro problema principale è la concatenazione delle stringhe. Come già accennato, questo crea un sovraccarico non necessario solo per eseguire l'abbinamento desiderato. È anche "non necessario" poiché puoi completare evitarlo usando $or con le condizioni su ciascuno dei campi in quanto esistono già all'interno del documento effettivo:

 $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, naturalmente, qualunque siano effettivamente le condizioni della query "completa", ma dovresti farti un'idea di base.

Inoltre, se non stai effettivamente cercando "parole parziali", allora una "text search" definito sui campi con i "nomi". Dopo aver creato l'indice che sarebbe:

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

Nel complesso, consiglierei davvero di guardare da vicino tutte le altre opzioni piuttosto che apportare una piccola modifica al codice esistente. Con una piccola attenta ristrutturazione del modo in cui memorizzi le cose e in effetti le "indicizzi", ottieni enormi vantaggi in termini di prestazioni che il tuo ampio $concat L'approccio della "forza bruta" semplicemente non può fornire.