aggregate function not working php codeigniter - mongodb

I wrote a aggregate query to get data in mongodb. same query I need to run via php codigniter,
This is my mongodb query
db.states.aggregate([
{
$project: {
states: {
$filter: {
input: "$states",
as: "state",
cond: { $eq: [ "$$state.country_id", "101" ] }
}
}
}
}
])
same query i need to convert for php codignater. I have used this library to perform the db operarion.
I tried:
$pipeline = [
[
'$project' => [
'states' =>[
'$filter' => [
'input' => '$states',
'as' => 'state',
'cond' => [
'$eq' => [
'$$state.country_id' => $country_id
]
]
]
]
]
]
];
$result = $this->mongo_db2->aggregate('tb10_states', $pipeline);
But, its throw Message: Unrecognized expression '$$state.country_id'
Suggest me How to fix this.

Related

Query on the basis of empty array and non existing field with other conditions in expr

I have documents like
{
'_id': ...
'registration_temp_perm_no' => 'SIEI/216/2020',
'first_name' => 'Azra',
'last_name' => 'Ali'
},
{
'_id': ...
'registration_temp_perm_no': ....
'first_name': ...,
'last_name' : ...,
'transport_details' : [
{
...
status : "Inactive"
}
]
},
{
'_id': ...
'registration_temp_perm_no':...
'first_name' : ....
'last_name': .....,
'transport_details': []
}
Note that the documents are created/updated via front end functionalities in the following three ways
where embedded document 'transport_details' does not exist at all.
where embedded document 'transport_details' contains empty array
where embedded document 'transport_details' may contain multiple sub arrays
Now I want to fetch those documents where the status of last sub array is "Inactive" or "Requested" and where the sub array "transport_details" doesn't exist and "transport_details" is empty.
I have written query like
$query = array(
'$or' => array(
array("first_name" => new MongoDB\BSON\Regex($arg, 'i')),
array("middle_name" => new MongoDB\BSON\Regex($arg, 'i')),
array("last_name" => new MongoDB\BSON\Regex($arg, 'i')),
array("registration_temp_perm_no" => $arg)
),
"schoolId"=> new MongoDB\BSON\ObjectID($this->schoolId),
'$expr' => [
'$or' => [
['transport_details' => ['$exists' => false]],
['transport_details' => ['$size' => 0]],
['$eq'=> [['$arrayElemAt' => ['$transport_details.status', -1]], "Requested"]],
['$eq'=> [['$arrayElemAt' => ['$transport_details.status', -1]], "Inactive"]],
]
]
);
If we remove the lines ['transport_details' => ['$exists' => false]], ['transport_details' => ['$size' => 0]], the query is able to fetch those documents where the status of last sub array is "Inactive" or "Requested" otherwise it does not work.
When you use $expr, all next operators should have Aggregation-expressions syntax. Check full list here
You need to change
['transport_details' => ['$exists' => false]],
['transport_details' => ['$size' => 0]]
to
['$eq'=> ['$transport_details', undefined]], //equivalent to {$exists:false}
['$eq'=> [['$size' => '$transport_details'], 0]] // $size returns number, we compare numbers
If we apply $IfNull operator, we combine "no exist" + "empty array" into single condition.
Try this one:
db.collection.find({
... your extra conditions here
$expr: {
$or: [
{
$eq: [
{
$size: {
$ifNull: [
"$transport_details",
[]
]
}
},
0
]
},
{
$eq: [
{
$arrayElemAt: [
"$transport_details.status",
-1
]
},
"Requested"
]
},
{
$eq: [
{
$arrayElemAt: [
"$transport_details.status",
-1
]
},
"Inactive"
]
}
]
}
})
MongoPlayground

Filter on projection not working in mongodb

