MongoDB SELECT * GROUP BY - mongodb

I am playing around with MongoDB trying to figure out how to do a simple
SELECT * FROM data GROUP BY userid
But I can't figure out how? I have a collection like
data=[{
_id: "Kcs93S4R9v",
userid: "KqDuEwgv",
time: 1584353953159,
content: {}
}, {
_id: "3d0c4375",
userid: "LqDuEwgv",
time: 1584353953159,
content: {}
}, {
_id: "574ee87",
userid: "LqDuEwgv",
time: 1584353953159,
content: {}
}, {
_id: "Pcs93S4R9v",
userid: "KqDuEwgv",
time: 1584353953159,
content: {}
}]
I want the output to be like
[{
_id: "Kcs93S4R9v",
userid: "KqDuEwgv",
time: 1584353953159,
content: [{}, {}]
}, {
_id: "574ee87",
userid: "LqDuEwgv",
time: 1584353953159,
content: [{}, {}]
}]
But I can't seem to figure it out. Is there any way to do this with MongoDB?

You need to use $group operator in aggregation. Like below:
db.collection.aggregate([
{
$group: {
_id: "$userid",
time: {
$first: "$time"
},
content: {
$push: "$content"
}
}
}
])
MongoPlayGroundLink
Edit:
To first match the documents with the time field, you can use the $match operator,
db.collection.aggregate([
{
$match: {
time: 1584353953159
}
},
{
$group: {
_id: "$userid",
time: {
$first: "$time"
},
content: {
$push: "$content"
}
}
}
])

Related

What's the best way to using nosql in this situation?

I want to find the most up-to-date data by user in mongodb.
Assuming the data exists as follows:
{
_id: "1",
user_id: "userA",
date: "2022-10-20 11:00:00.000000000"
},
{
_id: "2",
user_id: "userA",
date: "2022-10-25 18:00:00.000000000"
},
{
_id: "3",
user_id: "userB",
date: "2022-10-25 18:00:00.000000000"
},
{
_id: "4",
user_id: "userC",
date: "2022-10-25 18:00:00.000000000"
}
So I want find result like this:
{
_id: "2",
user_id: "userA",
date: "2022-10-25 18:00:00.000000000"
},
{
_id: "3",
user_id: "userB",
date: "2022-10-25 18:00:00.000000000"
},
{
_id: "4",
user_id: "userC",
date: "2022-10-25 18:00:00.000000000"
}
What's the best way to this?
If you're using Mongo version 5.2+ then you can use $group with the new $bottom operator, like so:
db.collection.aggregate([
{
$group: {
_id: "$user_id",
root: {
"$bottom": {
"sortBy": {
"date": 1
},
"output": "$$ROOT"
}
}
}
},
{
$replaceRoot: {
newRoot: "$root"
}
}
])
MongoPlayground
Otherwise there is no real good way of doing this, you'll have to sort the entire collection by date (assuming all dates are saved in a proper format) then group by user id and get the "latest" instance.
db.collection.aggregate([
{
$sort: {
date: -1
}
},
{
$group: {
_id: "$user_id",
root: {
$first: "$$ROOT"
}
}
},
{
$replaceRoot: {
newRoot: "$root"
}
}
])
Mongo Playground

create index for mongodb collection

I have a MongoDB collection
{
id: String,
country: String,
createdAt: Date,
...
}
What is the right indices for the collection using this aggregate?
.aggregate([
{ $match: { country: "US" } },
{ $sort: { createdAt: -1 } },
{
$group: {
_id: { id: '$id' },
totalPrice: { $first: '$totalPrice' },
},
},
])
I have these indices on the schema
schema.index({ country: 1 });
schema.index({ createdAt: 1 });
schema.index({ country: 1, id: 1, createdAt: 1 });
But I think something is not healty

Group document mongodb

I'm trying to group document with mongodb but couldn't figure out how.
I have a document looks like this
{
_id: ObjectId('12345'),
username: 'asd',
region: 'zxc',
amount: 500,
type: 'car',
brand: 'vent',
order: 2
},
{
_id: ObjectId('98283'),
username: 'asd',
region: 'zxc',
amount: 1500,
type: 'car',
brand: 'dinosaur',
order: 1
}
And I want to group the document by username, region, type and make a new sub document from the result and order the sub-document ascending by the order. Also calculate the amount as a totalAmount. Which looks like this.
{
username: 'asd',
region: 'zxc',
type: 'car',
cart: [
{
brand: 'dinosaur',
amount: 1500
},
{
brand: 'vent',
amount: 500
}
],
totalAmount: 2000
}
I could only do this so far
db.test.aggregate([
{
$group: {
_id: {username: "$username"},
region: {$first: "$region"},
type: {$first: "$type"},
totalAmount: {$sum: "$amount"}
}
}
])
Thanks
You should put all of the fields you want to group on in the _id.
Use $push to collect values into an array, and $sum to compute the total:
db.collection.aggregate([
{
$group: {
_id: {
username: "$username",
region: "$region",
type: "$type"
},
cart: {
$push: {
brand: "$brand",
amount: "$amount"
}
},
total: {
$sum: "$amount"
}
}
}
])
Playground

