My collection contains documents of the following form:
{
'game_name': 'football',
'scores': [4,1,2,7,6,0,5]
}
How do I find the 'scores' of all such objects in the collection and sort them in ascending order?
If I understand your Question
db.yourcollection.aggregate([
{
$unwind:"$scores"
},{
$sort:{
"scores":1
}
},{
$group:{
_id:null,
scores:{$addToSet:"$scores"}
}
},{
$project:{
_id:0,
scores:1
}
}
])
Result is:
{
"result" : [
{
"scores" : [
7,
5,
4,
6,
2,
1,
0
]
}
],
"ok" : 1
}
Related
db.artists.insertMany([
{ "_id" : 1, "achievements" : {"third_record":true, "second_record": true} },
{ "_id" : 3, "achievements" : {"sixth_record":true, "second_record": true} },
{ "_id" : 2, "achievements" : {"first_record":true, "fifth_record": true} },
{ "_id" : 4, "achievements" : {"first_record":true, "second_record": true} },
])
I would like to count how many first_record, second_record, etc achievements have been obtained, I don't know beforehand the names of the achievements. I just want it to count all the achievements matched in the first stage. How do I use aggregation to count this? I saw another question suggest using unwind but that seems to be for arrays only and not objects?
May be this:
db.collection.aggregate([
{
$project: {
as: {
$objectToArray: "$achievements"
}
}
},
{
$unwind: "$as"
},
{
$group: {
_id: "$as.k",
number: {
$sum: {
"$cond": [
{
$eq: [
"$as.v",
true
]
},
1,
0
]
}
}
}
}
])
Idea
convert object to array
unwind to get them separate
group by id, adding 1 for true, 0 for false.
Suppose my collection consists of items that looks like this:
{
"items" : [
{
"item_id": 1,
"item_field": 10
},
{
"item_id": 2,
"item_field": 15
},
{
"item_id": 3,
"item_field": 3
},
]
}
Can I somehow select the entry of items with the lowest value of item_field, in this case the one with item_id 3?
I'm ok with using the aggregation framework. Bonus point if you can give me the code for the C# driver.
You can use $reduce expression in the following way.
The below query will set the initialValue to the first element of $items.item_field and followed by $lt comparison on the item_field and if true set $$this to $$value, if false keep the previous value and $reduce all the values to find the minimum element and $project to output min item.
db.collection.aggregate([
{
$project: {
items: {
$reduce: {
input: "$items",
initialValue:{
item_field:{
$let: {
vars: { obj: { $arrayElemAt: ["$items", 0] } },
in: "$$obj.item_field"
}
}
},
in: {
$cond: [{ $lt: ["$$this.item_field", "$$value.item_field"] }, "$$this", "$$value" ]
}
}
}
}
}
])
You can use $unwind to seperate items entries.
Then $sort by item_field asc and then $group.
db.coll.find().pretty()
{
"_id" : ObjectId("58edec875748bae2cc391722"),
"items" : [
{
"item_id" : 1,
"item_field" : 10
},
{
"item_id" : 2,
"item_field" : 15
},
{
"item_id" : 3,
"item_field" : 3
}
]
}
db.coll.aggregate([
{$unwind: {path: '$items', includeArrayIndex: 'index'}},
{$sort: { 'items.item_field': 1}},
{$group: {_id: '$_id', item: {$first: '$items'}}}
])
{ "_id" : ObjectId("58edec875748bae2cc391722"), "item" : { "item_id" : 3, "item_field" : 3 } }
We can get expected result using following query
db.testing.aggregate([{$unwind:"$items"}, {$sort: { 'items.item_field': 1}},{$group: {_id: "$_id", minItem: {$first: '$items'}}}])
Result is
{ "_id" : ObjectId("58edf28c73fed29f4b741731"), "minItem" : { "item_id" : 3, "item_field" : 3 } }
{ "_id" : ObjectId("58edec3373fed29f4b741730"), "minItem" : { "item_id" : 3, "item_field" : 3 } }
Given the following documents:
{ "_id" : ObjectId("585901b7875bab86885cf54f"), "foo" : 24, "bar" : [ 1, 2, 5, 6 ] }
{ "_id" : ObjectId("585901be875bab86885cf550"), "foo" : 42, "bar" : [ 3, 4 ] }
I want to get all the unique values in the bar field, something like:
{"_id": "something", "bar": [1, 2, 3, 4, 5, 6]}
This is what I tried:
db.stuff.aggregate([{
$group: {
_id: null,
bar: {
$addToSet: {$each: "$bar"}
}
}
}])
But complains that $each is not a recognized operator.
This does work:
db.stuff.aggregate([{
$group: {
_id: null,
bar: {
$addToSet: "$bar"
}
}
}])
But obviously produces a wrong result:
{ "_id" : null, "bar" : [ [ 3, 4 ], [ 1, 2, 5, 6 ] ] }
EDIT
I managed to have the result I want by adding a first $unwind stage:
db.stuff.aggregate([{
$unwind: { "$bar" },
$group: {
_id: null,
bar: {
$addToSet: "$bar"
}
}
}])
=> { "_id" : null, "bar" : [ 4, 3, 5, 2, 6, 1 ] }
Is it possible at all to make it in one single pipeline stage?
The distinct() works with array field as well so will beautifully do this.
db.stuff.distinct('bar')
The aggregation framework is overkill for this and will not perform well
Consider I have the following data below:
{
"id":123,
"name":"apple",
"codes":["ABC", "DEF", "EFG"]
}
{
"id":234,
"name":"pineapple",
"codes":["DEF"]
}
{
"id":345,
"name":"banana",
"codes":["HIJ","KLM"]
}
If I didn't want to search by a specific code, is there a way to find all fruits in my mongodb collection which shares the same code?
db.collection.aggregate([
{ $unwind: '$codes' },
{ $group: { _id: '$codes', count: {$sum:1}, fruits: {$push: '$name'}}},
{ $match: {'count': {$gt:1}}},
{ $group:{_id:null, total:{$sum:1}, data:{$push:{fruits: '$fruits', code:'$_id'}}}}
])
result:
{ "_id" : null, "total" : 1, "data" : [ { "fruits" : [ "apple", "pineapple" ], "code" : "DEF" } ] }
I have documents in my collections like to:
{
_id: 1,
activities: [
{
activity_id: 1,
travel: [
{
point_id: 1,
location: [-76.0,19.1]
},
{
point_id: 2,
location: [-77.0,19.3]
}
]
},
{
activity_id: 2,
travel: [
{
point_id: 3,
location: [-99.3,18.2]
}
]
}
]
},
{
_id: 2,
activities: [
{
activity_id: 3,
travel: [
{
point_id: 4,
location: [-75.0,11.1]
}
]
}
]
}
I can get the total number of activities, as follows:
db.mycollection.aggregate(
{$unwind: "$activities"},
{$project: {count:{$add:1}}},
{$group: {_id: null, number: {$sum: "$count" }}}
)
I get (3 activities):
{ "result" : [ { "_id" : null, "number" : 3 } ], "ok" : 1 }
question: How can I get the total number of elements in all travels?
expected result: 4 elements
these are:
{
point_id: 1,
location: [-76.0,19.1]
},
{
point_id: 2,
location: [-77.0,19.3]
},
{
point_id: 3,
location: [-99.3,18.2]
},
{
point_id: 4,
location: [-75.0,11.1]
}
You can easily transform document by using double $unwind
e.g.
db.collection.aggregate([
{$unwind: "$activities"},
{$unwind: "$activities.travel"},
{$group:{
_id:null,
travel: {$push: {
point_id:"$activities.travel.point_id",
location:"$activities.travel.location"}}
}},
{$project:{_id:0, travel:"$travel"}}
])
This will emit which is very close to your desired output format:
{
"travel" : [
{
"point_id" : 1.0,
"location" : [
-76.0,
19.1
]
},
{
"point_id" : 2.0,
"location" : [
-77.0,
19.3
]
},
{
"point_id" : 3.0,
"location" : [
-99.3,
18.2
]
},
{
"point_id" : 4.0,
"location" : [
-75.0,
11.1
]
}
]
}
Update:
If you just want to know total number of travel documents in whole collection,
try:
db.collection.aggregate([
{$unwind: "$activities"},
{$unwind: "$activities.travel"},
{$group: {_id:0, total:{$sum:1}}}
])
It will print:
{
"_id" : NumberInt(0),
"total" : NumberInt(4)
}
Update 2:
OP wants to filter documents based on some property in aggregation framework. Here is a way to do so:
db.collection.aggregate([
{$unwind: "$activities"},
{$match:{"activities.activity_id":1}},
{$unwind: "$activities.travel"},
{$group: {_id:0, total:{$sum:1}}}
])
It will print (based on sample document):
{ "_id" : 0, "total" : 2 }