I have done aggregation query on student table with activity table which gives the desired fields except one field i.e "ActivityForMediumSize". This field comes empty.
The code is
$query = ["is_temp_deleted"=>0, "funRun" => "Yes"];
$pipeline = array(
array(
'$match' => $query
),
array(
'$lookup' => array(
'from' => 'studentTbl',
'localField' => '_id',
'foreignField' => 'activity_details.activityId',
'as' => 'studentsOfActivities'
)
),
['$lookup'=> [
'from'=> 'studentTbl',
'localField' => 'academic_year_Id',
'foreignField' => 'studentsOfActivities.academic_year_Id',
'as'=> 'Students'
]],
['$project' => [
'_id' => 1.0,
'activityName' => 1.0,
'activityTime' => 1.0,
'activityDate' => 1.0,
'funRun' => 1.0,
'Students' => 1.0,
'studentsOfActivities' => 1.0,
'studentsOfWeekgroup' => [
'$filter' => [
'input' => '$Students',
'as' => 'item',
'cond' => [
'$eq' => ['$$item.academic_year_Id', new MongoDB\BSON\ObjectID($this->id)],
]
]
],
'ActivityForMediumSize' => [
'$filter' => [
'input' => '$studentsOfActivities',
'as' => 'item',
'cond' => [
'$eq' =>['$$item.activity_details.-1.shirtSize', 'M'],
]
]
],
'StudentCountPerActivity'=>['$size'=>'$studentsOfActivities'],
// 'MediumCount'=>['$size'=>'$countForMedium']
]],
);
$cursor = $this->db->activitiesTbl->aggregate($pipeline)->toArray();
I have checked in console that data in "studentsOfActivities" appears like
0 =>
"_id": ObjectId("5bf518378d03ec0b2400536f"),
"ssn_Last_four": "0001",
"first_name": "Thomas",
"activity_details": [
{
"studentId": ObjectId("5bf518378d03ec0b2400536f"),
"funrun": "Yes",
"weekgroupId": ObjectId("5bf50bd48d03ec0b2400536d"),
"activityId": ObjectId("5c10a0e08d03ec1834001c6d"),
"attending": "Yes",
"id": ObjectId("5c10e5ae3250041188005dde")
},
{
"studentId": ObjectId("5bf518378d03ec0b2400536f"),
"funrun": "Yes",
"weekgroupId": ObjectId("5bf50bd48d03ec0b2400536d"),
"activityId": ObjectId("5c10a0e08d03ec1834001c6d"),
"attending": "Yes",
"shirtSize": "L",
"id": ObjectId("5c13338032500409f4007cf3")
}
]
1=>
"_id": ObjectId("5bf518378d03ec0b2400536e"),
....
....
The embedded document "activity_details" contains student's all the activities and the last element of it is considered active one i.e why I have used -1 in the above code.
Now in projection "ActivityForMediumSize" I am trying find those students who have selected shirtSize: "M". The filter on projection "ActivityForMediumSize" is not working. Please help !!!
Note that there are some students where the last activity inside "activity_details" which does not contain "shirtSize" field.
I guess "is not working" means you have an empty array. It's because you cannot refer to the last element of an array just by "-1". Instead you need to use array operators to get the last element. For example it can be a combination of arrayElemAt and slice so the filter condition can look like :
$eq: [
{ $let: {
vars: {
last: { $arrayElemAt: [
{ $slice: [ '$$item. activity_details', -1 ] },
0
] }
},
in: '$$last.shirtSize'
} },
'M'
]
It's in js syntax but it should be trivial to translate to php.

Error message lookup pipeline must be a string, is type array

