PHP Mongo Aggregation only returns _id - php

I am trying to return a collection of messages grouped by in_reply_to field, I have this code:
$result = $this->db->Message->aggregate(
array(
array(
'$project' => array('message' => 1, 'in_reply_to'=> 1, 'to_user' => 1, 'from_user' => 1)
),
array(
'$group' => array('_id' => '$in_reply_to'),
),
)
);
print_r($result);exit;
the result is:
Array (
[result] => Array (
[0] => Array (
[_id] => MongoId Object (
[$id] => 53a03d43b3f7e236470041a8
)
)
[1] => Array (
[_id] => MongoId Object (
[$id] => 53a03cbdb3f7e2e8350041bb
)
)
)
[ok] => 1
)
Ideally I'd like the entire Message object, but I did think that $project would be used to specify returns fields, even so, I dont get the fields I'm specifying.
Any help is greatly appreciated

In order to get all the messages in the thread you basically want to $push
$result = $this->db->Message->aggregate(
array(
array(
'$group' => array(
'_id' => '$in_reply_to',
'messages' => array(
'$push' => array(
'_id' => '$_id',
'message' => '$message',
'to_user' => '$to_user',
'from_user' =>'$from_user'
)
)
)
)
)
);
MongoDB 2.6 you have the $$ROOT variable that shortens this:
$result = $this->db->Message->aggregate(
array(
array(
'$group' => array(
'_id' => '$in_reply_to',
'messages' => array(
'$push' => '$$ROOT'
)
)
)
)
);
So that puts all of the related messages inside the "messages" array tied to that key.
Just as side note, while you can do this you may as well just sort the results by your "in_reply_to" field and process them that way looking for changes in the value to indicate a new thread.
Sorting with a find would be the fastest way to process, even if it does not conveniently put everything right under the one key.

If you want to get additional fields beside _id field, when using $group operator, you need to include them using some of the available accummulators like $first or $last. You can see the full list on the MongoDB $group documentation page.
The query will look like this:
$result = $this->db->Message->aggregate(
array(
array(
'$project' => array(
'message' => 1,
'in_reply_to'=> 1,
'to_user' => 1,
'from_user' => 1
)
),
array(
'$group' => array(
'_id' => '$in_reply_to',
'message' => array('$first' => '$message'),
'to_user' => ('$first' => '$to_user'),
'from_user' => ('$first' => '$from_user')
),
),
)
);
If the message, to_user and from_user values are same in all documents using $last instead of $first $last will produce the same results.

Related

how to retrieve a table with many tables with PDO

Recently I've been using PDO
and I'd like to get a table with hasmany related table.
I can get a table with the tables like this
array (
0 =>
stdClass::__set_state(array(
'order_id' => '170',
'purchase_id' => '222',
'product_option_id' => '014',
)),
1 =>
stdClass::__set_state(array(
'order_id' => '170',
'purchase_id' => '600',
'product_option_id' => '015',
)),
)
with SQL query like this
SELECT ord.order_id,puc.purchase_id,puc.product_option_id
FROM order ord
JOIN purchase puc
ON ord.order_id = puc.order_id
WHERE ord.order_id = '170'
However I'd like to get this data like this
array (
0 =>
array(
'order_id' => '170',
'purchase_id' => '222',
'purchase' => array(
0 =>
array(
'purchase_id' => '222',
'product_option_id' => '014',
),
1 =>
array(
'purchase_id' => '600',
'product_option_id' => '015',
),
)
)
)
How do get the data like this with PDO?
Thank you
Luckily, you CAN have it with PDO.
Were you need anything but id from orders, the mission were impossible.
But as long as you need only unique value from orders, you can get the thing like this
array (
170 =>
array(
0 =>
array(
'purchase_id' => '222',
'product_option_id' => '014',
),
1 =>
array(
'purchase_id' => '600',
'product_option_id' => '015',
),
)
)
)
with as simple code as
$sql = "your sql";
$pdo->query($sql)->fetchAll(PDO::FETCH_GROUP);
However, I see very little sense in fetching the same order ID usedto filter the data. May be it should be some other query that is closer to your real needs? Like one that is fetching several orders?
You can't get it like this with PDO, but you can rearrange the results. A possible way to do it would be to use the array_reduce function.
$orders = array_reduce($results, function ($carry, $item) {
if (!isset($carry[$item->order_id])) {
$carry[$item->order_id] = array(
'order_id' => $item->order_id,
'purchase' => array(array(
'purchase_id' => $item->purchase_id,
'product_option_id' => $item->product_option_id,
));
);
} else {
$carry[$item->order_id]['purchase'][] = array(
'purchase_id' => $item->purchase_id,
'product_option_id' => $item->product_option_id,
);
}
return $carry;
}, array());

