Join 2 laravel collections - php

I have 2 collections :
$collection1 = [
{ name : "aaa" , score : 10 },
{ name : "bbb" , score : 20 }
];
$collection2 = [
{ name : "aaa" , score : 30 },
{ name : "bbb" , score : 10 }
];
and I want to join them to get the following result :
$collection3 = [
{ name : "aaa" , score : 40 },
{ name : "bbb" , score : 30 }
]
merge() and union() didn't do the job for me .

Combine the collections, group by the name and sum the score. This can be done like so with collections in Laravel. Note that group by returns a collection of the grouped data.
$collection1->concat($collection2);
$collection1->groupBy('name')->map(function ($scores) {
$score = $scores->first();
$score['score'] = $scores->sum('score');
return $score;
});

it can be achieve by laravel collection map() and where() function
$collection1 = collect([
[
"name" => "aaa",
"score" => 10
],
[
"name" => "bbb",
"score" => 20
]
]);
$collection2 = collect([
[
"name" => "aaa",
"score" => 30
],
[
"name" => "bbb",
"score" => 10
]
]);
$collection3 = $collection1->map(function ($item) use ($collection2) {
$found = $collection2->where('name', $item['name']);
if ($found) {
$item['score'] = $item['score'] + $found->first()['score'];
}
return $item;
});
dd($collection3);
This is the result https://phpsandbox.io/n/soft-flower-ap1e-vo2xy

$collection = collect();$collection->merge(['col1' => $col1, 'col2' => $col2 ]);
OR $states = [ 'Delhi', 'Tamil Nadu', 'Uttar Pradesh'];
foreach ($states as $state)
{ $data = DB::table('user')->select('user.*') ->where("user.city", $state)->where('user.status', 1)->get(); if (!empty($data[0])) $stateUser = $stateUser->merge([$state => $data]);}

Related

Sum all specified fields in a collection

I am using PHP to access a MongoDB collection in which I have recorded purchases of my shop.
Example document:
{"_id":{"$oid":"61ff011a5b55479726fe5208"},"monthYear":"2-2022","timestamp":{"$date":"2022-02-05T22:58:34.952Z"},"productIndex":"x","productPriceUSD":10,"customerName":"x","customerID":"x","notes":"x","__v":0}
Now I want to add the value of field "productPriceUSD" from all documents in the collection and get a sum at the end.
Maybe someone can help me?
That's my start:
I know, it probably does not make any sense.
$client = new MongoDB\Client(
'xxx'
);
$sumtotal = function() use ($client)
{
$collection = $client->database->purchases;
$options = [];
$filter = [
['$group' => [
'_id' => '$_id',
'productPriceUSD' => [
'$sum' => [
'$cond' => [ [ '$gt' => [ '$productPriceUSD', 0 ] ], '$productPriceUSD', 0 ],
],
]],
]];
$result = $collection->aggregate($filter, $options);
};
Here two pipelines you need
{
$group: {
_id: "",
total: { $sum: "$productPriceUSD" }
}
},
{
$project: {
_id: 0,
total: "$total"
}
}
Playground link: https://mongoplayground.net/p/8OTGkK7wLIp

PHP MongoDB $set using arrayFilters