aggregation on replies and comments with same user in mongodb

Imagine we have the structure like the following:
comments: [{
user: '...',
upVotes: 12,
text: '...'
replies: [{
user: '...',
upVotes: 34,
text: '...'
}]
}]
what we want is to retrieve the user and text of comments and replies with the same user!
I have implemented the following query but it doesn't work:
db.getCollection('links').aggregate([
{
$unwind: "$comments"
},
{
$unwind: "$comments.replies"
},
{
$project: {
sameAuthor: {
$eq: ["$comments.user", "$comments.replies.user"]
},
}
}
])
I don't know where the problem is!
If you need more clear result please add result sample in the comment.
db.links.aggregate([
{ $unwind: '$comments'},
{ $unwind: '$comments.replies'},
{ $match: { $expr: { $eq: ["$comments.user", "$comments.replies.user"] } } },
{ $group: {
_id: '$_id',
user: { $first: "$comments.user" },
commentText: { $first: "$comments.text" },
repliesTaxt: { $push: "$comments.replies.text" }
}
}
])

MongoDB - Help needed to make some aggregation

I am having a bad time trying to do an aggregation in MongoDB.
I need to cross some infos from each user and as a final result I want a list of users (where there is only one object for each user) and for each object there is some lists with distinct information.
1 - The createdAtList array must be ordered from the oldest to the newest date. The sumOfTotal means the current position total summed up with the previous sumOfTotal (Exemplified in the code below), not just the sum of the total's
2 - The categotyList must be ordered like: category1, category2, category3 ...
3 - The desired final result must be ordered like: user1, user2, user3 ...
Basically I need some help to do the following:
//List of docs from my collection:
[
{
_id: "doc1",
user: "user1",
category: "category1",
createdAt: "2018-01-01T00:00:00.000Z"
},
{
_id: "doc2",
user: "user1",
category: "category2",
createdAt: "2017-12-12T00:00:00.000Z",
},
{
_id: "doc3",
user: "user1",
category: "category1",
createdAt: "2017-12-12T00:00:00.000Z",
},
{
_id: "doc4",
user: "user1",
category: "category2",
createdAt: "2018-01-01T00:00:00.000Z"
},
{
_id: "doc5",
user: "user1",
category: "category3",
createdAt: "2017-11-11T00:00:00.000Z"
}
]
//Desired result:
{
user: "user1",
createdAtList: [ //list ordered by createdAt
{
createdAt: "2017-11-11T00:00:00.000Z",
total: 1,
sumOfTotal: 0
}
{
createdAt: "2017-12-12T00:00:00.000Z",
total: 2,
sumOfTotal: 3 //summed up with the previous
}
{
createdAt: "2018-01-01T00:00:00.000Z",
total: 2,
sumOfTotal: 5 //summed up with the previous
}
],
categotyList: [ //list ordered by category
{
category: "category1",
total: 2
},
{
category: "category2",
total: 2
},
{
category: "category3",
total: 1
}
]
},
...
Is possible to do this in the same aggregate?
I do not think it really makes sense to have the createdAtList.sumOfTotal field. I do not think the fields in an array should be dependent upon a particular order of the elements. If you want some field to contain the sum of the createdAtList.total field, I think there should only be one field (outside of the array). That being said, here is the query I came up with to give you the desired results (using "users" as the name of the collection):
db.users.aggregate([
{
$group: {
_id: {
user: "$user",
createdAt: "$createdAt"
},
total: { $sum: 1 },
category: { $push: "$category" }
}
},
{
$project: {
_id: 0,
user: "$_id.user",
createdAt: "$_id.createdAt",
total: "$total",
category: 1
}
},
{ $unwind: "$category" },
{
$group: {
_id: {
user: "$user",
category: "$category"
},
catTotal: { $sum: 1 },
createdAtList: {
$push: {
createdAt: "$createdAt",
total: "$total"
}
}
}
},
{
$project: {
_id: 0,
user: "$_id.user",
createdAtList: 1,
category: "$_id.category",
catTotal: 1
}
},
{ $unwind: "$createdAtList" },
{
$group: {
_id: "$user",
createdAtList: {
$addToSet: "$createdAtList"
},
categoryList: {
$addToSet: {
category: "$category",
total: "$catTotal"
}
}
}
},
{ $unwind: "$createdAtList" },
{ $sort: { "createdAtList.createdAt": 1 } },
{
$group: {
_id: "$_id",
createdAtList: {
$push: "$createdAtList"
},
categoryList: {
$first: "$categoryList"
}
}
},
{ $unwind: "$categoryList" },
{ $sort: { "categoryList.category": 1 } },
{
$group: {
_id: "$_id",
createdAtList: {
$first: "$createdAtList"
},
categoryList: {
$push: "$categoryList"
}
}
},
{
$project: {
_id: 0,
user: "$_id",
createdAtList: 1,
sumOfTotal: { $sum: "$createdAtList.total" },
categoryList: 1
}
},
{ $sort: { user: 1 } },
]).pretty()