MongoDB php add tz offset to aggregate query and average output - php

I know that mongoDB (php-driver) saves the date as ISODate in Milliseconds UTC, so i have to add a offset for my timezone (GMT +2:00) to the aggregate query.
$dateStart = new MongoDB\BSON\UTCDateTime(strtotime("now 00:00:00") * 1000);
$dateEnd = new MongoDB\BSON\UTCDateTime(time() * 1000);
$queryHours = [
['$match' => ['date' => ['$gte' => $dateStart, '$lte' => $dateEnd]]],
['$project' => ['date' => ['$add' => ['$date', 2*60*60000]]]],
['$group' => ['_id' => ['$hour' => '$date'], 'average' => ['$avg' => '$zapi.apiRes.curlHeader.total_time']]],
['$sort' => ['_id' => 1]],
];
The $hour result seems to be correct, but i get no "average" result and i doesn't see my failure since days, staring at the code. :(
Sample Document:
{
"_id" : ObjectId("58e4e3ca135d9e038242b248"),
"date" : ISODate("2017-04-05T12:32:10Z"),
[...]
"zapi" : {
"apiRes" : {
"apiCall" : {
[...]
},
"curlHeader" : {
"url" : "http://api:/api/foo",
"http_code" : 204,
"total_time" : 0.02906399999999998,
[...]
},
}
},
}
I know that mongoDB working on this feature nativ: https://jira.mongodb.org/browse/SERVER-28611.
Thanks and best regards,
lun4tic

You can replace your $project stage with $addFields if you are on 3.4 version.
$queryHours =
[
['$match' => ['date' => ['$gte' => $dateStart, '$lte' => $dateEnd]]],
['$addFields' => ['date' => ['$add' => ['$date', 2*60*60000]]]],
['$group' => ['_id' => ['$hour' => '$date'], 'average' => ['$avg' => '$zapi.apiRes.curlHeader.total_time']]],
['$sort' => ['_id' => 1]],
];
Or
You can simply move the timezone arithmetic from $project stage into $group stage.
['$group' => ['_id' => ['$hour' => [ '$add' => ['$date', 2*60*60000] ] ], 'average' => ['$avg' => '$zapi.apiRes.curlHeader.total_time']]],

Your error should be the project phase. You have to add 'zapi.apiRes.curlHeader.total_time' => 1 so try this:
$queryHours = [
['$match' => ['date' => ['$gte' => $dateStart, '$lte' => $dateEnd]]],
['$project' => ['date' => ['$add' => ['$date', 2*60*60000]], 'zapi.apiRes.curlHeader.total_time' => 1]],
['$group' => ['_id' => ['$hour' => '$date'], 'average' => ['$avg' => '$zapi.apiRes.curlHeader.total_time']]],
['$sort' => ['_id' => 1]],
];

Related

How to do a group within a group in Mongo?

I have a query where by:
$offenses = \App\LawCase::raw(function ($collection) {
return $collection->aggregate([
[
'$match' => ['active' => true, 'type' => 'criminal', 'current_offense_literal'=> ['$exists' => true]]
],
[
'$group' => ['_id' => '$current_offense_category',
'offense_literals' => ['$addToSet' => '$current_offense_literal'], 'code'=> ['$addToSet' => '$current_offense'], 'count' => ['$sum' => 1]]
]
]);
});
It works fine but I want to get how many occurrences of a particular 'current_offense_literal' I got above. I guess a group within a group?

Add a row to an array

I'm trying to create a function, in order to create an array.
I'm able to create this array :
$result = [ 'data' =>
[0 =>
[
'months' => $months[0],
'years' => $years[0],
'date' => $date[0],
'hr' => $hr[0],
'isms' => $isms[0],
'product' => $product[0],
'exploitation' => $exploitation[0]
],
],
];
And my prob is that i would like to create a function(PHP) in order to add some rows in this array. I would like to have this kind of result :
$result = [ 'data' =>
[0 =>
[
'months' => $months[0],
'years' => $years[0],
'date' => $date[0],
'hr' => $hr[0],
'isms' => $isms[0],
'product' => $product[0],
'exploitation' => $exploitation[0]
],
[
'months' => $months[0],
'years' => $years[0],
'date' => $date[0],
'hr' => $hr[0],
'isms' => $isms[0],
'product' => $product[0],
'exploitation' => $exploitation[0]
]
]
];
But everytime i try to push, it creates a "1=>...."
What can I do ? Could you help me ?
You need to do like below:-
$result['data'][] = [
'months' => $months[0],
'years' => $years[0],
'date' => $date[0],
'hr' => $hr[0],
'isms' => $isms[0],
'product' => $product[0],
'exploitation' => $exploitation[0]
];
Output:-https://eval.in/990308
Your first array should be like this -
$result = [ 'data' =>
[0 =>
[0 =>
[
'months' => $months[0],
'years' => $years[0],
'date' => $date[0],
'hr' => $hr[0],
'isms' => $isms[0],
'product' => $product[0],
'exploitation' => $exploitation[0]
],
],
],
];
Now use below code to add new row to your array -
$row = [
'months' => $months[0],
'years' => $years[0],
'date' => $date[0],
'hr' => $hr[0],
'isms' => $isms[0],
'product' => $product[0],
'exploitation' => $exploitation[0]
];
$result['data'][0][] = $row;
print_r($result);

