Symfony how to mongo-odm-aggregation-bundle - mongodb

I am confused to ask this question but I can not find a solution to my problem.
I use the mongo-odm-agregation-bundle to perform an aggregate on my data.
I don't know how to use correctly this bundle, the documentation is not sufficiently explicit and the result is not that i would expect.
So, in mongoDB my code is for the aggregate :
id: { Epreuve:"$EPREUVE", month: { $month: "$DATE" },
day: { $dayOfMonth: "$DATE" }, year: { $year: "$DATE" }},
total: { $sum: "$SCORE" },
nbmots: {$sum: "$NBMOTS"},
moymots: {$avg : "$NBMOTS"},
moytemps:{$avg: "$CHRONOS"},
position: { $sum: 1 },
And the result is :
{
"_id" : {
"Epreuve" : "Verbe",
"month" : NumberInt(2),
"day" : NumberInt(21),
"year" : NumberInt(2017)
},
"total" : NumberLong(430),
"nbmots" : NumberLong(16),
"moymots" : 16.0,
"moytemps" : 147.24,
"position" : 1.0
}
In Symfony, i use this sample to test :
$expr = new \Solution\MongoAggregation\Pipeline\Operators\Expr;
$aq = $this->get('doctrine_mongodb.odm.default_aggregation_query')->getCollection('PortailBundle:DataScoreMotsMeles')->createAggregateQuery();
$result = $aq->match(['SESSION'=>$currentSession])
->group(['_id' => [ 'Epreuve' => "EPREUVE", ],
'Score' => $expr->sum("SCORE"),
'nbMots'=> $expr->sum("NBMOTS"),
'moyMots'=> $expr->avg("NBMOTS"),
'moyTemps'=> $expr->avg("CHRONOS"),
'count' => $expr->sum(1)])
->sort(['count' => -1])
->limit(10)
->getQuery()
->aggregate()
->toArray();
The result is :
Array ( [0] => Array ( [_id] => Array ( [Epreuve] => **EPREUVE** ) [Score] => **0** [nbMots] => **0** [moyMots] => [moyTemps] => [count] => 3 ) )
The problem is the result is 0 each time.
It is normal because i use :
$expr->sum("NBMOTS")
instead of :
$expr->sum('$NBMOTS')
But if i use '$NBMOTS' it doesn't works. So how i do ? I need your help.

Related

Project with Match in aggregate not working after use substr in mongodb

I have face one use with mongodb.
below is my sample record.
{
"_id" : ObjectId("56fa21da0be9b4e3328b4567"),
"us_u_id" : "1459169911J4gPxpYQ7A",
"us_dealer_u_id" : "1459169911J4gPxpYQ7A",
"us_corporate_dealer_u_id" : "1459169173rgSdxVeMLa",
"us_oem_u_id" : "1459169848CK5yOpXito",
"us_part_number" : "E200026",
"us_sup_part_number" : "",
"us_alter_part_number" : "",
"us_qty" : 0,
"us_sale_qty" : 2,
"us_date" : "20160326",
"us_source_name" : "BOMAG",
"us_source_address" : "",
"us_source_city" : "",
"us_source_state" : "",
"us_zip_code" : "",
"us_alternet_source_code" : "",
"updated_at" : ISODate("2016-03-29T06:34:02.728Z"),
"created_at" : ISODate("2016-03-29T06:34:02.728Z")
}
I have try to get all recored having unique date
So, I have made below query using aggregate
.aggregate(
[
{
"$match":{
"yearSubstring":"2016",
"monthSubstring":"03",
"us_dealer_u_id":"1459169911J4gPxpYQ7A"
}
},
{
"$project":
{
"yearSubstring":{"$substr":["$us_date",0,4]},
"monthSubstring":{"$substr":["$us_date",4,2]},
"daySubstring":{"$substr":["$us_date",6,2]}
}
},
{
"$group":
{
"_id":{"monthSubstring":"$monthSubstring",
"yearSubstring":"$yearSubstring",
"daySubstring":"$daySubstring"
},
"daySubstring":{"$last":"$daySubstring"}
}
},
{"$sort":{"us_date":1}}
]
)
I have try both way to pass year and month (as string and as int)
but I have get blank result.
if I'm remove month and year from condition then record came.
mostly I have try all the diff. diff. solution but result is same.
Thank in advance.
You have written incorrect query.
You don't have yearSubstring and monthSubstring fields on this stage.
{
"$match":{
"yearSubstring":"2016",
"monthSubstring":"03",
"us_dealer_u_id":"1459169911J4gPxpYQ7A"
}
},
You should write as following:
.aggregate(
[
{
"$match":{
"us_dealer_u_id":"1459169911J4gPxpYQ7A"
}
},
{
"$project":
{
"yearSubstring":{"$substr":["$us_date",0,4]},
"monthSubstring":{"$substr":["$us_date",4,2]},
"daySubstring":{"$substr":["$us_date",6,2]}
}
},
{
"$match":{
"yearSubstring":"2016",
"monthSubstring":"03"
}
},
{
"$group":
{
"_id":{"monthSubstring":"$monthSubstring",
"yearSubstring":"$yearSubstring",
"daySubstring":"$daySubstring"
},
"daySubstring":{"$last":"$daySubstring"}
}
},
{"$sort":{"us_date":1}}
]
)
If you want to get other fields, you should include them into projection stage.