How to subtract in mongodb php

$getdataPipeline = array(
array(
'$match' => array(
'project_id' => array('$in' => $mysql_project_id) // Validating project ID
),
'$match' => array('project_id' => $project_id)
),
array(
'$group' => array(
'_id' => array('pro_id' => '$project_id', 'uid' => '$user_id'),
"wh" => array('$subtract' => array(array('$sum' => '$toltal_timein_minutes'), array('$sum' => '$holding_durationin_minutes')))
))
);
Running query:
$ValidProjectIdInMongo = $collection->aggregate($getdataPipeline);
I'm getting an error like
Uncaught exception 'MongoResultException' with message 'localhost:27017: exception: unknown group operator '$subtract''
The $sum is the accumulator to be used with $group, so it must be the top level operator used. Therefore your other operations need to happen "inside" the $sum:
$getdataPipeline = array(
array(
'$match' => array('project_id' => $project_id)
),
array(
'$group' => array(
'_id' => array('pro_id' => '$project_id', 'uid' => '$user_id'),
"wh" => array(
'$sum' => array(
'$subtract' => array(
'$toltal_timein_minutes',
'$holding_durationin_minutes'
)
)
)
)
)
);
You can do it here because this is basic subtraction, but more complex operations would generally require a separate $project stage after the $group.
Also note that yout $match pipeline stage is incorrect and will actually be interpretted just as I have re-written above. You perhaps mean in $in condition for both the possible values, which is a logical $or.
You also have what looks like typing errors in the field names.

Condensing, restructuring and adding subarrays