I am using mongodb 3.6 version.
I have vehicle table with documents like
{
"_id": ObjectId("5b976220d2ccda12fc0050fb"),
"VehicleNumber": "JK 678",
"NumberOfSeats": "47",
}
{
"_id": ObjectId("67976220d2ccda12fc005068"),
"VehicleNumber": "JK 779",
"NumberOfSeats": "47",
}
I have route table with data like
{
"_id": ObjectId("5b7fb426d2ccda11fc005185"),
"Name": "New Jersey City",
"VehicleDetails": [
{
"VehicleEntryId": "b8d0d2b5-8f32-6850-4d79-34ed79138d6d",
"VehicleId": ObjectId("5b976220d2ccda12fc0050fb"),
...
"Status": "Active"
},
{
"VehicleEntryId": "b8d0d2b5-8f32-6850-4d79-34ed79138568",
"VehicleId": ObjectId("67976220d2ccda12fc005068"),
...
"Status": "Active"
}
],
...
}
I have written mongodb aggregation query like
$cursor = $this->collection->aggregate([
['$match' => ["_id" => new MongoDB\BSON\ObjectID('5b7fb426d2ccda11fc005185')]],
[
'$addFields' => [
'filteredIds' => [
'$map' => [
'input' => '$VehicleDetails',
'as' => 'item',
'in' => [
'$cond' => [
['$eq' => ['$$item.Status', 'Active']],
'$$item.VehicleId',
false
]
]
]
]
]
],
array(
'$lookup' => array(
'from' => 'VehicleTbl',
'$pipeline' => [
[ '$match'=> ["_id" => ['$in' => ['$$filteredIds'] ]]],
],
'as' => 'AllotedVehicleDetails'
)
),
])->toArray();
Basically I am trying to fetch all assigned vehicle to a particular route. So I first fetch all the actively alloted vehicle ids by using $addFeilds operator and putting them in "filteredIds" then in lookup I am trying to fetch vehicle informations from vehicle table using pipeline. The above code throws error message 'Uncaught exception 'MongoDB\Driver\Exception\RuntimeException' with message '$lookup argument '$pipeline: [ { $match: { _id: { $in: [ "$$filteredIds" ] } } } ]' must be a string, is type array' in' line no 32.
Please help!!!
use pipeline instead of $pipeline.
Example:
{
$lookup : {
'from' : 'vehicles',
let : { localFilterIds : '$filteredIds'},
pipeline : [
{'$match' : { $expr: { '$in' : [ '$_id', '$$localFilterIds' ] }} }
],
'as' : 'AllotedVehicleDetails'
}
}
Note: This is tested in mongoDb GUI Robo 3T.

fetching embedded documents on search criteria of another embedded document

