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
Related
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
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"
}
}
}
])
I need to aggregate "lastNames" and "occupations" for a "name" to get a result:
{
name: 'John',
occupations: ['software engineer', 'qa']
lastNames: ['Smith', 'Red', 'Doe']
}
input
name: 'John'
documents present in mongo:
{name: 'John', lastName: 'Smith', occupation: 'software engineer'}
{name: 'Steve', lastName: 'Smith', occupation: 'senior software engineer'}
{name: 'John', lastName: 'Doe', occupation: 'qa'}
{name: 'Steve', lastName: 'Doe', occupation: 'manager'}
{name: 'John', lastName: 'Red', occupation: 'software engineer'}
I started with this aggregation query:
Employees.aggregate([
{ $match: { name: name } },
{
$unwind: {
path: '$lastName',
},
},
{
$unwind: {
path: '$occupation',
},
},
{ $group: { _id: '$name' } },
]);
but this returns an empty array, so I kinda stuck as I never did aggregations before.
Is there a way to produce this required result?
Would be this one:
db.collection.aggregate([
{ $match: { name: "John" } },
{
$group: {
_id: "$name",
occupations: { $addToSet: "$occupation" },
lastNames: { $addToSet: "$lastName" },
}
},
{
$project: {
_id: 0,
name: "$_id",
occupations: 1,
lastNames: 2
}
}
])
Mongo playground
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()
I have this aggregate:
[
{
$match: {_id: new ObjectId('xxx')}
},
{
$unwind: "$users"
},
{
$group: {
_id: '$users.status',
count: {$sum: 1}
}
},
{
$project: {
name: 1,
field1: 1,
field2: 1,
count: '$count'
}
}
]
The schema:
{
name: String,
field1: String,
field2: Schema.Types.Mixed,
users: [{
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
status: String
}]
}
The expected result:
{
name: 'NY',
field1: 'example',
field2: 'example2',
statuses: [
{
_id: 'ONLINE',
count: 20
},
{
_id: 'OFFLINE',
count: 120
},
{
_id: 'OTHER',
count: 230
}
]
}
This way I get group result just for the users statuses, and not for the other fields of the original object.