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

$expr arrayElementAt fungerer ikke i aggregering for indlejret dokument

Hurtig løsning

Din "pipeline" virker primært ikke her fordi dit første $project mangler det felt, du ønsker at bruge på et senere tidspunkt. "hurtig rettelse" er derfor dybest set at inkludere det felt i det "projicerede" dokument, da det er sådan aggregeringspipeline stadier fungerer:

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

Eller endda siden du brugte $$ROOT for Student alligevel skal du blot kvalificere feltet under den sti:

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

dog Jeg ville stærkt* beder om, at du gør IKKE gør det.

Hele konceptet med "sammenkædning af strenge" for at lave en senere $match på indholdet er en rigtig dårlig idé, da det betyder, at hele samlingen bliver omskrevet i pipelinen, før nogen "filtrering" rent faktisk bliver udført.

Ligeledes er det også et problem at se efter at matche på det "sidste" array-element. En langt bedre tilgang er i stedet faktisk at tilføje "nye elementer" til "begyndelsen" af arrayet i stedet for "slutningen". Dette er faktisk hvad $position eller muligvis endda $sort modifikatorer til $push gøre for dig ved at ændre, hvor elementerne bliver tilføjet, eller den sorterede rækkefølge af varer.

Ændring af Array til "nyeste først"

Dette kræver lidt arbejde ved at ændre den måde, du gemmer ting på, men fordelene er en væsentligt forbedret hastighed af sådanne forespørgsler, som du ønsker, uden at du behøver en evalueret $expr argument.

Kernekoncepterne er at "pre-pendere" nye array-elementer med syntaks som:

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

Hvor $alloments skal være et array som krævet af $each og $position bruges til 0 for at tilføje det nye array-element "først".

Alternativt hvis du faktisk har noget som created_date som en egenskab inden for hvert af objekterne i arrayet, så "kunne" du bruge noget som $sort som en modifikator i stedet.

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

Det afhænger virkelig af, om din "forespørgsel" og andre adgangskrav er afhængige af "sidst tilføjet" eller "seneste dato", og så også typisk om du har til hensigt eventuelt at ændre en sådan created_date eller anden "sort"-egenskab på en måde, som ville påvirke rækkefølgen af ​​array-elementerne, når de "sorteres".

Grunden til at du gør dette, er at matche det "seneste" (som nu er det "første") element i arrayet bliver simpelthen:

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

MongoDB tillader det "første" array-indeks at blive specificeret med "Dot Notation" , ved hjælp af 0 indeks. Hvad du ikke kan do er at angive et "negativt" indeks, dvs.:

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

Det er grunden til, at du gør de ting, der er vist ovenfor under "opdatering" for at "omorganisere" dit array til den brugbare form.

Sammenkædning er dårlig

Det andet hovedproblem er sammenkædningen af ​​strenge. Som allerede nævnt skaber dette unødvendige overhead bare for at udføre den matchning, du ønsker. Det er også "unødvendigt", da du kan undgå dette ved at bruge $or med betingelserne på hvert af felterne, som de allerede eksisterer i det aktuelle dokument:

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

Og selvfølgelig hvad end de "fulde" forespørgselsbetingelser faktisk skal være, men du burde få den grundlæggende idé.

Også hvis du ikke rent faktisk leder efter "delord", så en "tekstsøgning" defineret over felterne med "navnene". Efter oprettelse af indekset ville det være:

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

Generelt vil jeg virkelig anbefale at se nærmere på alle de andre muligheder i stedet for at foretage en lille ændring af din eksisterende kode. Med lidt omhyggelig omstrukturering af, hvordan du opbevarer ting og faktisk "indekserer" ting, får du enorme ydeevnefordele, som din omfattende $concat "brute force"-tilgangen kan simpelthen ikke levere.




  1. MongoDb unikke begrænsninger på et datointerval

  2. Hvordan kan jeg bede MongoDB om at evaluere noget JavaScript for at opnå værdi for et felt?

  3. løse et løfte ved hjælp af mongodb og nodejs

  4. Udskrivning af Mongo-forespørgselsoutput til en fil, mens du er i mongo-skallen