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?
Related
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
My collection:
($usersWithCommission) Illuminate\Support\Collection {#2625
#items: array:2 [
0 => array:3 [
"userId" => 1
"name" => "Sim Aufderhar"
"net_commission" => null
]
1 => array:3 [
"userId" => 2
"name" => "Carolyn Lang III"
"net_commission" => null
]
]
}
I would like modify the net_commission property, but I can not:
foreach ($soldProperties as $property) {
if (!$property->buyer_user_id && !$property->seller_transferring_user_id) {
$usersWithCommission->where('userId', $property->user_id)->first()['net_commission'] += $property->net_commission_of_sold;
}
}
How I can?
Thanks your answers.
Collections provide a method map which allows you to iterate your collection and add/modify fields.
function modify_net_commision($var) {
return YOUR_LOGIC_HERE;
}
$collection = [
[ "userId" => 1, "name" => "Sim Aufderhar", "net_commission" => null ],
[ "userId" => 2, "name" => "Carolyn Lang III", "net_commission" => null ],
];
$external_var = 'I will be used on modify_net_commision function';
$new_collection = collect($collection)->map(function ($arr) use ($external_var) {
$arr['net_commission'] = modify_net_commision($external_var);
return $arr;
})
If you want to remove some fields from your collection, use reject method.
Doc: https://laravel.com/docs/5.8/collections
Hope it helps you.
Have a great day.
public function test()
{
$collection = [
[ "userId" => 1, "name" => "Sim Aufderhar", "net_commission" => null ],
[ "userId" => 2, "name" => "Carolyn Lang III", "net_commission" => null ],
];
$data= collect($collection)->map(function($collection, $key) {
$collect = (object)$collection;
return [
'userId' => $collect->userId,
'name' => $collect->name,
'net_commission' => $this->modify_commision($key)
];
});
dd($data);
}
public function modify_commision($key) {
$property = [
['userId' => 1 ,'net_commission_of_sold' => 30],
['user_id' => 2,'net_commission_of_sold' => 40]
];
return $property[$key]['net_commission_of_sold'];
}
***Hope it helps you***
I want to execute below mongodb script using php:
db.getCollection('play_history').aggregate(
[ { $match : { At : {$gte : 1470307490, $lt : 1470307497649}, moneyType:1 } } ,
{
$group:
{
_id: { gameType: "$gameType", sessionId: "$sessionId"},
}
},
{
$group:
{
_id: "$_id.gameType",
count: { $sum: 1}
}
}
])
Below is the php snip code:
$match = [ 'moneyType' => 1,
'At' => ['$gt' => 12, '$lt' => 1470307497649]];
$command = new MongoDB\Driver\Command([
'aggregate' => 'play_history',
'pipeline' => [
['$match' => $match],
['$group' =>
['_id' => ["gameType" => '$gameType', "sessionId" => '$sessionId']]],
['$group' =>
['_id' => '$_id.gameType' ],
'countGame' => ['$sum' => 1 ]],
],
'cursor' => new stdClass,
]);
$cursor = $manager->executeCommand('falcon', $command);
The above code give me the following error:
A pipeline stage specification object must contain exactly one field
I have tried to change several grouping orders but it didn't work. Is that a limitation of PHP to just has only one group stage per command?
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);
I am using the following MongoDB query (and it's working with RoboMongo), returning an array with 30 elements, one for each day of the date range specified on $match element:
db.alcobaca.aggregate( [
{ $project:
{ time:1,
temp:1,
frio:
{
$cond:
[{ $lte: [ "$temp", 7.2 ] }, 0.25, 0 ]}}},
{
$match: {
time: {
$gte: new Date('11/01/2011'),
$lt: new Date('11/30/2011')
}
}
},
{
$group: {
_id: {
ord_date: {
day: {$dayOfMonth: "$time"},
month: {$month: "$time"},
year: {$year: "$time"}
}
},
horasFrio: { $sum: '$frio' }
}
},
{ $sort: { '_id.ord_date': 1} }
])
When I translate to PHP, working on the Laravel framework, after a successful connection to the database (I have already tested it with another, simpler query):
$c->aggregate (
[
['$project' =>
['time' => 1,
'temp' => 1,
'frio' =>
['$cond' =>
[['$lte' => ['$temp', 7.2]], 0.25, 0]]]],
['$match' => [
'time' => [
'$gte' => new DateTime('11/01/2011'),
'$lt' => new DateTime('11/03/2011')
]
]
],
[
'$group' => [
'_id' => [
'ord_date' => [
'day' => ['$dayOfMonth' => '$time'],
'month' => ['$month' => '$time'],
'year' => ['$year' => '$time']
]
],
'horasFrio' => ['$sum' => '$frio']
]
],
['$sort' => ['_id.ord_date' => 1]]
]
);
It returns an empty array. Am I missing some syntax issue?
Thank you