How to apply correctly $limit and $skip in subfields?

I'm starting with mongodb and I'm finding many difficulties with the following scheme.
{
"_id" : "AAA",
"events" : [
{
"event" : "001",
"time" : 1456823333
},
{
"event" : "002",
"time" : 1456828888
},
{
"event" : "003",
"time" : 1456825555
},...
]
}
I want to get the events sorted by date and apply limit and skip.
I'm using the following query:
$op = array(
array('$match' => array('_id' => $userId)),
array('$unwind' => '$events'),
array('$sort' => array('events.time' => -1)),
array('$group' => array('_id' => '$_id',
'events' => array('$push' => '$events')))
//,array('$project' => array('_id' => 1, 'events' => array('$events', 0, 3)))
//,array('$limit' => 4)
//,array('$skip' => 3)
);
$result= Mongo->aggregate('mycollection', $op);
I have tried everything to filter $project or $limit and $skip but none of it works.
How should I apply the limit and skyp conditions in events?
If I do not apply the conditions of "limit" above the result is ordered correctly.
Result:
{ "waitedMS":0,
"result":[
{
"_id":"AAA",
"events":[
{
"event":"002",
"time":1456828888,
},
{
"event":"003",
"time":1456825555,
},
{
"event":"001",
"time":1456823333,
},...
}
],
"ok":1
}
Order correctly but I can not limit the number of results for paging.

how to increment the conuter in project block in mongoid using $cond keyword in rails

Am new to MongoID
i need to increment the counter in project using $cond keyword
lists = TableName.collection.aggregate({"$match" => {"starttime"=> { "$gte" => u},"_mac_id" => {"$in" => mac_ids.uniq} }}, {"$project"=>{"stayedtime"=>1, "rl_mac_id"=>1, "vd_mac_id"=>1, "con"=> {"$cond"=> [ { "$gte"=> [ "$stayedtime", 300 ] }, "then"=> { "$sum" => 1 } , "else"=> { "$sum" => 1 } ] }}},{"$group"=>{"_id"=>"$rl_mac_id","c"=>"$con", "visits"=> { "$sum" => 1 }, "tust"=>{"$sum"=>"$stayedtime"}, "visitors"=> { "$addToSet" => "$vd_mac_id"}}})
but its not working.. its giving error. I know there is no "$sum" keyword in project. But instead of that is there any thing.
Advance thanks..

MongoDB Aggregation Framework

I have a document that's structured as follows:
{
'_id' => 'Star Wars',
'count' => 1234,
'spelling' => [ ( 'Star wars' => 10, 'Star Wars' => 15, 'sTaR WaRs' => 5) ]
}
I would like to get the top N documents (by descending count), but with only one one spelling per document (the one with the highest value). It there a way to do this with the aggregation framework?
I can easily get the top 10 results (using $sort and $limit). But how do I get only one spelling per each?
So for example, if I have the following three records:
{
'_id' => 'star_wars',
'count' => 1234,
'spelling' => [ ( 'Star wars' => 10, 'Star Wars' => 15, 'sTaR WaRs' => 5) ]
}
{
'_id' => 'willow',
'count' => 2211,
'spelling' => [ ( 'willow' => 300, 'Willow' => 550) ]
}
{
'_id' => 'indiana_jones',
'count' => 12,
'spelling' => [ ( 'indiana Jones' => 10, 'Indiana Jones' => 25, 'indiana jones' => 5) ]
}
And I ask for the top 2 results, I'll get:
{
'_id' => 'willow',
'count' => 2211,
'spelling' => 'Willow'
}
{
'_id' => 'star_wars',
'count' => 1234,
'spelling' => 'Star Wars'
}
(or something to this effect)
Thanks!
Your schema as designed would make using anything but a MapReduce difficult as you've used the keys of the object as values. So, I adjusted your schema to better match with MongoDB's capabilities (in JSON format as well for this example):
{
'_id' : 'star_wars',
'count' : 1234,
'spellings' : [
{ spelling: 'Star wars', total: 10},
{ spelling: 'Star Wars', total : 15},
{ spelling: 'sTaR WaRs', total : 5} ]
}
Note that it's now an array of objects with a specific key name, spelling, and a value for the total (I didn't know what that number actually represented, so I've called it total in my examples).
On to the aggregation:
db.so.aggregate([
{ $unwind: '$spellings' },
{ $project: {
'spelling' : '$spellings.spelling',
'total': '$spellings.total',
'count': '$count'
}
},
{ $sort : { total : -1 } },
{ $group : { _id : '$_id',
count: { $first: '$count' },
largest : { $first : '$total' },
spelling : { $first: '$spelling' }
}
}
])
Unwind all of the data so the aggregation pipeline can access the various values of the array
Flatten the data to include the key aspects needed by the pipeline. In this case, the specific spelling, the total, and the count.
Sort on the total, so that the last grouping can use $first
Then, group so that only the $first value for each _id is returned, and then also return the count which because of the way it was flattened for the pipeline, each temporary document will contain the count field.
Results:
[
{
"_id" : "star_wars",
"count" : 1234,
"largest" : 15,
"spelling" : "Star Wars"
},
{
"_id" : "indiana_jones",
"count" : 12,
"largest" : 25,
"spelling" : "Indiana Jones"
},
{
"_id" : "willow",
"count" : 2211,
"largest" : 550,
"spelling" : "Willow"
}
]

MongoDb aggregation or mapreduce for invoicing statistics?

I'm new to MongoDb and have a job for (I suppose) MapReduce or Aggregation.
I have an "invoices" collection with documents in this format:
{
date: 'some unix timestamp',
total: 12345,
paid: true
}
I need to display a table with months (jan-dec) as columns, a row for each year and the sum of total in the month (divided in paid and unpaid) in the cell. Like this:
| Jan | Feb | ...
2013 | 1,222 / 200 | 175 / 2,122 | ...
...
Can you help me get the mongo command right?
Maybe I'm better off writing some JS code to execute in mongo?
I've now found a solution using MapReduce. Here it is in use from PHP:
$map = new MongoCode('
function() {
var d = new Date(this.date*1000);
emit({y: d.getFullYear(), m: d.getMonth()}, {
total: this.total,
notPaid: this.paid ? 0 : this.total,
count: 1
});
};
');
$reduce = new MongoCode('
function(month, values) {
result = { total: 0, notPaid: 0, count: 0 };
for (var i = 0; i < values.length; i++) {
result.total += values[i].total;
result.notPaid += values[i].notPaid;
result.count += values[i].count;
}
return result;
};
');
$result = $db->command(array(
'mapreduce' => 'invoices',
'map' => $map,
'reduce' => $reduce,
'out' => 'temp'
));
echo $result['timeMillis'];
Now the results are in the "temp" collection, one document per month. Could it be optimized or enhanced?
You can do this with aggregation framework like this:
db.invoices.aggregate( [
{
"$project" : {
"yr" : {
"$year" : "$date"
},
"mo" : {
"$month" : "$date"
},
"total" : 1,
"unpaid" : {
"$cond" : [
"$paid",
0,
"$total"
]
}
}
},
{
"$group" : {
"_id" : {
"y" : "$yr",
"m" : "$mo"
},
"total" : {
"$sum" : "$total"
},
"unpaid" : {
"$sum" : "$unpaid"
}
}
}
] )
You can use another $project at the end to pretty-up the output, and a $sort to order it, but that's the basic functioning core of it.