MongoDB groupby distinct sort together - php

i have mongodb 1 collections structure like this-
{
"_id" : ObjectId("54d34cb314aa06781400081b"),
"entity_id" : NumberInt(440),
"year" : NumberInt(2011),
}
{
"_id" : ObjectId("54d34cb314aa06781400081e"),
"entity_id" : NumberInt(488),
"year" : NumberInt(2007),
}
{
"_id" : ObjectId("54d34cb314aa06781400081f"),
"entity_id" : NumberInt(488),
"year" : NumberInt(2008),
}
{
"_id" : ObjectId("54d34cb314aa067814000820"),
"entity_id" : NumberInt(488),
"year" : NumberInt(2009),
}
{
"_id" : ObjectId("54d34cb314aa067814000827"),
"entity_id" : NumberInt(489),
"year" : NumberInt(2009),
}
so in output i want that i should get "entity_id" with max "year" only .(suppose with "488" entity_id "year" should be 2009).
i have tried writing query
$fin_cursor = $db->command(array(
"distinct" =>"Profit_and_Loss",
"key" =>'entity_id',
"query" => array(array('$and'=>$financial_pl_search_array),array('$sort'=>array("year"=>-1))),
));
in output i want 2 fields "entity_id" and "year".
can anyone suggest me best way of doing it.
Thanks in advance.

You're better of using .aggregate() to do this. It's also a direct method on the collection objects in modern versions of the driver:
$result = $db->collection('Profit_and_loss')->aggregate(array(
array( '$group' => array(
'_id' => '$entity_id',
'year' => array( '$max' => '$year' )
))
));
The .distinct() command only runs over a single field. Other forms require JavaScript evaluation as you have noted and run considerably slower than native code.

Related

PHP MongoDB Driver and Aggregations

I am at my first steps with mongoDB and php, trying to figure out how aggregations works. I have an approximate idea on how to use them from the command line but I am trying to translate this for the php driver. I am using the restaurants dexample DB, a list of records like this
{
"_id" : ObjectId("59a5211e107765480896f3f8"),
"address" : {
"building" : "284",
"coord" : [
-73.9829239,
40.6580753
],
"street" : "Prospect Park West",
"zipcode" : "11215"
},
"borough" : "Brooklyn",
"cuisine" : "American",
"grades" : [
{
"date" : ISODate("2014-11-19T00:00:00Z"),
"grade" : "A",
"score" : 11
},
{
"date" : ISODate("2013-11-14T00:00:00Z"),
"grade" : "A",
"score" : 2
},
{
"date" : ISODate("2012-12-05T00:00:00Z"),
"grade" : "A",
"score" : 13
},
{
"date" : ISODate("2012-05-17T00:00:00Z"),
"grade" : "A",
"score" : 11
}
],
"name" : "The Movable Feast",
"restaurant_id" : "40361606"
}
I just want to count how many restaurants for location, what I am doing is
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->myNewDb->restaurants;
$results = $collection->aggregate(
[
'name' => '$name'
],
[
'$group' => [
'cuisine' => ['sum' => '$sum']
]
]
);
and I am getting this error
Fatal error: Uncaught exception 'MongoDB\Exception\InvalidArgumentException'
with message '$pipeline is not a list (unexpected index: "name")'
any idea? I can't find any good documentation on php.net.
thanks
M
Just take a look into documentation, and you will see, that the pipelines must be passed as an array.
The aggregate method accepts two parameters $pipelines and $options (public function aggregate(array $pipeline, array $options = [])).
Also as was mentioned before, the $group must have the _id element.
Groups documents by some specified expression and outputs to the next
stage a document for each distinct grouping. The output documents
contain an _id field which contains the distinct group by key. The
output documents can also contain computed fields that hold the values
of some accumulator expression grouped by the $groupā€˜s _id field.
$group does not order its output documents.
https://docs.mongodb.com/manual/reference/operator/aggregation/group/
So your code must look like this:
$results = $collection->aggregate([
[
'$group' => [
'_id' => '$cuisine',
'sum' => ['$sum' => 1],
'names' => ['$push' => '$name']
]
]
]);
This code groups documents by cuisine element, counts the items and collects all name values into array.

PHP MongoDB Driver Query Only Specific Field From All Documents In Collection

