MongoDB Aggregate functions convert object array to string array - mongodb

I have some documents in a collection. Every document has a challenge_id field. I want to map those document array into a string array. The final string array should consist of challenge ids from each document
input:
[
{
_id: ObjectId("62c3e31931e7df585c39e4e1"),
activity_id: ObjectId("62c3e31931e7df585c39e4df"),
challenge_id: ObjectId("62bd543c3a3937000958f2dd"),
status: "active",
createdAt: ISODate("2022-07-05T07:07:05.823Z"),
updatedAt: ISODate("2022-07-05T07:07:05.823Z")
},
{
_id: ObjectId("62c3e33f299750585cc70b23"),
activity_id: ObjectId("62c3e33e299750585cc70b21"),
challenge_id: ObjectId("62bd543c3a3937000958f2dd"),
status: "active",
createdAt: ISODate("2022-07-05T07:07:43.612Z"),
updatedAt: ISODate("2022-07-05T07:07:43.612Z")
},
{
_id: ObjectId("62c3e359341e86585c65c714"),
activity_id: ObjectId("62c3e359341e86585c65c712"),
challenge_id: ObjectId("62bd543c3a3937000958f2dd"),
status: "active",
createdAt: ISODate("2022-07-05T07:08:09.409Z"),
updatedAt: ISODate("2022-07-05T07:08:09.409Z")
}
]
output should looks like:
['62bd543c3a3937000958f2dd','62bd543c3a3937000958f2dd', '62bd543c3a3937000958f2dd' ]
Is it possible to do this with an aggregate function ? How ?

You can use $group like this:
db.collection.aggregate([
{$group: {_id: 0, res: {$push: {$toString: "$challenge_id"}}}},
{$project: {res: 1, _id: 0}}
])
See how it works on the playground example

Related

How to retrieve object values with same key inside an array

I'm trying to get (filter) all the objects from a document array that match the same key.
Document Schema example:
{
...
country: "ES",
aut_comms:
[
...
{name: "Aragon", province: "Huesca"},
{name: "Aragon", province: "Teruel"},
{name: "Aragon", province: "Zaragoza"},
{name: "Madrid", province: "Madrid"}
...
]
}
If it is possible, im tying to retrieve from the query only the values from the objects that match the same key. Resulting in an array composed like : ["Huesca", "Teruel", "Zaragoza"]
An Array of objects that match the filter will also do the trick:
[
{name: "Aragon", province: "Huesca"},
{name: "Aragon", province: "Teruel"},
{name: "Aragon", province: "Zaragoza"}
]
Thanx
You will be able to get this array by first unwinding the array and then manipulating it
db.demo.aggregate([
{
$unwind:"$aut_comms"
},
{
$match:{"aut_comms.name":"Aragon"}
},
{
$group:{
_id:null,
result: {$push:"$aut_comms.province"}
}
}
])
Edit
It is indeed possible to do such query and output in your expected format. You can do either $unwind or $match first. Personally, I prefer to do a $match first as it would limit the number of (unnecessary) documents generated by the $unwind operation.
db.getCollection('col').aggregate([
{$match: {"aut_comms.name": "Aragon"}},
{$project: {_id: 0, "aut_comms": 1}},
{$unwind: "$aut_comms"},
{$match: {"aut_comms.name": "Aragon"}},
{$project: {"province": "$aut_comms.province"}},
{$group: {
_id: null,
province: {$push: "$province"}
}}
])
The output will be:
/* 1 */
{
"_id" : null,
"province" : [
"Huesca",
"Teruel",
"Zaragoza"
]
}

MongoDB Aggregation, tabular typ results

