Change query response array value using MongoDB and PHP - php

I am new in MongoDB. I have created query in MongoDB using PHP. My query is as follows:
$date_start = new MongoDate(strtotime("2016-06-08T18:30:00.000Z"));
$default_date = new MongoDate(strtotime("1970-01-01T00:00:00.000Z"));
$pipeline = array(
array('$project' => array(
'MainsPower' => 1,
'EventTS' => 1
)),
array('$unwind' => array(
'path' => '$MainsPower',
'includeArrayIndex' => "arrayIndex",
'preserveNullAndEmptyArrays' => true
)),
array('$match' => array(
'$and' => array(
//array('EventTS' => array('$gt' => $date_start)),
//array('PanelID' => 'A00911'),
array("MainsPower" => array('$ne' => null))
)
)),
array(
'$project' => array(
'MainsPower' => 1,
'_id' => 0,
'EventTS' => array(
'$add' => array(
array('$subtract' => array('$EventTS', $default_date)),
array('$multiply' => array(60000, '$arrayIndex'))
)
)
)
),
);
$result = $collection - > aggregate($pipeline);
Output of query is as below
Array
(
[0] => Array(
[EventTS] => 1497033900000[MainsPower] => 204
)
[1] => Array(
[EventTS] => 1497034800000[MainsPower] => 204
)
[2] => Array(
[EventTS] => 1497035700000[MainsPower] => 204
)
)
But, I want output as below because while plotting graph I need data in below format I searched many solution and I tried to apply the same but failed to get records in required format
Array
(
[0] => Array(
[1497033900000, 204]
)
[1] => Array(
[1497034800000, 204]
)
[2] => Array(
[1497035700000, 204]
)
)

Use the below code:
$formattedResponse = array();
foreach($mongoOutput as $out){
$formattedResponse[] = array($out["EventTS"],$out["MainsPower"]);
}
where mongoOutput is the default output you are getting from mongo, and formattedResponse is the variable in which you will get the output in desired format.
Please change variable names as per your needs.

Related

How to get distance with $near?

I can get my elements, with the following code :
$lines = $loc->find(
array("loc" =>
array('$near' =>
array('$geometry' =>
array('type' => 'Point', 'coordinates' =>
array(floatval($longitude), floatval($latitude))
),
'$maxDistance' => 10000 //meters
)
)
)
);
This is the results :
Array
(
[_id] => MongoId Object
(
[$id] => 5490003c815289663d8bbd95
)
[name] => My adress
[loc] => Array
(
[type] => Point
[coordinates] => Array
(
[0] => 5.050948
[1] => 45.040419
)
)
)
But now, I need to get the distance from my given point.
Is it possible ?
EDIT :
This is the code used to insert datas :
$loc = $client->localisation->localisation;
$loc->ensureIndex( array("loc" => "2dsphere"));
$loc->insert(
array(
"idobject" => $myValue
"name" => $myName
"loc" => array("type" => "Point", "coordinates" => array($longitude, $latitude))
));
The $near operator does not return a distance along with the returned results. It only orders the documents in the response.
Under the PHP driver the easiest form is to use the $geoNear aggregation pipeline stage instead. This allows you to project a field in the results, or even use it in other pipeline stages:
$loc->aggregate(
array(
array(
'$geoNear' => array(
'near' => array(
'type' => 'Point',
'coordinates' =>array(floatval($longitude), floatval($latitude))
),
'maxDistance' => 1000,
'distanceField' => 'distance',
'spherical' => true
)
)
)
);
There is is a database command form for geoNear as well, but direct command results are often not as nice.

Traversing an nested array and transform some values