I'm trying to update the value of "position" in "attributes" using the $ set operator with arrayFilters.
The command below works as expected on the terminal
db.produtos.insert(
{
"_id": 1,
"nome":"Nome do Produto",
"descricao":"Lorem ipsum dolor sit amet.",
"atributos": [
{ "id" : 1, "posicao" : 2 },
{ "id" : 2, "posicao" : 1 },
{ "id" : 3, "posicao" : 3 }
]
}
)
db.produtos.update(
{"_id": 1 },
{
$set: {
"atributos.$[elem1].posicao": 1,
"atributos.$[elem2].posicao": 2,
"atributos.$[elem3].posicao": 3
}
},
{
arrayFilters: [
{ "elem1.id": 1 },
{ "elem2.id": 2 },
{ "elem3.id": 3 },
]
}
)
But when I try to create the same command in PHP it does not work.
In my tests, the document is found but never updated.
Just to explain, I get the "attributes.id" of a string, I mount an array with the update and another to filter
<?php
$ids = explode(',', '1,2,3');
$update = [];
$filters = [];
$i = 1;
foreach ($ids as $linha) :
$update['atributos.$[elem'.$i.'].posicao'] = $i;
$filters[]['elem'.$i.'.id'] = $linha;
$i++;
endforeach;
$conexao = new MongoDB\Driver\Manager('mongodb://localhost:27017');
$cmd = new MongoDB\Driver\Command(
[
'findAndModify' => 'produtos',
'query' => ['_id' => 1],
'update' => ['$set' => $update],
'upsert' => true,
'returnDocument' => true,
'new' => true,
'arrayFilters' => $filters,
]
);
$result = $conexao->executeCommand('teste', $cmd)->toArray();
$result = json_decode(json_encode($result), true);
echo '<pre>';
print_r($cmd);
print_r($result);

Shanty Mongo aggregate $multiply

How to get percentages between desktop and mobile from a group pipeline in a MongoDB aggregate.
Sample data:
{
_id : 1,
full_url : 'www.example.com/blog/about',
access_time : ISODate("2015-12-21T14:50:19Z")
},
{
_id : 2,
full_url : 'www.example.com/blog/statistic',
access_time : ISODate("2015-12-21T14:50:22Z")
},
{
_id : 3,
full_url : 'm.example.com/detail/article1',
access_time : ISODate("2015-12-21T14:50:41Z")
}
Result should look like this:
{
_id : 'www.example.com',
percentage : 28
},
{
_id : 'm.example.com',
percentage : 72
}
Shanty Mongo
class App_Model_Mongodb_RequestLog extends Shanty_Mongo_Document
{
protected static $_db = 'sam';
protected static $_collection = 'requestlog';
public static function device()
{
$total = self::all()->count();
$pipeline = [
[
'$match' => [
'access_time' => [
'$gte' => new \MongoDate( strtotime('-1 minute') ),
'$lte' => new \MongoDate(),
],
'$and' => [
['full_url' => new \MongoRegex("/www.example.com/i")],
['full_url' => new \MongoRegex("/m.example.com/i")],
]
]
],
[
'$group' => [
'_id' => '$full_url',
'count' => ['$sum' => 1]
]
],
[
'$project' => [
'percentage' => [
'$multiply' => [
'$count', 100 / $total
]
]
]
],
[
'$sort' => ['percentage' => -1]
]
];
return self::getMongoCollection()->aggregate($pipeline);
}
}
But I've no idea how to do that. I've tried $divide and other things, but without success. any suggestions?

mongodb avg in aggregation of array element