I have a document model that looks like this
{
_id: objectId,
per: [{
_pid: objectId,
thing1: sting,
thing2: string
}],
ore: [{
_oid: objectId,
thing1: sting,
thing2: string
}],
tre: [{
_tid: objectId,
thing1: sting,
thing2: string
}]
}
and I want to pull back a tabular representation
[
{_id,_pid,thing1,thing2},
{_id,_pid,thing1,thing2},
{_id,_oid,thing1,thing2},
{_id,_oid,thing1,thing2},
{_id,_oid,thing1,thing2},
{_id,_tid,thing1,thing2}
]
How would I go about doing this - Im sure it's an aggregation thing
$setUnion within aggregation would let you combine multiple arrays into one:
See live at MongoPlayground
db.collection.aggregate([
{ $group: {
_id: "$_id",
array: { $push: {
$setUnion: [
"$per",
"$ore",
"$tre"
]
}
}
}
}
]
From that point, it just unwinding the result to your liking.
Here is the completed example with all the unwinding: https://mongoplayground.net/p/Z9-HHMoQOPA

Distinct query in Mongo

I have a document (name, firstName, age).
This command gives me the different names of the document:
db.getCollection('persons').distinct("name")
How do I do to get the corresponding firstNames?
Thanks!
You could try an aggregation query which groups the name and firstName. Eventually you could also add a count to it to see which combinations are repeated (but that is not necessary).
Here is an example:
db.test1.aggregate(
[
{
$group: {_id: {name: "$name", firstName: "$firstName"}, count: {$sum: 1}}
}
]
)
Here is another another option to show an aggregated list:
db.test1.aggregate(
[
{
$group: {_id: {name: "$name"}, firstName: { $push: "$firstName" }}
}
]
)

mongo $project not projecting original values

I am new to Mongodb, and NoSQL in general and I am trying to use mongodbs aggregate function to aggregate data from one collection to be inserted into another. An example of the original collection would be this:
Original Collection
{
supplier: 'aldi',
timestamp: '1492807458',
user: 'eddardstark#gmail.com',
hasBeenAggregated:false,
items:[{
name: 'butter',
supplier: 'aldi',
expiry: '1492807458',
amount: 454,
measureSymbol: 'g',
cost: 2.19
},{
name: 'milk',
supplier: 'aldi',
expiry: '1492807458',
amount: 2000,
measureSymbol: 'ml',
cost: 1.49
}]
}
An example of the output I am trying to achieve would be:
New Collection
{
user:'eddardstark#gmail.com',
amount: 3.68,
isIncome: false,
title: 'food_shopping',
timestamp: '1492807458'
}
The aggregation function that I am using is:
Aggregation
var result = db.runCommand({
aggregate: 'food_transactions',
pipeline: [
{$match: {hasBeenAggregated: false}},
{$unwind: '$items'},
{$group:{_id: '$_id',amount:{$sum: '$items.cost'}}},
{$project: {
_id:0,
user:1,
amount:1,
isIncome: {$literal: false},
title:{$literal: 'food_shopping'},
timestamp:1
}}
]
});
printjson(result)
This aggregation function does not return the user or timestamp fields. Instead, I get the following output:
Output
{
"amount" : 3.6799999999999997,
"isIncome" : false,
"title" : "food_shopping"
}
If I don't group the results and perform the calculations in the $project stage, the fields are all projected correctly, but obviously, there is a new document created for each sub-document in the items array and that rather defeats the purpose of the aggregation.
What am I doing wrong?
Update your $group pipeline to include all the fields you wish to project further down the pipeline.
To include user field you can use $first
{$group:{_id: '$_id', user:{$first:'$user`}, amount:{$sum: '$items.cost'}}},
Additionally, if you are 3.4 version you can simplify your aggregation to below.
Use $reduce to sum all the item's cost in a single document. For all documents you can add $group after $reduce.
db.collection.aggregate([
{$match: {hasBeenAggregated: false}},
{$project: {
_id:0,
user:1,
amount: {
$reduce: {
input: "$items",
initialValue: 0,
in: { $add : ["$$value", "$$this.cost"] }
}
},
isIncome: {$literal: false},
title:{$literal: 'food_shopping'},
timestamp:1
}}
])

Count of unique items in mongodb documents with Array of Strings

I'm having a problem that seems like it can be solved by some aggregation samples I've seen, but I've not come up with an answer yet.
Basically I have documents like so:
{
date: '2015-01-14 00:00:00.000Z',
attendees: ['john', 'jane', 'james', 'joanne'],
groupName: '31'
}
And I need to find the unique attendees for a groupName and their attendance count. So for example, with the data:
{
date: '2015-01-13 00:00:00.000Z',
attendees: ['john', 'jane', 'james', 'joanne'],
groupName: '31'
},
{
date: '2015-01-14 00:00:00.000Z',
attendees: ['james', 'joanne'],
groupName: '31'
},
{
date: '2015-01-15 00:00:00.000Z',
attendees: ['joanne'],
groupName: '31'
}
I'd like to get something like:
[{
name: 'joanne',
count: 3
}, {
name: 'john',
count: 1
}, {
name: 'james',
count: 2
}]
I can't seem to find an aggregation to get this type of result. Any help is appreciated.
you can do this:
db.collection.aggregate([
{$unwind: '$attendees'},
{$group: {_id: '$attendees', count: {$sum: 1}}},
{$project: {_id:0, name: '$_id', count: '$count'}}
])