mongodb sorting group result - mongodb

I am trying to get the first of each group, then I need to sort the resultset. I have achieved the first part but unable to sort the result. Here is what I have tried
Sample Data
{
"_id" : ObjectId("57f549e1831529409b000001"),"name" : "book1","author" : "abc","revision" : 1.0,
"published_on" : ISODate("2016-10-05T18:43:45.902Z"),"publisher" : "newpublisher"
},
{
"_id" : ObjectId("57f54a4483152940ad000001"),"name" : "book1","author" : "a1","revision" : 1.1,
"published_on" : ISODate("2016-10-05T18:45:24.436Z"), "publisher" : "newpublisher"
},
{
"_id" : ObjectId("57f54baa83152940c3000001"), "name" : "oldbook", "author" : "alice","revision" : 1.0,
"published_on" : ISODate("2016-10-05T18:51:22.484Z"),"publisher" : "newpublisher"
},
{
"_id" : ObjectId("57f54c4983152940c3000002"),"name" : "artoflearning","author" : "mike","revision" : 1.0,
"published_on" : ISODate("2016-10-05T18:54:01.585Z"),"publisher" : "oldpublisher"
},
{
"_id" : ObjectId("57f54c5883152940c3000003"),"name" : "artoflearning","author" : "mike","revision" : 1.1,
"published_on" : ISODate("2016-10-05T18:54:16.568Z"),"publisher" : "oldpublisher"
},
{ "_id" : ObjectId("57f54c6583152940c3000004"),"name" : "artoflearning","author" : "mike","revision" : 1.2,
"published_on" : ISODate("2016-10-05T18:54:29.848Z"),"publisher" : "oldpublisher"
},
{ "_id" : ObjectId("57f5513f8315294116000000"),"name" : "learning","author" : "mike","revision" : 2.0,
"published_on" : ISODate("2016-10-05T19:15:11.342Z"),"publisher" : "newpublisher"
}
I am getting latest revision (sort by revision in desc) group by name for a given publisher. I have used this query to achieve
Book.collection.aggregate([
{'$match' => {"publisher"=>"newpublisher"}},
{'$sort' => {'revision' => -1}},
{'$group' => {'_id' => '$name',
'revision' => {'$first' => '$revision'},
'id' => {'$first' => '$_id'},
'name' => {'$first' => '$name'},
'published_on' => {'$first' => '$published_on'},
'publisher' => {'$first' => '$publisher'}
}
},
{'$project' => {'_id' => '$id',
'revision' => '$revision',
'name' => '$name',
'published_on' => '$published_on',
'publisher' => '$publisher'
}
},
{'$skip' => 1},
{ '$limit' => 10 }
])
Since I need paginated result, I have applied skip and limit.
I have got this result
{"_id"=>{"$oid"=>"57f54a4483152940ad000001"}, "revision"=>1.1, "name"=>"book1", "published_on"=>"2016-10-05T18:45:24.436Z", "publisher"=>"newpublisher"},
{"_id"=>{"$oid"=>"57f5513f8315294116000000"}, "revision"=>2.0, "name"=>"learning", "published_on"=>"2016-10-05T19:15:11.342Z", "publisher"=>"newpublisher"}
Now I would like to sort by 'name' or 'published_on' columns. When I try to apply that on the result from above, I am getting error
Please advise