I'm looking for a clean solution to transform a nested array.
Here is what I'm trying to achieve...
Original array:
$map = array(
'name' => 'super test',
'machine_name' => 'super_test',
'class' => 'openlayers_map_map',
'options' => array(
'width' => 'auto',
'height' => '300px',
'contextualLinks' => 1,
'provideBlock' => 1,
'view' => array(
'center' => array(
'lat' => '0',
'lon' => '0',
),
'rotation' => '0',
'zoom' => '2',
),
'layers' => array(
'0' => array(
'name' => 'Ma super layer',
'machine_name' => 'plouf',
'class' => 'openlayers_layer_tile',
'options' => array(
'source' => array(
'name' => 'Ma super layer',
'machine_name' => 'plouf',
'class' => 'openlayers_source_osm'
),
'param1' => 'ca roule'
)
),
),
'controls' => array(
'control_mouseposition',
'0' => array(
'name' => 'Control attribution',
'machine_name' => 'openlayers_control_attribution',
'class' => 'openlayers_control_attribution',
'options' => array(
'collapsible' => 1
)
),
'control_rotate',
'control_zoom',
),
'interactions' => array(
'interaction_doubleclickzoom',
'interaction_dragpan',
'interaction_dragrotateandzoom',
'interaction_mousewheelzoom',
),
)
);
Final array:
$map = array(
'name' => 'super test',
'machine_name' => 'super_test',
'class' => 'openlayers_map_map',
'options' => array(
'width' => 'auto',
'height' => '300px',
'contextualLinks' => 1,
'provideBlock' => 1,
'view' => array(
'center' => array(
'lat' => '0',
'lon' => '0',
),
'rotation' => '0',
'zoom' => '2',
),
'layers' => array(
'0' => (object) openlayers_layer_tile
'name' => 'Ma super layer',
'machine_name' => 'plouf',
'class' => 'openlayers_layer_tile',
'options' => array(
'source' => (object) openlayers_source_osm
'name' => 'Ma super layer',
'machine_name' => 'plouf',
'class' => 'openlayers_source_osm'
),
'param1' => 'ca roule'
)
),
),
'controls' => array(
'control_mouseposition',
'0' => (object) openlayers_control_attribution
'name' => 'Control attribution',
'machine_name' => 'openlayers_control_attribution',
'class' => 'openlayers_control_attribution',
'options' => array(
'collapsible' => 1
)
),
'control_rotate',
'control_zoom',
),
'interactions' => array(
'interaction_doubleclickzoom',
'interaction_dragpan',
'interaction_dragrotateandzoom',
'interaction_mousewheelzoom',
),
)
);
Basically, I need to traverse the array, find all children with the 'class' key and transform them into objects of the same name.
If you don't have the classes ready to be instantiated, this code will create anonymous objects instead. (The class name still being present as a property.)
function class_to_object (&$arr) {
if (is_array($arr)) {
foreach ($arr as $key => &$val) {
class_to_object($val);
}
if (isset($arr['class'])) {
$arr = (object) $arr;
}
}
}
class_to_object($map);
Result :
(Notice that the first array is turned into an object, since it contains the field "class" too. I guess you can tweak the function easily enough if you don't want that behavior)
stdClass Object
(
[name] => super test
[machine_name] => super_test
[class] => openlayers_map_map
[options] => Array
(
[width] => auto
[height] => 300px
[contextualLinks] => 1
[provideBlock] => 1
[view] => Array
(
[center] => Array
(
[lat] => 0
[lon] => 0
)
[rotation] => 0
[zoom] => 2
)
[layers] => Array
(
[0] => stdClass Object
(
[name] => Ma super layer
[machine_name] => plouf
[class] => openlayers_layer_tile
[options] => Array
(
[source] => stdClass Object
(
[name] => Ma super layer
[machine_name] => plouf
[class] => openlayers_source_osm
)
[param1] => ca roule
)
)
)
[controls] => Array
(
[0] => stdClass Object
(
[name] => Control attribution
[machine_name] => openlayers_control_attribution
[class] => openlayers_control_attribution
[options] => Array
(
[collapsible] => 1
)
)
[1] => control_rotate
[2] => control_zoom
)
[interactions] => Array
(
[0] => interaction_doubleclickzoom
[1] => interaction_dragpan
[2] => interaction_dragrotateandzoom
[3] => interaction_mousewheelzoom
)
)
)
This totally untested recursive function may get you on the right track:
function recursive_hydrate_array($arr)
{
if(!is_array($arr) || !isset($arr["class"]))
{
throw new Exception("Argument is not an array or does not have a 'class' key.");
}
$klass = $arr["class"];
unset($arr["class"]);
$obj = new $klass();
foreach($arr as $k => $v)
{
if(is_array($arr[$k]) && isset($arr[$k]["class"]))
{
$obj->{$k} = recursive_hydrate_array($arr[$k]);
}
else
{
$obj->{$k} = $arr[$k];
}
}
return $obj;
}
Note that I am making three assumptions here:
The classes in question already exist.
Each class can be instantiated without passing any parameters to its constructor.
All relevant properties of each class are public and can be set from outside of the class.

mongodb - php - aggregate projection and match

I'm trying to match criteria (cl=1) and then aggregate all the results by month (I've tried year/month/date as well to no luck).
When I run the code below it seems like it works properly, the output shows the months etc, however none of the stats are being added.
The $du field in Mongo is a MongoDate object.
Does anyone have any idea what I'm doing wrong?
See very bottom for a code snippet that is searched by date and returns a result.
$result = $collection->aggregate(
array(
array('$match' => array(
'cl' => $app_id) # application id, int such as "2"
),
array('$project' => array(
//'year' => array('$year' => '$du'),
'month' => array('$month' => '$du'),
//'week' => array('$week' => '$du'),
)),
array('$group' =>
array(
'_id' => array(
'month' => '$month',
),
'i' => array('$sum' => '$vs.i'),
'r' => array('$sum' => '$vs.r'),
'u' => array('$sum' => '$vs.u'),
)
),
array(
'$sort' => array(
'_id' => -1
)
),
array(
'$limit' => 14
)
)
);
Output:
Array
(
[result] => Array
(
[0] => Array
(
[_id] => Array
(
[month] => 10
)
[i] => 0
[r] => 0
[u] => 0
)
[1] => Array
(
[_id] => Array
(
[month] => 9
)
[i] => 0
[r] => 0
[u] => 0
)
.......
working - searching by date
$start = new MongoDate(strtotime("2012-10-01 00:00:00"));
$end = new MongoDate(strtotime("2013-10-15 00:00:00"));
$cursor = $collection->find(array("du" => array('$gt' => $start, '$lte' => $end)));
while ($cursor->hasNext()) {
$document = $cursor->getNext();
print_r($document);exit;
}
# RESPONSE
Array
(
[_id] => MongoId Object
(
[$id] => 51acbfd33dd49497848a8e5b
)
[cl] => 1
[du] => MongoDate Object
(
[sec] => 1370275795
[usec] => 0
)
[vs] => Array
(
[i] => 11
[u] => 1
)
)
The stats aren't there because you haven't chosen to project them. Project gives you _id by default, but anything else you want must be specified.
You'll need something like this instead of your current $project:
array('$project' => array(
'month' => array('$month' => '$du'),
'vs' => 1
))

MailChimp API member-info issue

I need to retrieve all users infos via api, looking for it in the documentation I found this:
http://apidocs.mailchimp.com/api/2.0/lists/member-info.php
This is my code:
$params = array(
'id' => $list_id,
'emails' => array(
'euid' => $member_id,
),
);
$infos = $this->MailChimp->call('lists/member-info', $params);
print_r($infos);
and this is the result:
Array ( [success_count] => 0 [error_count] => 1 [errors] => Array ( [0] => Array ( [email] => 63a885b7cf [error] => "email" should be a struct [code] => -100 ) ) [data] => Array ( ) )
What does " "email" should be a struct " means?
This also works and is slightly simpler:
$result = $MailChimp->call('lists/member-info', array(
'id' => $list_id,
'emails' => array(array('email'=>$email))
));
The above example uses this API: https://github.com/drewm/mailchimp-api/
Solved!
My $params array was wrong.
MailChimp need an array format in this way:
$params = array(
'id' => $list_id,
'emails' => array(
0 => array(
'euid' => $member_id,
),
),
);
Mine does this as follows:
array(
'0' => array('email' => $mail1)
'1' => array('email' => $mail2)
...
...
);
Thanks,

Removing unnecessary nested index in array (generated by Cake PHP)

The following question can either be solved by probably changing my use of the find method in Cake PHP OR using some PHP function. I would prefer to solve it using Cake but it doesn't matter too much. I have any array like this:
Array
(
[0] => Array
(
[Model] => Array
(
[id] => 14
[foo] => bar
)
)
[1] => Array
(
[Model] => Array
(
[id] => 15
[foo] => something
)
) .............
I just want to remove the Model index and just use the numeric one. The following function generated this array:
$arr = $this->Model->find('all', array('contain' => false ) );
I probably need to change the 'contain' part of the call. Basically, in addition to the data that appears under each Model index, I also have a second and third model and the contain = false just restricts Cake from getting data from the current model (Model).
If I understand your question correctly, I think CakePHP's Set::combine function will help http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::combine :
$result = Set::combine($your_array, '{n}.Model.id', '{n}.Model.data');
This will result in:
Array
(
[14] => Array
(
[foo] => bar
)
[15] => Array
(
[foo] => something
)
)
you have to write your own piece of code to modify your array , here is a function that will do what you want (you can improve it)
function reformArray($array,$modelName)
{
foreach($arr as $key => $value)
{
$newArr[] = $arr[$key][$modelName];
}
return $newArr;
}
you have to pass your array and the Model Name to this function and will return you the result
$a = array(
array(
'User' => array(
'id' => 2,
'group_id' => 1,
'Data' => array(
'user' => 'mariano.iglesias',
'name' => 'Mariano Iglesias'
)
)
),
array(
'User' => array(
'id' => 14,
'group_id' => 2,
'Data' => array(
'user' => 'phpnut',
'name' => 'Larry E. Masters'
)
)
),
array(
'User' => array(
'id' => 25,
'group_id' => 1,
'Data' => array(
'user' => 'gwoo',
'name' => 'The Gwoo'
)
)
)
);
RUN THIS TO REMOVE THE MODEL NAME USER
Set::extract($a, '{n}.User');
WILL RETURN THIS
$a = array(
array(
'id' => 2,
'group_id' => 1,
'Data' => array(
'user' => 'mariano.iglesias',
'name' => 'Mariano Iglesias'
)
),
array(
'id' => 14,
'group_id' => 2,
'Data' => array(
'user' => 'phpnut',
'name' => 'Larry E. Masters'
)
),
array(
'id' => 25,
'group_id' => 1,
'Data' => array(
'user' => 'gwoo',
'name' => 'The Gwoo'
)
)
);
In CakePHP 2:
$data = $this->Model->find('all');
$data = Set::extract('/Model/.', $data );
You can achieve this result by foreach also
foreach ($data as $k => &$t) {
$t = $t['Model']
}
It will be much easier with Object. Change the Array with Object.
Actually I had faced the same problem with nested array And I found the solution with Set::map($array);
If you don't want the model_name as nested array, You can prefer this solution.
$data = array(
array(
"IndexedPage" => array(
"id" => 1,
"url" => 'http://blah.com/',
'hash' => '68a9f053b19526d08e36c6a9ad150737933816a5',
'get_vars' => '',
'redirect' => '',
'created' => "1195055503",
'updated' => "1195055503",
)
),
array(
"IndexedPage" => array(
"id" => 2,
"url" => 'http://blah.com/',
'hash' => '68a9f053b19526d08e36c6a9ad150737933816a5',
'get_vars' => '',
'redirect' => '',
'created' => "1195055503",
'updated' => "1195055503",
),
)
);
$mapped = Set::map($data);
/* $mapped now looks like: */
Array
(
[0] => stdClass Object
(
[_name_] => IndexedPage
[id] => 1
[url] => http://blah.com/
[hash] => 68a9f053b19526d08e36c6a9ad150737933816a5
[get_vars] =>
[redirect] =>
[created] => 1195055503
[updated] => 1195055503
)
[1] => stdClass Object
(
[_name_] => IndexedPage
[id] => 2
[url] => http://blah.com/
[hash] => 68a9f053b19526d08e36c6a9ad150737933816a5
[get_vars] =>
[redirect] =>
[created] => 1195055503
[updated] => 1195055503
)
)
you may do this :
$tmp = $this->Model->find('all', array('contain' => false ) );
$arr = $tmp['ModelName'];

Categories