I have a collection HostelTbl which has many documents one of the document is like
{
"_id": ObjectId("5ae69fb4d2ccda0e70005551"),
"Name": "Hostel-MGL-02",
...
"RoomsDetails": [
{
"RoomId": "9a21e427-16fd-7a9e-bc16-537dc55093bb",
"FloorNumber": "1",
"RoomNumber": "101",
...
},
{
"RoomId": "050c65ab-355e-9bec-00a0-2999e75bcdc4",
"FloorNumber": "1",
"RoomNumber": "104",
...
},
{
"RoomId": "f197c635-a6fb-b4d6-9cf5-ed53177edd47",
"FloorNumber": "2",
"RoomNumber": "109",
...
},
...
{
...
}
],
"AllotmentsDetails": [
{
"AllotmentId": "5eb05e1d-2690-afad-747a-ef29450ae3f3",
"StudentId": ObjectId("5ab8d5d4ff24ae120400085e"),
"RoomId": "9a21e427-16fd-7a9e-bc16-537dc55093bb",
"FromDate": ISODate("2018-06-12T22:00:00.0Z"),
"ToDate": ISODate("2018-07-04T22:00:00.0Z"),
...
},
{
"AllotmentId": "40d73d84-0f94-48d8-823c-2255fd2ae0ed",
"StudentId": ObjectId("5ab8d5bcff24ae120400085d"),
"RoomId": "050c65ab-355e-9bec-00a0-2999e75bcdc4",
"FromDate": ISODate("2018-01-21T22:00:00.0Z"),
"ToDate": ISODate("2018-02-06T22:00:00.0Z"),
...
},
{
"AllotmentId": "40d73564-0f94-48d8-823c-2255fd2440428",
"StudentId": ObjectId("5ab8d5bc4624ae1204000855),
"RoomId": "f197c635-a6fb-b4d6-9cf5-ed53177edd47",
"FromDate": ISODate("2018-03-21T22:00:00.0Z"),
"ToDate": ISODate("2018-05-04T22:00:00.0Z"),
...
}
]
}
I have two embedded documents inside one documents. One is storing rooms information and another storing rooms allotments to a student information.
Now I want to fetch those available rooms from RoomsDetails if their allotments detail's "ToDate" field contains the dates in past from today inside AllotmentsDetails Embedded document. Like in the above case the rooms with ids
"050c65ab-355e-9bec-00a0-2999e75bcdc4", f197c635-a6fb-b4d6-9cf5-ed53177edd47 contains ToDate dates in the past inside AllotmentsDetails embedded documents. It should fetch those two rooms only.
I am not able to find such queries. Please help!!!
I am trying to tweak the below lines of code
public function fetchAllAvailableRooms()
{
$cursor = $this->collection->aggregate(array(
array(
'$match' => array(
"_id" => new MongoDB\BSON\ObjectID($this->id)
)
),
array(
'$project' => array(
'RoomsDetails' => array(
'$filter' => array(
'input' => '$AllotmentsDetails',
'as' => 'allot',
'cond' => array(
'$lt' => array('$$allot.FromDate', $this->Today)
)
)
),
'Name' => 1
)
)
))->toArray();
return $cursor;
}
You can use the $map and $filter aggregation operators for that.
$map - for AllotmentsDetails transformation into final list
of the room ids
$filter - for RoomsDetails filtering by the
final list of the room ids
Example:
public function fetchAllAvailableRooms()
{
$cursor = $this->collection->aggregate([
['$match' => ["_id" => new MongoDB\BSON\ObjectID($this->id)]],
[
'$addFields' => [
'filteredIds' => [
'$map' => [
'input' => '$AllotmentsDetails',
'as' => 'item',
'in' => [
'$cond' => [
['$lt' => ['$$item.ToDate', $this->Today]],
'$$item.RoomId',
false
]
]
]
]
]
],
[
'$project' => [
'RoomsDetails' => [
'$filter' => [
'input' => '$RoomsDetails',
'as' => 'item',
'cond' => ['$in' => ['$$item.RoomId', '$filteredIds']]
]
]
]
]
])->toArray();
return $cursor;
}
/* Result example
{
"_id" : ObjectId("5ae69fb4d2ccda0e70005551"),
"RoomsDetails" : [
{
"RoomId" : "050c65ab-355e-9bec-00a0-2999e75bcdc4",
"FloorNumber" : "1",
"RoomNumber" : "104"
},
{
"RoomId" : "f197c635-a6fb-b4d6-9cf5-ed53177edd47",
"FloorNumber" : "2",
"RoomNumber" : "109"
}
]
}
*/
P.S. PHP code is not tested, so i'm not 100% sure if it is working.
P.P.S. The final solution is strongly depends on the details of your project.

how to convert aggregate to mapReduce using Laravel MongoDB by jenssegers

Right now, I have the below code in my laravel app. I'm running into this error: mongo1:27017: exception: aggregation result exceeds maximum document size (16MB). I read that I should use mapReduce instead of aggregate to avoid this issue but am unsure how to translate the below to mapReduce using Laravel MongoDB by jenssegers.
Here is my code using aggregate:
$email_duplicates = User::raw(function ($collection) {
return $collection->aggregate(
[
[
'$group' => [
'_id' => [
'email' => '$email',
],
'uniqueIds' => [
'$addToSet' => '$_id',
],
'count' => [
'$sum' => 1,
],
],
],
[
'$match' => [
'count' => [
'$gt' => 1,
],
],
],
],
[
'allowDiskUse' => true,
]
);
});
Can anyone help?