something wrong with my loop from aggregation mongodb - php

i want to return the result of data from my aggregation mongodb for datatable, when i try to debug the aggregation result it return an array(with much data in it), then i just loop it but the return only one array data.. what happens here?
here my code:
$aggregation = [
[ '$match' => $condition ],
[
'$project' => [
'_id' => 1,
'id' => 1,
'companyid' => 1,
'user' => [
'employeeid' => 1,
'yearin' => 1,
'department' => 1,
'status' => 1
]
]
]
];
$appGeneralComponent = new AppGeneralComponent();
$result = $appGeneralComponent->datatableQuery($this->employeeCollection, $aggregation, []);
$result['data'] = array_map(function ($value) {
foreach ($value['user'] as $key => $v) {
return [
'uid' => Encoder::Encrypt($value['_id']),
'employeeid' => $v['id'],
'yearin' => $v['yearin'],
'department' => $v['department'],
'status' => $v['status']
];
}
}, $result['data']);
//here only return 1 array data which is it should 10 data
echo "<pre>";var_dump($result);exit;

Related

Mongodb with PHP aggregate() like cursor

I need to iterate over large result set of Mongodb collection in PHP. But I am getting timeout error. How to use it like a cursor till result is not finished and parse returned documents in chunks? The pipeline looks like
$mongoPipeline = [
[
'$match' => [
'global_campaign_id' => ['$in' => $campaigns_ids]
]
],
[
'$group' => [
'_id' => [
'global_campaign_id' => '$global_campaign_id',
'device_id' => '$device_id',
'partner_id' => '$partner_id',
],
'date_last' => ['$max' => '$date_created'],
'partner_id' => ['$first' => '$server'],
'campaign_id' => ['$first' => '$global_campaign_id'],
'device_id' => ['$first' => '$device_id']
]
],
[
'$limit' => 5,
],
];
$options = [
'allowDiskUse' => true,
//'maxTimeMS' => 1000 * 60 * 1,
'useCursor' => true,
//'explain' => true,
];
$result = [];
/** #var Cursor $cursor */
$cursor = $this->mongoDb->{$collection}->aggregate($pipeline, $options);
$cursor->setTypeMap(['root' => 'array', 'document' => 'array', 'array' => 'array']);
foreach ($result as $doc) $result[] = $this->parseDocument($doc);
return $result;
If I use $cursor->toArray() if fall down on timeoutError.
How this script should look like?

Removing part of an array from a multidimensional nested array

