sql >> Database >  >> NoSQL >> MongoDB

$expr arrayElementAt werkt niet in aggregatie voor ingesloten document

Snelle oplossing

Uw "pijplijn" werkt hier niet voornamelijk omdat uw initiële $project mist het veld dat u later wilt gebruiken. De "snelle oplossing" is daarom eigenlijk om dat veld op te nemen in het "geprojecteerde" document, want zo werken aggregatiepijplijnfasen:

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

Of zelfs sinds je $$ROOT . hebt gebruikt voor Student hoe dan ook, kwalificeer gewoon het veld onder dat pad:

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

echter Ik zou sterk* smeek u dat u NIET . doet doe dat.

Het hele concept van "aaneenschakelen van strings" om een ​​latere $match op de inhoud is een heel slecht idee, omdat het betekent dat de hele collectie in de pijplijn wordt herschreven voordat er daadwerkelijk wordt 'gefilterd'.

Evenzo is het een probleem om te matchen op het "laatste" array-element. Een veel betere benadering is om in plaats daarvan "nieuwe items" toe te voegen aan het "begin" van de array, in plaats van aan het "einde". Dit is eigenlijk wat de $position of mogelijk zelfs de $sort modificaties voor $push voor u doen, door respectievelijk te wijzigen waar items worden toegevoegd of de gesorteerde volgorde van items.

De array wijzigen in "nieuwste eerst"

Dit kost wat moeite door de manier waarop je dingen opslaat te veranderen, maar de voordelen zijn een sterk verbeterde snelheid van dergelijke zoekopdrachten zoals je wilt zonder dat een geëvalueerde $expr nodig is. argument.

De kernconcepten zijn om nieuwe array-items "pre-pend" te maken met syntaxis zoals:

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

Waar $alloments moeten een array zijn zoals vereist door $each en $position wordt gebruikt om 0 om het nieuwe array-item "eerste" toe te voegen.

Of als u daadwerkelijk iets heeft als created_date als een eigenschap binnen elk van de objecten in de array, dan "zou" u iets als $sort in plaats daarvan als modifier.

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

Het hangt er echt van af of uw "query" en andere toegangsvereisten afhankelijk zijn van "laatst toegevoegd" of "laatste datum", en dan meestal ook of u van plan bent zo'n created_date mogelijk te wijzigen of een andere "sorteer"-eigenschap op een manier die de volgorde van de array-elementen zou beïnvloeden wanneer ze "gesorteerd" zijn.

De reden dat u dit doet, is dat het matchen van het "laatste" (wat nu het "eerste") item in de array is, eenvoudig wordt:

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

MongoDB staat toe dat de "eerste" array-index wordt gespecificeerd met "Dot Notation" , met behulp van de 0 inhoudsopgave. Wat je niet kunt do is een "negatieve" index specificeren, d.w.z.:

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

Dat is de reden waarom je de dingen doet die hierboven bij "update" worden getoond om je array te "herordenen" naar de werkbare vorm.

Aaneenschakeling is slecht

Het andere belangrijke probleem is de aaneenschakeling van strings. Zoals eerder vermeld, creëert dit onnodige overhead om de gewenste matching te doen. Het is ook "onnodig", omdat je dit volledig kunt vermijden met $of met de voorwaarden op elk van de velden zoals ze al bestaan ​​in het eigenlijke document:

 $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
 ])

En natuurlijk wat de "volledige" zoekvoorwaarden ook moeten zijn, maar u zou het basisidee moeten krijgen.

Ook als u niet echt op zoek bent naar "gedeeltelijke woorden", dan is een "text search" gedefinieerd over de velden met de "namen". Na het maken van de index zou dat zijn:

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

Over het algemeen zou ik echt aanraden om goed naar alle andere opties te kijken in plaats van een kleine wijziging aan te brengen in je bestaande code. Met een beetje zorgvuldige herstructurering van hoe je dingen opslaat en inderdaad dingen "indexeert", krijg je enorme prestatievoordelen die je uitgebreide $concat "brute force"-aanpak kan gewoon niet leveren.




  1. Nodejs Mongoose render twee modellen uit collecties

  2. MongoDB - Aggregatie - Om unieke items in een array te krijgen

  3. MongoDB $stdDevSamp

  4. Maak opnieuw verbinding met ECONNREFUSED in NodeJS in Kubernetes-cluster