sql >> Databasteknik >  >> NoSQL >> MongoDB

$expr arrayElementAt fungerar inte i aggregering för inbäddade dokument

Snabb lösning

Din "pipeline" fungerar inte här i första hand eftersom ditt initiala $project saknar det fält du vill använda i ett senare skede. "snabbkorrigeringen" är därför i princip att inkludera det fältet i det "projicerade" dokumentet, eftersom det är så aggregeringspipelinestadier fungerar:

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 till och med sedan du använde $$ROOT för Student Hur som helst, kvalificera helt enkelt fältet under den sökvägen:

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

dock Jag skulle starkt* bönfaller att du gör INTE gör det.

Hela konceptet med "sammanfogande strängar" för att göra en senare $match på innehållet är en riktigt dålig idé eftersom det innebär att hela samlingen skrivs om i pipelinen innan någon "filtrering" faktiskt blir gjord.

Likaså att leta efter att matcha på det "sista" arrayelementet är också ett problem. Ett mycket bättre tillvägagångssätt är att istället faktiskt lägga till "nya objekt" till "början" av arrayen, istället för "slutet". Detta är faktiskt vad $position eller möjligen till och med $sort modifierare till $push göra åt dig, genom att ändra var objekt läggs till eller sorterad ordning på objekt respektive.

Ändra Array till "nyaste först"

Detta kräver lite arbete genom att ändra sättet du lagrar saker på, men fördelarna är avsevärt förbättrad hastighet för sådana frågor som du vill utan att behöva en utvärderad $expr argument.

Kärnkoncepten är att "prependera" nya arrayobjekt med syntax som:

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

Där $alloments måste vara en array som krävs av $each och $position används för att 0 för att lägga till det nya arrayobjektet "först".

Alternativt om du faktiskt har något som created_date som en egenskap inom vart och ett av objekten i arrayen, då "kan" du använda något som $sort som en modifierare istället.

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

Det beror verkligen på om din "fråga" och andra åtkomstkrav förlitar sig på "senast tillagd" eller "senaste datum", och då också vanligtvis om du avser att eventuellt ändra ett sådant created_date eller annan "sort"-egenskap på ett sätt som skulle påverka ordningen på arrayelementen när de "sorterades".

Anledningen till att du gör detta är att sedan matcha det "senaste" (som nu är det "första") objektet i arrayen blir helt enkelt:

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

MongoDB tillåter att det "första" arrayindexet specificeras med "Dot Notation" , med 0 index. Vad du inte kan do är att ange ett "negativt" index, dvs:

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

Det är anledningen till att du gör de saker som visas ovan under "uppdatering" för att "omordna" din array till den fungerande formen.

Konkatenering är dålig

Den andra huvudfrågan är sammanlänkningen av strängar. Som redan nämnts skapar detta onödiga overhead bara för att göra den matchning du vill ha. Det är också "onödigt" eftersom du kan undvika detta med $or med villkoren för vart och ett av fälten eftersom de redan finns i själva dokumentet:

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

Och naturligtvis oavsett vad de "fullständiga" frågevillkoren faktiskt behöver vara, men du borde få grundidén.

Också om du faktiskt inte letar efter "delord", sedan en "textsökning" definieras över fälten med "namnen". Efter att ha skapat indexet som skulle vara:

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

Sammantaget skulle jag verkligen rekommendera att titta noga på alla andra alternativ istället för att göra en liten ändring i din befintliga kod. Med lite noggrann omstrukturering av hur du lagrar saker och faktiskt "indexerar" saker, får du enorma prestandafördelar som din omfattande $concat "brute force"-metoden kan helt enkelt inte leverera.




  1. Hur man återanvänder en mongo-anslutning med löften

  2. ValueError:Extra Data-fel vid import av json-fil med python

  3. Mongodb - $grupp i en $grupp (med 'nyckel')

  4. Hur får man anslutningsstatus i C# MongoDB-drivrutinen v2.0?