I've a four levels of nested array like this:
$array = [
[
'website' => [
'id' => 'one'
],
'children' => [
[
'website' => [
'id' => 'one.one'
],
'children' => [
[
'website' => [
'id' => 'one.one.one'
],
'children' => [
[
'website' => [
'id' => 'one.one.one.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.one.one.two'
],
'children' => []
]
]
],
[
'website' => [
'id' => 'one.one.two'
],
'children' => [
[
'website' => [
'id' => 'one.one.two.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.one.two.two'
],
'children' => []
]
]
]
]
],
[
'website' => [
'id' => 'one.two'
],
'children' => [
[
'website' => [
'id' => 'one.two.one'
],
'children' => [
[
'website' => [
'id' => 'one.two.one.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.two.one.two'
],
'children' => []
]
]
],
[
'website' => [
'id' => 'one.two.two'
],
'children' => [
[
'website' => [
'id' => 'one.two.two.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.two.two.two'
],
'children' => []
]
]
]
]
]
]
]
];
now, I'd like to remove some of the array element based on some rules. For simplicity, let's say, we'd like to remove array elements that has 'id' equals to one.one.two or one.two.one.
I was looking into some answers provided on stackoverflow and was trying to apply them to solve my issue, but didn't go much. The most chalanging thing is to unset part of an array where it's not the farthest point in the array.
I was trying this to note at which level I want to delete the arrays and then trying to unset them using those array index.
$pointer = [];
foreach($array as $key => $level1) {
$level1pointer = $key;
$pointer[] = markToDelete($level1, $level1pointer);
foreach($level1['children'] as $key => $level2) {
$level2pointer = $key;
$pointer[] = markToDelete($level2, $level1pointer, $level2pointer);
foreach($level2['children'] as $key => $level3) {
$level3pointer = $key;
$pointer[] = markToDelete($level3, $level1pointer, $level2pointer, $level3pointer);
foreach($level3['children'] as $key => $level4) {
$level4pointer = $key;
$pointer[] = markToDelete($level4, $level1pointer, $level2pointer, $level3pointer, $level4pointer);
}
}
}
}
function markToDelete($array, $level1 = null, $level2 = null, $level3 = null, $level4 = null) {
$exclusionList = [
'one.one.two',
'one.two.one'
];
if (!empty($array['website']) && in_array($array['website']['id'], $exclusionList)) {
print_r('marking for deletion: '. $array['website']['id'] . PHP_EOL);
return [
'id' => $array['website']['id'],
'level1' => $level1,
'level2' => $level2,
'level3' => $level3,
'level4' => $level4
];
}
return [];
}
I was also trying to use Iterator like this:
$it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array), \RecursiveIteratorIterator::LEAVES_ONLY);
$newArray = [];
foreach($it as $key => $value) {
$exclusionList = [
'one.one.two',
'one.two.one'
];
if(!in_array($value, $exclusionList)) {
print_r(sprintf('value: %s is ready to be deleted', $value).PHP_EOL);
$newArray[] = $value;
}
}
but I need a way to unset the array when looping through the iterator.
I'd like to get an output like this:
$array = [
[
'website' => [
'id' => 'one'
],
'children' => [
[
'website' => [
'id' => 'one.one'
],
'children' => [
[
'website' => [
'id' => 'one.one.one'
],
'children' => [
[
'website' => [
'id' => 'one.one.one.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.one.one.two'
],
'children' => []
]
]
],
]
],
[
'website' => [
'id' => 'one.two'
],
'children' => [
[
'website' => [
'id' => 'one.two.two'
],
'children' => [
[
'website' => [
'id' => 'one.two.two.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.two.two.two'
],
'children' => []
]
]
]
]
]
]
]
];
I'd really appreciate help on how to resolve this in more efficient way. Thanks.
Here's a recursive function that will do what you want. It traverses the array, looking for children whose website id is in the list of ids to be removed and unsetting them. Note that because of your additional top-level array, you need to iterate the function over those values.
function delete_entries(&$array, $ids_to_delete) {
foreach ($array['children'] as $index => &$child) {
if (in_array($child['website']['id'], $ids_to_delete)) {
unset($array['children'][$index]);
}
delete_entries($child, $ids_to_delete);
}
}
foreach ($array as &$arr) {
delete_entries($arr, array('one.one.two', 'one.two.one'));
}
var_export($array);
The output is as you desire but too long to reproduce here. See the demo on 3v4l.org
Update
The above code won't delete entries on the top level because the array structure is different from lower levels in the array. That can be dealt with in the outer foreach loop:
$excluded = array('two', 'one.one.two', 'one.two.one');
foreach ($array as $key => &$arr) {
if (in_array($arr['website']['id'], $excluded)) {
unset($array[$key]);
}
else {
delete_entries($arr, $excluded);
}
}
Updated demo

How to get particular nested array based on the given matched key?

