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.