I have the following collection structure
{
"_id": {
"d_timestamp": NumberLong(1429949699),
"d_isostamp": ISODate("2015-04-25T08:14:59.0Z")
},
"XBT-USD-cpx-okc": [
{
"buySpread": -1.80081
}
I run the following aggregation
$spreadName ='XBT-USD-stp-nex';
$pipe = array(
array(
'$match' => array(
'_id.d_isostamp' => array(
'$gt' => $start, '$lt' => $end
)
)
),
array(
'$project' => array(
'sellSpread' =>'$'.$spreadName.'.sellSpread',
)
),
array(
'$group' => array(
'_id' => array(
'isodate' => array(
'$minute' => '$_id.d_isostamp'
)
),
'rsell_spread' => array(
'$avg' => '$sellSpread'
),
)
),
);
$out = $collection->aggregate($pipe ,$options);
and I get as a result the value 0 for rsell_spread whereas if I run a $max for instance instead of an $avg in the $group , I get an accurate value for rsell_spread , w/ the following structure
{
"_id": {
"isodate": ISODate("2015-04-25T08:00:58.0Z")
},
"rsell_spread": [
-4.49996▼
]
}
So I have two questions :
1/ How come does the $avg function does not work?
2/ How can I can a result not in an array when I use $max for example (just a regular number)?
The $avg group accumulator operator does work, it's only that in your case it is being applied to an element in an array and thus gives the "incorrect" result.
When you use the $max group accumulator operator, it returns the the highest value that results from applying an expression to each document in a group of documents, thus in your example it returned the maximum array.
To demonstrate this, consider adding a few sample documents to a test collection in mongoshell:
db.test.insert([
{
"_id" : {
"d_timestamp" : NumberLong(1429949699),
"d_isostamp" : ISODate("2015-04-25T08:14:59.000Z")
},
"XBT-USD-stp-nex" : [
{
"sellSpread" : -1.80081
}
]
},
{
"_id" : {
"d_timestamp" : NumberLong(1429949710),
"d_isostamp" : ISODate("2015-04-25T08:15:10.000Z")
},
"XBT-USD-stp-nex" : [
{
"sellSpread" : -1.80079
}
]
},
{
"_id" : {
"d_timestamp" : NumberLong(1429949720),
"d_isostamp" : ISODate("2015-04-25T08:15:20.000Z")
},
"XBT-USD-stp-nex" : [
{
"sellSpread" : -1.80083
}
]
},
{
"_id" : {
"d_timestamp" : NumberLong(1429949730),
"d_isostamp" : ISODate("2015-04-25T08:15:30.000Z")
},
"XBT-USD-stp-nex" : [
{
"sellSpread" : -1.80087
}
]
}
])
Now, replicating the same operation above in mongoshell:
var spreadName = "XBT-USD-stp-nex",
start = new Date(2015, 3, 25),
end = new Date(2015, 3, 26);
db.test.aggregate([
{
"$match": {
"_id.d_isostamp": { "$gte": start, "$lte": end }
}
},
{
"$project": {
"sellSpread": "$"+spreadName+".sellSpread"
}
}/*,<--- deliberately omitted the $unwind stage from the pipeline to replicate the current pipeline
{
"$unwind": "$sellSpread"
}*/,
{
"$group": {
"_id": {
"isodate": { "$minute": "$_id.d_isostamp"}
},
"rsell_spread": {
"$avg": "$sellSpread"
}
}
}
])
Output:
/* 0 */
{
"result" : [
{
"_id" : {
"isodate" : 15
},
"rsell_spread" : 0
},
{
"_id" : {
"isodate" : 14
},
"rsell_spread" : 0
}
],
"ok" : 1
}
The solution is to include an $unwind operator pipeline stage after the $project step, this will deconstruct the XBT-USD-stp-nex array field from the input documents and outputs a document for each element. Each output document replaces the array with an element value. This will then make it possible for the $avg group accumulator operator to work.
Including this will give the aggregation result:
/* 0 */
{
"result" : [
{
"_id" : {
"isodate" : 15
},
"rsell_spread" : -1.80083
},
{
"_id" : {
"isodate" : 14
},
"rsell_spread" : -1.80081
}
],
"ok" : 1
}
So your final working aggregation in PHP should be:
$spreadName ='XBT-USD-stp-nex';
$pipe = array(
array(
'$match' => array(
'_id.d_isostamp' => array(
'$gt' => $start, '$lt' => $end
)
)
),
array(
'$project' => array(
'sellSpread' =>'$'.$spreadName.'.sellSpread',
)
),
array('$unwind' => '$sellSpread'),
array(
'$group' => array(
'_id' => array(
'isodate' => array(
'$minute' => '$_id.d_isostamp'
)
),
'rsell_spread' => array(
'$avg' => '$sellSpread'
),
)
),
);
$out = $collection->aggregate($pipe ,$options);

MongoDB aggregate query using PHP driver

I have a working MongoDB aggregate query which I can run via the MongoDB shell. However, I am trying to convert it to work with the official PHP Mongo driver (http://php.net/manual/en/mongocollection.aggregate.php).
Here is the working raw MongoDB query:
db.executions.aggregate( [
{ $project : { day : { $dayOfYear : "$executed" } } },
{ $group : { _id : { day : "$day" }, n : { $sum : 1 } } } ,
{ $sort : { _id : -1 } } ,
{ $limit : 14 }
] )
Here is my attempt (not working) in PHP using the Mongo driver:
$result = $c->aggregate(array(
'$project' => array(
'day' => array('$dayOfYear' => '$executed')
),
'$group' => array(
'_id' => array('day' => '$day'),
'n' => array('$sum' => 1)
),
'$sort' => array(
'_id' => 1
),
'$limit' => 14
));
The error from the above PHP code is:
{"errmsg":"exception: wrong type for field (pipeline) 3 != 4","code":13111,"ok":0}
Any ideas? Thanks.
The parameter in your Javascript is an array of 4 objects with one element each, in your PHP it's an associative array (object) with 4 elements. This would represent your Javascript:
$result = $c->aggregate(array(
array(
'$project' => array(
'day' => array('$dayOfYear' => '$executed')
),
),
array(
'$group' => array(
'_id' => array('day' => '$day'),
'n' => array('$sum' => 1)
),
),
array(
'$sort' => array(
'_id' => 1
),
),
array(
'$limit' => 14
)
));
In addition, if you have at least PHP5.4, you can use simpler array syntax. Transformation to PHP is then trivial, you simply replace curly braces with square brackets and colons with arrows:
$result = $c->aggregate([
[ '$project' => [ 'day' => ['$dayOfYear' => '$executed'] ] ],
[ '$group' => ['_id' => ['day' => '$day'], 'n' => ['$sum' => 1] ] ],
[ '$sort' => ['_id' => 1] ],
[ '$limit' => 14 ]
]);
You can also use the JSON array directly:
$json = [
'{ $project : { day : { $dayOfYear : "$executed" } } },
{ $group : { _id : { day : "$day" }, n : { $sum : 1 } } } ,
{ $sort : { _id : -1 } } ,
{ $limit : 14 }'
];
$pipeline = [];
foreach ($json as $stage) {
array_push($pipeline, toPHP(fromJSON($stage)));
}
$result = $c->aggregate($pipeline);
You can also use combination like this:
use function MongoDB\BSON\toRelaxedExtendedJSON;
use function MongoDB\BSON\fromPHP;
use function MongoDB\BSON\toPHP;
use function MongoDB\BSON\fromJSON;
$operator = '$dayOfYear';
$json = [];
array_push($json, toRelaxedExtendedJSON(fromPHP(
array('$project ' => array('day' => array($operator => '$executed')))
)));
array_push($json,
'{ $group : { _id : { day : "$day" }, n : { $sum : 1 } } } ,
{ $sort : { _id : -1 } } ,
{ $limit : 14 }'
);
$pipeline = [];
foreach ($json as $stage) {
array_push($pipeline, toPHP(fromJSON($stage)));
}
$result = $c->aggregate($pipeline);
or
$operator = '$dayOfYear';
$json = [];
array_push($json,
'{ $group : { _id : { day : "$day" }, n : { $sum : 1 } } } ,
{ $sort : { _id : -1 } } ,
{ $limit : 14 }'
);
$pipeline = [];
array_push($pipeline, array('$project ' => array('day' => array($operator => '$executed'))));
foreach ($json as $stage) {
array_push($pipeline, toPHP(fromJSON($stage)));
}
$result = $c->aggregate($pipeline);
If your aggregation query is dynamic or based on user-input be aware of NoSQL-Injection!
https://www.acunetix.com/blog/web-security-zone/nosql-injections/

Categories