How to get particular nested array based on the given matched key using PHP built in function
Scenario
$id = 1035; // Searching ID
$a = [
'id'=> 291,
'children' => [
[
'id' => 1034,
'children' => [
[
'id' => 111,
'name' => 'ABC',
'figure' => '6 digits',
'children'=> []
],
[
'id' => 1035,
'lft' => 'LEFT',
'children' => [
[
'id' => 1036,
'children' => [
[
'id' => 222,
'someKey' => 'some value',
'children'=> []
]
]
],
[
'id' => 333,
'someKey' => 'some value',
'children'=> []
]
],
]
],
],
[
'id' => 1024,
'title' => 'ABC',
'children' => [
],
]
]
];
Please note, 'id' & 'children' keys are always be there. How to get the "children" of "1035" ID..?
Expected Output
[
[
'id' => 1036,
'children' => [
[
'id' => 222,
'someKey' => 'some value',
'children'=> []
]
],
],
[
'id' => 333,
'someKey' => 'some value',
'children'=> []
]
];
Tried
function getRecursiveCategoryIds($key, $categories = []){
$return = null;
try {
array_walk_recursive($categories, function($v, $k) use ($key, &$return){
if (null != $return) {
// Run loop to get the next immediate "children" key
if ($k == 'children') { // It's not matching anymore
$return = $v;
//return false;
throw new Exception;
}
} else if($v == $key) {
// Found
$return = $v;
}
});
} catch(Exception $e) {}
return $return;
}
$d = getRecursiveCategoryIds($id, $a);
echo '<pre>D: '; print_r($d); die;
I tried by the above code, but the "if ($k == 'children') {" is not matched any more..!
Any suggestions are welcome... (PHP's Built in function is most prefer!)
I was able to do this. Please check the comments in the code:
<?php
$id = 1035; // Searching ID
$myObj = array();
$a = [
'id'=> 291,
'children' => [
[
'id' => 1034,
'children' => [
[
'id' => 111,
'name' => 'ABC',
'figure' => '6 digits',
'children'=> []
],
[
'id' => 1035,
'lft' => 'LEFT',
'children' => [
[
'id' => 1036,
'children' => [
[
'id' => 222,
'someKey' => 'some value',
'children'=> []
]
]
],
[
'id' => 333,
'someKey' => 'some value',
'children'=> []
]
],
]
],
],
[
'id' => 1024,
'title' => 'ABC',
'children' => [
],
]
]
];
function findObject($id, $obj) {
global $myObj;
// This is an object.
if (isset($obj["id"])) {
echo "Checking {$obj["id"]}<br />";
// Check the id to what we need.
if ($obj["id"] == $id) {
// Yay! We found it. Return the object.
echo "Yay we found {$obj["id"]}<br />";
$myObj = $obj;
}
else {
echo "Checking children of {$obj["id"]}<br />";
// See if it has any children
if (isset($obj["children"]) && count($obj["children"]) > 0) {
echo "There are children for {$obj["id"]}<br />";
foreach ($obj["children"] as $child) {
findObject($id, $child);
}
}
}
}
}
findObject($id, $a);
print_r($myObj);
Output
Checking 291Checking children of 291There are children for 291Checking 1034Checking children of 1034There are children for 1034Checking 111Checking children of 111Checking 1035Yay we found 1035Need to find a way to break out!Checking 1024Checking children of 1024Found it!Array
(
[id] => 1035
[lft] => LEFT
[children] => Array
(
[0] => Array
(
[id] => 1036
[children] => Array
(
[0] => Array
(
[id] => 222
[someKey] => some value
[children] => Array
(
)
)
)
)
[1] => Array
(
[id] => 333
[someKey] => some value
[children] => Array
(
)
)
)
)
Demo:
https://ideone.com/UoKqrU
https://3v4l.org/rWkPq
You can use function inside other check :
$id=1035;
$a = [
'id'=> 291,
'children' => [
[
'id' => 1034,
'children' => [
[
'id' => 111,
'name' => 'ABC',
'figure' => '6 digits',
'children'=> []
],
[
'id' => 1035,
'lft' => 'LEFT',
'children' => [
[
'id' => 1036,
'children' => [
[
'id' => 222,
'someKey' => 'some value',
'children'=> []
]
]
],
[
'id' => 333,
'someKey' => 'some value',
'children'=> []
]
],
]
],
],
[
'id' => 1024,
'title' => 'ABC',
'children' => [
],
]
]
];
function nigsearch($arr,$id)
{
if(gettype($arr) == 'array')
foreach($arr as $key =>$list)
{
if(gettype($list) == 'array'){
if(isset($list['id']))
{
if($list['id'] ==$id)
print_r($list['children']);
}
nigsearch($list,$id);
}
}
}
foreach($a as $key)
{
nigsearch($key,$id);
}

How can run recursive function on multiple Level array in php

I have two arrays one is $apiRes and second is $mappData i want to match fields exist in mappData array and assign value of apiRes to match field.
Note: response of api may have different and mapdata array will change according to api response My two array and output format :
<?php
$apiRes = [
[
'firstname' => 'first name des',
'title' => "title des",
'category' => 1,
'result' =>
[
0 => [
'name' => 'Masterpass',
'skill' => 'low level one'
],
1 => [
'name' => 'Visa',
'skill' => 'low level two'
],
2 => [
'name' => 'Pocketpos',
'skill' => 'low level three'
],
],
'list' => [
'product_name'=>'product name',
'amount' => [
'currency'=>'$',
'kind' => 'kind'
]
],
'priority' => 'Low',
'visible_to' => 'Everyone',
]
];
$mappData = [
0 => [
"src_field" => "firstname",
"target_field" => "new1519110449758",
"src_field_data_type" => "string"
],
1 => [
"src_field" => "result.name",
"target_field" => "new1519110811942",
"src_field_data_type" => "string"
],
2 => [
"src_field" => "list.product_name",
"target_field" => "new1519110451708",
"src_field_data_type" => "string"
],
3 => [
"src_field" => "list.amount.currency",
"target_field" => "new1517556165360",
"src_field_data_type" => "string"
]
];
My final output should be:
$output = [
"new1519110449758" => "first name des",
"new1519110451708" => "product name",
"new1517556165360" => "$",
"new1519110811942" => [
0 => "Masterpass",
1 => "Visa",
2 => "Pocketpos"
]
];
Please help
Thanks
I've changed your mappData slightly as it's difficult to know how to deal with the result.name element as it's repeated. What I've done is change it so that it's *result.name and it uses the fact there is an * in it to mean there are multiple values.
I've tried to comment the code with enough to explain each bit, the main principle is using a recursive routine to go through each level of the array one step at a time.
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
$apiRes = [
[
'firstname' => 'first name des',
'title' => "title des",
'category' => 1,
'result' =>
[
0 => [
'name' => 'Masterpass',
'skill' => 'low level one'
],
1 => [
'name' => 'Visa',
'skill' => 'low level two'
],
2 => [
'name' => 'Pocketpos',
'skill' => 'low level three'
],
],
'list' => [
'product_name'=>'product name',
'amount' => [
'currency'=>'$',
'kind' => 'kind'
]
],
'priority' => 'Low',
'visible_to' => 'Everyone',
]
];
$mappData = [
0 => [
"src_field" => "firstname",
"target_field" => "new1519110449758",
"src_field_data_type" => "string"
]
,
1 => [
"src_field" => "*result.name",
"target_field" => "new1519110811942",
"src_field_data_type" => "string"
],
2 => [
"src_field" => "list.product_name",
"target_field" => "new1519110451708",
"src_field_data_type" => "string"
],
3 => [
"src_field" => "list.amount.currency",
"target_field" => "new1517556165360",
"src_field_data_type" => "string"
]
];
$result = [];
// Process next element of the array
function getArrayElement ( $next, $data, $array = false ) {
// Extract key for this level
$key = array_shift($next);
// If starts with * then this means there are multiple of them
if ( $key[0] == "*" ){
$nextArray = true;
// remove from current key
$key = substr($key,1);
}
else {
$nextArray = false;
}
if ( $array ){
$res = [];
// extract the data from each element at this level
foreach ( $data as $read ) {
$res[] = $read[$key];
}
$data = $res;
}
else {
// Fetch the element for the key for this level
$data = $data [ $key ];
}
// If there are more levels to deal with then repeat this method
if ( count($next) > 0 ) {
$data = getArrayElement ( $next, $data, $nextArray );
}
return $data;
}
// Flatten out original array if necessary
if ( count($apiRes) == 1 ){
$apiRes = $apiRes[0];
}
// Process each part of lookup
foreach ( $mappData as $element ) {
// Create an array of the elments broken down into each level
$map = explode( '.', $element['src_field']);
$result[$element['target_field']] = getArrayElement($map, $apiRes);
}
print_r($result);

Recursively Create an Array from another Array

I am trying to make a multi-dimensional array build an array path adding the hr field so it looks like this:
I just can't figure out how to add the totals, nor create a sub-array so the dot notation in an option too. My goal is to get something like this:
[1] => [1][2][1][5][0][6] = 35 (the second child path "1")
[1] => [1][2][1][5][0][7] = 25
or Something like this:
array (
[children.0.children.0.children.0.total] = 20
[children.0.children.1.children.1.total] = 35
// etc
)
The complicated part is that it goes in different directions and I want to know what is the highest and lowest total based on the path:
==> Run Code Here or Copy/Paste
// -------------
// The Flattener
// -------------
function doit($myArray) {
$iter = new RecursiveIteratorIterator(new RecursiveArrayIterator($myArray));
$result = array();
foreach ($iter as $leafKey => $leafValue) {
$keys = array();
foreach (range(0, $iter->getDepth()) as $depth) {
$keys[] = $iter->getSubIterator($depth)->key();
}
$result[ join('.', $keys) ] = $leafValue;
}
return $result;
}
// -------------
// Example Tree
// -------------
$tree = [
'id' => 1,
'type' => 'note',
'data' => [],
'children' => [
[
'id' => 2,
'type' => 'wait',
'data' => [
'hr' => 10,
],
'children' => [
[
'id' => 3,
'type' => 'wait',
'data' => [
'hr' => 10,
],
'children' => [
'id' => 4,
'type' => 'exit',
'data' => [],
'children' => []
]
],
[
'id' => 5,
'type' => 'note',
'data' => [
'hr' => 10,
],
'children' => [
[
'id' => 6,
'type' => 'wait',
'data' => [
'hr' => 10,
],
'children' => []
],
[
'id' => 7,
'type' => 'exit',
'data' => [],
'children' => []
],
]
]
],
]
]
];
$result = doit($tree);
print_r($result);
This seems to work, I found it somewhere googling all day.
array_reduce(array_reverse($keys), function($parent_array, $key) {
return $parent_array ? [$key => $parent_array] : [$key];
}, null);

Categories