I've been scratching my head and failing miserably at coming up with a solution to my array structuring issue. I'm not sure exactly what part would be better to try and fix, the data being returned from SQL or the PHP array after the fact.
My SQL data is returned like this:
$i = 0;
while ( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_ASSOC ) ) {
$colData[$i] = array(
'name' => $row['FULLNAME'],
'invoice' => $row['CUST_InvoiceNumber_020911544'],
array(
'service' => $row['CUST_Service_052400634'],
'date' => date_normalizer($row['CUST_ServiceDate_064616924']),
'service_amount' => $row['CUST_ServiceAmount_054855553'],
),
'do_all_for' => $row['CUST_DoAllFor_021206685'],
'memo' => $row['CUST_Memo_021614200'],
'paymenttype' => $row['CUST_PAYMENTTYPE_123838203'],
'deposit' => $row['CUST_DEPOSIT_124139703'],
'datepaid' => date_normalizer($row['CUST_DATEPAID_124941578']),
);
$i++;
}
And the resultant array has this structure:
array (
0 =>
array (
'name' => 'ABRAHAM PRETORIS',
'invoice' => '63954',
0 =>
array (
'service' => 'Tree Work',
'date' => '2015-01-22',
'service_amount' => '1305.00',
),
'do_all_for' => '4924.68',
'memo' => 'CHECK #947 $2400',
'paymenttype' => 'VISA',
'deposit' => '4429.48',
'datepaid' => '2015-02-09',
),
1 =>
array (
'name' => 'ABRAHAM PRETORIS',
'invoice' => '63954',
0 =>
array (
'service' => 'DRF',
'date' => '2015-01-22',
'service_amount' => '740.00',
),
'do_all_for' => '4924.68',
'memo' => 'CHECK #947 $2400',
'paymenttype' => 'VISA',
'deposit' => '4429.48',
'datepaid' => '2015-02-09',
),
2 =>
array (
'name' => 'ABRAHAM PRETORIS',
'invoice' => '63954',
0 =>
array (
'service' => 'Stumps',
'date' => '2015-01-26',
'service_amount' => '360.00',
),
'do_all_for' => '4924.68',
'memo' => 'CHECK #947 $2400',
'paymenttype' => 'VISA',
'deposit' => '4429.48',
'datepaid' => '2015-02-09',
),
Notice that I'm getting a new subarray for the same person because the sub-subarray (service, date & service_amount) has multiple values.
What I'm trying to accomplish is condensing the array so that I only have one array for "ABRAHAM PRETORIS" etc, but all of the different services listed as a sub array. I would like it to look like this:
array (
0 =>
array (
'name' => 'ABRAHAM PRETORIS',
'invoice' => '63954',
0 =>
array (
'service' => 'Tree Work',
'date' => '2015-01-22',
'service_amount' => '1305.00',
),
1 =>
array (
'service' => 'DRF',
'date' => '2015-01-22',
'service_amount' => '740.00',
),
2 =>
array (
'service' => 'STUMPS',
'date' => '2015-01-26',
'service_amount' => '360.00',
),
'do_all_for' => '4924.68',
'memo' => 'CHECK #947 $2400',
'paymenttype' => 'VISA',
'deposit' => '4429.48',
'datepaid' => '2015-02-09',
),
I've looked at tons of examples of nested foreach statements and php array functions but I just can't wrap my head around how to loop through and add the additional services to the array then proceed when it's a row with a different name and/or invoice number.
Thanks in advance for the help!!
First, make sure your SQL query has an order by name, invoice. That will ensure all the records you want to group are sequential.
Then you have to create a loop with some additional inner logic:
// Creates an array to hold the final array.
$result = array();
// This var will keep track of name changes.
$current_name = '';
while ( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_ASSOC ) )
{
// Let's check if the name changed. This will be true for the first
// time the loop runs.
if($current_name != $row['FULLNAME'])
{
// If we are beginning, the if below will not run. But in subsequent
// records, it will add the acumulated array to the main result.
if($current_name != '') $result[] = $temp;
// The temp array will be populated with all data that DOES NOT change
// for the current name.
$temp = array('name' => $row['FULLNAME'],
'invoice' => $row['CUST_InvoiceNumber_020911544'],
'do_all_for' => $row['CUST_DoAllFor_021206685'],
'memo' => $row['CUST_Memo_021614200'],
'paymenttype' => $row['CUST_PAYMENTTYPE_123838203'],
'deposit' => $row['CUST_DEPOSIT_124139703'],
'datepaid' => date_normalizer($row['CUST_DATEPAID_124941578']),
);
// Update the current name.
$current_name = $row['FULLNAME'];
}
// The part that runs only on name changes has finished. From now on, we
// will take care of data which will be accumulated
// in a sub-array (until name changes and the block above resets it).
$temp['sub-array'][] =
array('service' => $row['CUST_Service_052400634'],
'date' => date_normalizer($row['CUST_ServiceDate_064616924']),
'service_amount' => $row['CUST_ServiceAmount_054855553']);
}
// After the loop, the last temp array needs to be added too.
$result[] = $temp;
This is the general concept: you will create a temporary array to hold the current name, inside which you will acummulate other data. Once the name changes, the acummulated data will be dumped to the main result, the temp array is reset, and a new acummulation begins.
I can't test the code right now, so it probably needs some fixes, but this approach works really well, and my point here is to show you the concept, so you can adapt it to your specific needs.

Add new array values to existing array position

I have array which is presenting like this:
$array = array (
"list" => array(
"serwer-1" => 3
),
"servers" => array(
3 => array(
"msg" => "{'some data': 123123121313}",
"status" => 200
),
),
);
I want add new items to $array['list'] for example:
array (
"list" => array(
"serwer-1" => 3
//NEW DATA HERE
"serwer-2" => 7,
),
"servers" => array(
3 => array(
"msg" => "{'some data': 123123121313}",
"status" => 200
),
),
);
Maybe it's trivial, but I haven't any idea how to do this - I have bad day today :(
In order to add a key-value pair to an existing array just do it like the key exists there.
$array["list"]["serwer-2"] = 7;
Then you will get desired result.

Mongodb $exist not working

Ok I am not sure why this is not working I know the field is there because it has sub arrays in this mydetails field.
function firsttime($uid){
$collection = static::db()->members;
var_dump($collection->findOne(array("_id"=> new MongoId($uid), array("mydetails"=> array('$exists' => true)))));
}
all it returns is NULL
is there a better way to find if there is or is not a field
in this example I want to see if the field mydetails exist?
It would be nice if I could either have a true or false return.
an example data
array (
'_id' => new MongoId("53b9ea3ae7fda8863c8b4568"),
'mydetails' =>
array (
'name' =>
array (
'first' => 'Russell',
'last' => 'Harrower',
),
'email' => 'hidden#ipet.xyz',
'birthday' =>
array (
'day' => '02',
'month' => '02',
'year' => '1988',
),
)
)
You got an array( too much in there. Try this:
$collection->findOne(array("_id"=> new MongoId($uid), "mydetails"=> array('$exists' => true)));

Categories