Ok, guys. As most of you might know, there is this new MongoDB Driver for PHP 7 out there. I was trying it out. I got stuck at this query here :
There is a collection like this:
{
"_id" : ObjectId("592d026e2a5e742704e60055"),
"name" : "One",
"conn" : [
{
"_id" : ObjectId("592d03cd3c83d5265c004d0b"),
"name" : "MongoDB Fans",
"comments" : "nice!"
},
{
"_id" : ObjectId("592d06513c83d5265c004d17"),
"name" : "SQL Fans",
"comments" : "not so nice!"
}
]
},
{
"_id" : ObjectId("592d0eec2a5e742704e60056"),
"name" : "Two",
"conn" : [
{
"_id" : ObjectId("592d0ef13c83d5265c004d37"),
"name" : "Cassandra Fans",
"comments" : "spooky!"
}
]
}
Now, what I want to achieve is this:
1. Find all "conn" fields (irrespective of which document they belong to) that contain a user input (say "xyz") in "name" as a sub-string.
2. Project the selected "conn" fields out.
I have tried using the following filters and options without success (Almost all of these return all the "conn" subsets instead of only the filtered subsets):
$filters = ["conn.name" => new MongoDB\BSON\Regex($name,'i')];
$options = ["projection" => ["_id" => 0,"conn" =>1];
$filters = ["conn" => ["\$all" =>["name" => new MongoDB\BSON\Regex($name,'i')]]];
$options = ["projection" => ["_id" => 0,"conn" =>1];
$filters = ["conn.name" => '/'.$name.'/i'];
$options = ["projection" => ["_id" => 0,"conn" =>1];
NOTE: I am using PHP 7 and the latest mongod PECL extension

how to aggregate mongodb collection data in laravel

i have collection like this
{
"wl_total" : 380,
"player_id" : 1241,
"username" : "Robin",
"hand_id" : 292656,
"time" : 1429871584
}
{
"wl_total" : -400,
"player_id" : 1243,
"username" : "a",
"hand_id" : 292656,
"time" : 1429871584
}
as both collection have same hand_id i want to aggregate both these collection on the basis of hand_id
i want result as combine of
data=array(
'hand_id'=>292656,
'wl_total'=>
{
0=>380,
1=>-400
},
'username'=>
{
0=>"Robin",
1=>"a"
},
"time"=>1429871584
)
You basically want a $group by the "hand_id" common to all players, and then $push to different arrays in the document and then also do something with "time", I took $max. Nees to be an accumulator of some sort at any rate.
Also not sure what your underlying collection name is, but you can call this in laravel with a construct like this:
$result = DB::collection('collection_name')->raw(function($collection)
{
return $collection->aggregate(array(
array(
'$group' => array(
'_id' => '$hand_id',
'wl_total' => array(
'$push' => '$wl_total'
),
'username' => array(
'$push' => '$username'
),
'time' => array(
'$max' => '$time'
)
)
)
));
});
Which returns output ( shown in json ) like this:
{
"_id" : 292656,
"wl_total" : [
380,
-400
],
"username" : [
"Robin",
"a"
],
"time" : 1429871584
}
Personally I would have gone for a single array with all the infomation in it for the grouped "hand", but I supose you have your reasons why you want it this way.

PHP mongodb update on nested array with multiple conditions

I've thoroughly searched on the internet for this but haven't found a solution.
Problem : I want to update the quantity in this document. Criteria - itemId=126260, accessDays=30
`{
"_id" : ObjectId("547acfa95ca86bec2e000029"),
"session_id" : "1111",
"email" : "aasdasda#sdfsd.com",
"isProcessed" : 0,
"couponApplied" : "",
"countryId" : 2,
"items" : [
{
"itemId" : 126260,
"batchId" : 102970,
"accessDays" : null,
"quantity" : 2
},
{
"itemId" : 126260,
"batchId" : null,
"accessDays" : 30,
"quantity" : 2
}
]
}`
I am trying to do this using PHP :
`$condition = array( "session_id"=>'1111', 'items.itemId'=>126260, 'items.accessDays'=>30);
$new_values = array( '$set' => array("items.$.quantity" => 10) );
$cart_coll->update($condition, $new_values);`
But when I run this code, it updates the first nested object instead of the second.
What am I doing wrong here ? Does mongodb not consider multiple conditions in nested objects ?
You need to use $elemMatch to get the array marker to be in the right place. So your $query becomes
$condition = array( "session_id"=>'1111',
"items" => array(
'$elemMatch'=>array("itemId"=>126260, 'accessDays'=>30)));

sum query mongodb php

I have this collection
> db.test.find()
{ "_id" : ObjectId("5398ddf40371cdb3aebca3a2"), "name" : "ahmed", "qte" : 30 }
{ "_id" : ObjectId("5398de040371cdb3aebca3a3"), "name" : "demha", "qte" : 35 }
{ "_id" : ObjectId("5398de140371cdb3aebca3a4"), "name" : "ahmed", "qte" : 50 }
{ "_id" : ObjectId("5398de210371cdb3aebca3a5"), "name" : "ahmed", "qte" : 60 }
i would like to sum "qte" where "name"= "ahmed" and print the sum with php
i know how to do with SQL but i have no idea how it is in mongodb.
Thanks :)
Use the aggregation framework.
Assuming you have an the current collection as $collection
result = $collection->aggregate(array(
array(
'$match' => array(
'name' => 'ahmed'
)
),
array(
'$group' => array(
'_id' => NULL,
'total' => array(
'$sum' => '$qte'
)
)
)
));
The two parts are the $match to meet the criteria, and the $group to arrive at the "total" using $sum
See other Aggregation Framework Operators and the Aggregation to SQL Mapping chart for more examples.
This is done with an aggregate statement:
db.test.aggregate([
{
$match: {
name: "ahmed"
}
},
{
$group: {
_id:"$name",
total: {
$sum: "$qte"
}
}
}
])

Categories