You didn't said what error you're getting, that would help. But you said
Now I would like to sort by 'name' or 'published_on' columns. When I try to apply that on the result from above, I am getting error
and by that I understand you added the sort after the limit. If you did that, it only sorts the skipped and limited results, in order to sort all results you must call the sort before skipping and limiting them, like:
Book.collection.aggregate([
{'$match' => {"publisher"=>"newpublisher"}},
{'$sort' => {'revision' => -1}},
{'$group' => {'_id' => '$name',
'revision' => {'$first' => '$revision'},
'id' => {'$first' => '$_id'},
'name' => {'$first' => '$name'},
'published_on' => {'$first' => '$published_on'},
'publisher' => {'$first' => '$publisher'}
}
},
{'$project' => {'_id' => '$id',
'revision' => '$revision',
'name' => '$name',
'published_on' => '$published_on',
'publisher' => '$publisher'
}
},
{'$sort' => {'name' => 1}},
{'$skip' => 1},
{'$limit' => 10 }
])
I added the documents you posted in a collection and ran this query and this is the result
[ { _id: 57f5513f8315294116000000,
revision: 2,
name: 'learning',
published_on: Wed Oct 05 2016 16:15:11 GMT-0300 (BRT),
publisher: 'newpublisher' },
{ _id: 57f54baa83152940c3000001,
revision: 1,
name: 'oldbook',
published_on: Wed Oct 05 2016 15:51:22 GMT-0300 (BRT),
publisher: 'newpublisher' } ]
And sorted by published_on:
{'$sort': {'published_on': 1}}
[ { _id: 57f54baa83152940c3000001,
revision: 1,
name: 'oldbook',
published_on: Wed Oct 05 2016 15:51:22 GMT-0300 (BRT),
publisher: 'newpublisher' },
{ _id: 57f5513f8315294116000000,
revision: 2,
name: 'learning',
published_on: Wed Oct 05 2016 16:15:11 GMT-0300 (BRT),
publisher: 'newpublisher' } ]

Related

mongodb aggregation match date

I have data like
{
"_id" : ObjectId("5b63e593f033ab66fa25a142"),
"percent_change_1h" : 0.37,
"percent_change_24h" : -3.91,
"percent_change_7d" : -7.08,
"last_updated" : 1533273443,
"created_at" : "2018-08-03 05:18:11",
"updated_at" : "2018-08-03 05:18:11"
}
its have created_at date like this "2018-08-03 05:18:11"
but i can pass date in aggregation function like "2018-08-03"
data not get in this match
I can not use $gte or $lte becouse get only given date data
query is here
$date = "2018-08-15";
$filters = ['$match'=>[
'quotes'=>$quotes,
'created_at' => $date]
];
$join = ['$lookup'=>[
'from' => "cryptocurrencies_list",
'localField'=> "crypto_list_id",
'foreignField'=> "_id",
'as'=>"listdata"]
];
$limits = ['$limit'=>10];
$query = $detailscollection->aggregate([$filters,$join,$limits]);
You can try below aggregation
$detailscollection->aggregate([
[
'$addFields' => [
'date' => [
'$dateToString' => [
'date' => [
'$dateFromString' => [
'dateString' => '$created_at',
'timezone' => 'America/New_York'
]
],
'format' => '%Y-%m-%d'
]
]
]
],
[
'$match' => [
'date' => $date, 'quotes' => $quotes
]
]
])

Symfony how to mongo-odm-aggregation-bundle

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.

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.

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 associative array - pull possible?

Having the following Array:
array(
'id' => 12,
'keys' => array('x1' => array('idx' => 12, 'text'=> '1123145'),
'x2' => array('idx' => 14, 'text'=> '1123142'),
'x3' => array('idx' => 12, 'text'=> '1123145'),
'x4' => array('idx' => 14, 'text'=> '1123145')
)
)
I want to pull all keys with idx 12. So i do the following:
$mdb->db->collection->update(array('id' => 12), array('$pull' => array('keys' => array('idx' => 12))));
But it don't works, whats the problem?
This is impossible to do with this schema.
you are trying to pull the id = 12 from the array key, but the problem, is that each element of key is an object by itself.
the only way to do what you want with minimum modification is change schema in this way:
{
"_id" : 12,
"keys" : [
{
"type" : 'x1',
"idx" : 12,
"text" : "1111"
},
{
"type" : 'x2',
"idx" : 14,
"text" : "1111"
},
{
"type" : 'x3',
"idx" : 12,
"text" : "1111"
},
{
"type" : 'x4',
"idx" : 14,
"text" : "1111"
}
}]
}
than you can run your query as follows:
db.XXX.update(
{ "_id" : 12},
{
'$pull' : {
'keys' : {
'idx' : 12
}
}
}
);
I hope you will be able to transfer this into php with an arrays, because it looks for me that you are using php for querying