Mongo aggregate multi match/group

I have a data of stores, report_type is every type of data
- type 3 stored how much money that clients buy and how many transaction.
- type 5 stored how many people and average age of them.
I need an array like this:
"report_type3"
(
[_id] => 2441
[amountclient] => 3749078452,
[trans] => 1000
),
(
[_id] => 2442
[amountclient] => 15533754,
[trans] => 900
)
,
"report_type5"
(
[_id] => 2441
[people] => 120,
[avgage] => 23,
),
(
[_id] => 2441
[people] => 120,
[avgage] => 23,
)
My query looks like this, but it's not working
$cursor = $collection->aggregate([
['$match' => [ 'id_station' => ['$in' => $store ], 'report_type' => 3,
'date' => ['$gte'=> new MongoDB\BSON\UTCDateTime(strtotime("2017-07-01")*1000), '$lte'=> new MongoDB\BSON\UTCDateTime(strtotime("2017-08-05")*1000)]
]
],
['$group' => [
'_id' => [ 'id_station' => '$id_station', 'receiptid' => '$receiptid' ],
'amount' => [ '$sum' => '$amount' ]
]], .... *I group by receiptid because there are many items in one clients.*
[ '$group' => [
'_id' => '$_id.id_station',
'amount' => [ '$sum' => '$amount' ],
'trans' => [ '$sum' => '$trans' ]
]],
[
'$project' => [
'report_type3' => ['_id','$amountclient','$trans']
]
],
['$match' => [ 'id_station' => ['$in' => $store ], 'report_type' => 5,
'date' => ['$gte'=> new MongoDB\BSON\UTCDateTime(strtotime("2017-07-01")*1000), '$lte'=> new MongoDB\BSON\UTCDateTime(strtotime("2017-08-05")*1000)]
]
],
[ '$group' => [
'_id' => ['$_id.id_station', 'area_type' => '$area_type'],
'people' => [ '$sum' => '$people' ],
'avgage' => [ '$avg' => '$age' ]
]],
[
'$project' => [
'report_type5' => [ '_id', 'people', 'avgage' ]
]
]
]);
Could you please help me on this?

Is $sort always required after a $project and $match in Mongodb aggregation pipeline?

I have a working aggregation call that performs a $project, $match and $sort (by _id) which works perfectly fine.
In order to optimize my app, I tried dropping the $sort part at the end because I'm actually sorting by a datetime string in my php app after the cursor function is returned. Hence why I no longer wish to sort by _id.
However, when I remove this part of the pipeline, it returns no data. Is this a required part of my call?
$pipeline = [
['$project' => [
'date' => '$date',
'time' => '$time',
'datetime' => '$datetime',
'dev_id' => '$dev_id'
],
],
['$match' => [
'dev_id' => ['$in' => $deviceIdArray],
'datetime' => ['$gte' => $this->dateFrom, '$lte' => $this->dateTo]],
],
['$sort' => ['_id' => -1],
],
];
First you are doing project then doing match, so you are not getting any result as output.
Try the code below:-
$pipeline = [
['$match' => [
'dev_id' => ['$in' => $deviceIdArray],
'datetime' => ['$gte' => $this->dateFrom, '$lte' => $this->dateTo]],
]
['$project' => [
'date' => '$date',
'time' => '$time',
'datetime' => '$datetime',
'dev_id' => '$dev_id'
],
]
];

MongoDB or operator for range

This is the range Im using :
$Range1=array('$or'=>array(
array( '$gte' => floatval($O_NLA), '$lte' => $N_NLA ),
array( '$gte' => floatval($N_SLA), '$lte' => $O_SLA ))
);
$Range2=array('$or'=>array(
array( '$gte' => floatval($N_SLO), '$lte' => $O_SLO ),
array( '$gte' => floatval($O_NLO), '$lte' => $N_NLO ))
);
A part of my query :
$finder=$reposit->findBy(
array(
'field1' => $Range1,
'field2' => $Range2,
'display'=>1
))->toArray();
"message":"localhost:27017: invalid operator: $or"..... (it's long though)
How can I use $or operator with doctrine without query builder?
findBy([
'$or' => [
['field1' => ['$gte' => floatval($O_NLA), '$lte' => $N_NLA]],
['field1' => ['$gte' => floatval($N_SLA), '$lte' => $O_SLA]],
['field2' => ['$gte' => floatval($N_SLO), '$lte' => $O_SLO]],
['field2' => ['$gte' => floatval($O_NLO), '$lte' => $N_NLO]]
]
, 'display' => 1
])
Or actually:
findBy([
'$and' => [
['$or' => [
['field1' => ['$gte' => floatval($O_NLA), '$lte' => $N_NLA]],
['field1' => ['$gte' => floatval($N_SLA), '$lte' => $O_SLA]],
]],
['$or' => [
['field2' => ['$gte' => floatval($N_SLO), '$lte' => $O_SLO]],
['field2' => ['$gte' => floatval($O_NLO), '$lte' => $N_NLO]]
]]
]
, 'display' => 1
])
To be more precise according to your question.
Since the $or works upon condition which then contains a find() like array.

Categories