How to group multiple record using aggregate in mongodb - mongodb

I want to group on the basis of the element key that can be similar in many records.
Business_portfolio_attributes details
[{
"_id" : ObjectId("613f2428d62bbe20db999133"),
"status" : 1,
"element" : "Current Assets",
"instituteBenchMarkId" : ObjectId("613b11006845993d61346bf9"),
"attributeValue" : 352907.04,
"startDate" : ISODate("2021-07-01T00:00:00.000Z"),
"reportId" : ObjectId("613f2427d62bbe20db999130"),
"businessId" : ObjectId("61370a5ac3941822005f91de"),
"category" : "BS",
"__v" : 0,
"createdAt" : ISODate("2021-09-13T10:12:56.334Z"),
"updatedAt" : ISODate("2021-09-13T10:12:56.334Z")
}]
The query I have used is mentioned below. Any alternative for this query?
db.getCollection('business_portfolio_attributes').aggregate([
{$match: { category: "BS"}},
{$group: { _id: "$instituteBenchMarkId", elements:{ $push: {
"element": "$element",
"startDate": "$startDate"
} }} }
])

Related

Whats the alternative to $replaceRoot on mongoDB? $replaceRoot is incompatible with documentDB

The problem: I'm trying to make a query on MongoDB, but I'm using the DocumentDb from amazon, where some operations are no supported. I wanted to find an alternative to get the same result, if possible. Basically I want to change the root of the result, instead of being the first entity, I need it to be some merging of some values in different levels of the document.
So, I have the following structure in my collection:
{
"_id" : ObjectId("5e598bf4d98f7c70f9aa3b58"),
"status" : "active",
"invoices" : [
{
"_id" : ObjectId("5e598bf13b24713f50600375"),
"value" : 1157.52,
"receivables" : [
{
"situation" : {
"status" : "active",
"reason" : []
},
"rec_code" : "001",
"_id" : ObjectId("5e598bf13b24713f50600374"),
"expiration_date" : ISODate("2020-03-25T00:00:00.000Z"),
"value" : 1157.52
}
],
"invoice_code" : 9773,
"buyer" : {
"legal_name" : "test name",
"buyer_code" : "223132165498797"
}
},
],
"seller" : {
"code" : "321654897986",
"name" : "test name 2"
}
}
What I want to achieve is to list all "receivables" like this, where the _id is the _id of the receivable:
[{
"_id" : ObjectId("5e598bf13b24713f50600374"),
"situation" : {
"status" : "active",
"reason" : []
},
"rec_code" : "001",
"expiration_date" : ISODate("2020-03-25T00:00:00.000Z"),
"value" : 1157.52,
"status" : "active",
"seller" : {
"cnpj" : "321654897986",
"name" : "test name 2"
},
"invoice_code" : 9773.0,
"buyer" : {
"legal_name" : "test name",
"cnpj" : "223132165498797"
}
}]
This I can do with $replaceRoot in with the query below on MongoDB, but using documentDB I can't use $replaceRoot or $mergeObjects. Do you know how can I get the same result with other operators?:
db.testCollection.aggregate([
{ $unwind: "$invoices" },
{ $replaceRoot: {
newRoot: {
$mergeObjects: ["$$ROOT","$invoices"]}
}
},
{$project: {"_id": 0, "value": 0, "created_at": 0, "situation": 0}},
{ $unwind: "$receivables" },
{ $replaceRoot: {
newRoot: {
$mergeObjects: ["$receivables", "$$ROOT"]
}
}
},
{$project:{"created_at": 0, "receivables": 0, "invoices": 0}}
])
After going through mongodb operations, I could get a similar result fro what I wanted with the following query without $replaceRoot. It turns out it was a better query, I think:
db.testCollection.aggregate([
{$unwind: "$invoices"},
{$project : {
created_at: 1,
seller: "$seller",
buyer: "$invoices.buyer",
nnf: "$invoices.nnf",
receivable: '$invoices.receivables'
}
},
{$unwind: "$receivable"},
{$project : {
_id: '$receivable._id',
seller: 1,
buyer: 1,
invoice_code: 1,
receivable: 1,
created_at: 1,
}
},
{$sort: {"created_at": -1}},
])
This query resulted in the following structure list:
[{
"created_at" : ISODate("2020-03-06T09:47:26.161Z"),
"seller" : {
"name" : "Test name",
"cnpj" : "21231232131232"
},
"buyer" : {
"cnpj" : "21322132164654",
"legal_name" : "Test name 2"
},
"invoice_code" : 66119,
"receivable" : {
"rec_code" : "001",
"_id" : ObjectId("5e601bb5efff82b92935bad4"),
"expiration_date" : ISODate("2020-03-17T00:00:00.000Z"),
"value" : 6540.7,
"situation" : {
"status" : "active",
"reason" : []
}
},
"_id" : ObjectId("5e601bb5efff82b92935bad4")
}]
Support for $replaceRoot was added to Amazon DocumentDB in January 2021.

MongoDB, Grouping by Multiple Fields

pretty new to Mongo and am finding some simple things that i would do in SQL frustratingly difficult in Mongo.
I have an object similar to this below
[{
"_id" : ObjectId("5870fb29a1fe030e1a2909db"),
"updatedAt" : ISODate("2017-01-07T14:28:57.224Z"),
"createdAt" : ISODate("2017-01-07T14:28:57.224Z"),
"state" : "Available",
},
{
"_id" : ObjectId("5870fb29a1fe030e1a2909dc"),
"updatedAt" : ISODate("2017-01-07T14:28:57.224Z"),
"createdAt" : ISODate("2017-01-07T14:28:57.224Z"),
"state" : "notReady",
},
{
"_id" : ObjectId("5870fb29a1fe030e1a2909d9"),
"updatedAt" : ISODate("2017-01-07T14:28:57.224Z"),
"createdAt" : ISODate("2017-01-07T14:28:57.224Z"),
"state" : "Disconnected",
}]
What i'm looking to do it group the data by the Maximum date and the state.
Ideally the result i would be looking for would be something like the following.
{
latestDate: "2017-01-07T14:28:57",
states : {
available : 10,
disconnected : 5,
notReady : 2
}}
Basically i'm looking for the SQL equivalent of this:
SELECT createdAt, state, COUNT(rowid)
FROM db
WHERE date = (SELECT MAX(createdAt) FROM db)
GROUP BY 1,2
I've searched around here and have found some good info but am probably missing something straight forward. Ive only managed to get here so far
db.collection.aggregate([
{$project: {"_id" : 0,"state": 1, "date" : "$createdAt"}},
{$group : {"_id" : {"date":"$date", "state": "actual"}, "count":{"$sum":1}}}
])
Any help would be appreciated :)
db.collection.aggregate([
{
$group : {
_id : {
date : "$createdAt",
state : "$state"
},
count : {$sum : 1}
}
},
{
$group : {
_id : "$_id.date",
states : {
$addToSet : {
state : "$_id.state",
count : "$count"
}
}
}
},
{
$sort : {_id : -1}
},
{
$limit : 1
},
{
$project : {
_id : 0,
latestDate : "$_id",
states : "$states"
}
}
])
output :
{
"latestDate" : ISODate("2017-01-07T14:28:57.224Z"),
"states" : [
{
"state" : "Available",
"count" : 1
},
{
"state" : "notReady",
"count" : 1
},
{
"state" : "Disconnected",
"count" : 1
}
]
}

Mongodb aggregation: how to use unwind->group->project multiple times

I have an orders collection where I need to calculate some sums from multiple sub-arrays arrays but I can't figure out how to loose the multiplied items that the double unwind creates.
db.Orders.aggregate(
{$unwind: "$items"},
{$unwind: "$shipping"},
{$group: {
_id: {
year: { '$year': '$createdAt' },
month: { '$month': '$createdAt' },
day: { '$dayOfMonth': '$createdAt' }
},
mainItems: { $addToSet: '$items' },
totalSales: {$sum: {
$multiply: ["$items.quantity", "$items.variants.price"]
}},
averageSales: {$avg: {$multiply: ["$items.quantity", "$items.variants.price"]}},
/* this will not sum the individial orders because the unwind
* created multiple document per order*/
ordersPlaced: {$sum: 1},
itemsPurchased: {$sum: "$items.quantity"},
totalRefundAmount: {$sum: 0},
chargedForShipping: {$sum: "$shipping.shipmentMethod.rate"}
}}
)
If I take out the shipping from the unwind and the group the query will return the correct values except for the chargedForShipping (0 since it's unwinded) and ordersPlaces which will still be more than expected (but I also need the shipping information and even more additional ones that I took out for easier understanding).
Sample data:
[{
"_id" : "xK29ZHxGcYvgWgx5p",
"sessionId" : "yw7e9G7uBzYTy9Grq",
"userId" : "fZREMm2DmsnMosMKj",
"shopId" : "oiqQDnuBwabj44q2o",
"billing" : [
{
"shopId" : "oiqQDnuBwabj44q2o",
"_id" : "9TMJj9w65MmAkgg27",
"paymentMethod" : {
"amount" : 22.45,
"status" : "settled",
"mode" : "capture",
"transactionId" : "AP",
"createdAt" : ISODate("2016-02-15T13:44:35.116Z"),
"transactions" : [
{ type:"refund", amount:5}
]
}
},
{
"shopId" : "9YfkXWyCci8fN43Pj",
"_id" : "RwW8xMnFzQqdTpqtg",
"paymentMethod" : {
"createdAt" : ISODate("2016-02-15T13:44:35.116Z")
}
},
{
"shopId" : "SgXWPKGJkxBw6qsbT",
"_id" : "ASizt6BtkxpCxgEJn",
"paymentMethod" : {
"createdAt" : ISODate("2016-02-15T13:44:35.116Z")
}
}
],
"shipping" : [
{
"_id" : "yXb5T5zLuxPYmgoT5",
"shipmentMethod" : {
"name" : "Continental US",
"_id" : "womiJX2QZBFQQWFur",
"rate" : 9.949999999999999,
"shopId" : "9YfkXWyCci8fN43Pj",
},
"items" : [
{
"_id" : "48s9bmDfrRMqnkije",
"productId" : "KXtF5xqERWJsXk2yP",
"shopId" : "SgXWPKGJkxBw6qsbT",
"variantId" : "YQDHuyPHbhx4wruZx",
"quantity" : 1
}
],
"packed" : false,
"shipped" : false,
}
],
"items" : [
{
"_id" : "hhuiGFTBkLACLpPjQ",
"shopId" : "9YfkXWyCci8fN43Pj",
"productId" : "sDYNXMrnRJiyQ8gex",
"quantity" : 1,
"variants" : {
"_id" : "muJi6Bqnq2CD8B7AR",
"price" : 2.5,
"title" : "egy",
"weight" : 23,
},
"type" : "simple",
},
{
"_id" : "48s9bmDfrRMqnkije",
"shopId" : "SgXWPKGJkxBw6qsbT",
"productId" : "KXtF5xqERWJsXk2yP",
"quantity" : 1,
"variants" : {
"_id" : "YQDHuyPHbhx4wruZx",
"title" : "Bogi varinat title",
"price" : 10,
"type" : "variant",
"compareAtPrice" : 100000,
"weight" : 100,
},
"type" : "simple",
}
],
"email" : "test#user.com",
"createdAt" : ISODate("2016-02-15T13:44:35.091Z"),
"updatedAt" : ISODate("2016-02-15T14:24:55.174Z")
}]
What I would need is orderTotal per month, shippingTotal per month, totalRefunded per month, average sales per month. The issue is one I need from the items sub-array the other from the shipping sub-array and the third from the billing sub-array that is why I have issues with the unwind.

MongoDB group by subdocument counts and year

Here is a sample of my document from collection called products:
{
"_id" : "B000KIT6LQ",
"brand" : "unknown",
"category" : "Electronics",
"price" : "11.99",
"title" : "Scosche KA2067B 2005..."
"reviews" : [
{
"date" : ISODate("1969-12-31T23:59:59Z"),
"score" : 5,
"user_id" : "AK7M5Y7E9O3L7",
"sentiment" : 0.5,
"text" : "Bought this so I ...",
"user_gender" : "female",
"voted_total" : 0,
"voted_helpful" : 0,
"user_name" : "Alex",
"summary" : "It is what it is"
},
{
"date" : ISODate("1969-12-31T23:59:59Z"),
"score" : 5,
"user_id" : "A26VRLMPEA8IDR",
"sentiment" : 0.352,
"text" : "Years ago I worked as an...",
"user_gender" : "male",
"voted_total" : 0,
"voted_helpful" : 0,
"user_name" : "Jack R. Smith",
"summary" : "Great Kit"
},
{
"date" : ISODate("1969-12-31T23:59:59Z"),
"score" : 4,
"user_id" : "A1TGBDVX3QXCRH",
"sentiment" : 0.19318181818181818,
"text" : "This insert works great in my ...",
"user_gender" : "female",
"voted_total" : 0,
"voted_helpful" : 0,
"user_name" : "J. Reed",
"summary" : "Fits great in my 2006 Spectra5"
}
]
}
I have many documents with multiple categories. I am trying to create a mongo query which will result in all categories with the number of reviews (subdocument) per year. I have to group by categories and year, and get the count for number of reviews.
This is the query that I have got so far:
db.products.aggregate([
{ $unwind : "$reviews" },
{ $group: {
_id: {category: "$category", date: "$reviews.date.getFullYear()"},
count: { $sum: 1 }}},
{$sort:{"count": -1}}
])
For some reason the getFullYear() method is not working for me. If I group by $reviews.date I get the results.
Any pointers on getting the query right is appreciated.
Thanks
You can't use JavaScript functions like getFullYear() in your aggregate pipeline, you need to use the equivalent aggregation Date operator, which in this case is $year.
db.products.aggregate([
{ $unwind : "$reviews" },
{ $group: {
_id: {category: "$category", date: {$year: "$reviews.date"}},
count: { $sum: 1 }}},
{$sort:{"count": -1}}
])

How to ensure grouping via two separate criteria

Expanded from How to average the summed up values in mongodb?
Using MongoDB 2.4.8,
I have the following records
{
"category" : "TOYS",
"price" : 12,
"status" : "online",
"_id" : "35043"
}
{
"category" : "TOYS",
"price" : 13,
"status" : "offline",
"_id" : "35044"
}
{
"category" : "TOYS",
"price" : 22,
"status" : "online",
"_id" : "35045"
}
{
"category" : "BOOKS",
"price" : 13,
"status" : "offline",
"_id" : "35046"
}
{
"category" : "BOOKS",
"price" : 17,
"status" : "online",
"_id" : "35047"
}
{
"category" : "TOYS",
"price" : 19,
"status" : "unavailable",
"_id" : "35048"
}
{
"category" : "BOOKS",
"price" : 10,
"status" : "unavailable",
"_id" : "35049"
}
{
"category" : "BOOKS",
"price" : 17,
"status" : "unavailable",
"_id" : "35050"
}
I want to find the average price of all categories whose status is online OR offline and total price within a category is more than 50.
Toys offline and Toys online are considered two separate categories.
I adapted the answer given.
db.items.aggregate([
{$match:
{
$or: [
{status:"online"},
{status:"offline"}
]
}
},
{$group :
{
_id: "$category",
total_price: {$sum:"$price"},
}
},
{$match:
{
total_price:{$gt:50}
}
},
{$group :
{
_id: "1",
avg_price: {$avg:"$total_price"},
}
},
]);
But I believe this query I adapted grouped categories of the same name together which is not what I am looking for.
If online and offline are the only values for status, you can remove the initial $match step. If it is needed, it would be more appropriate to use the $in operator as these values could be found in the same index (if one existed).
I think the only step you are missing is that you can $group by multiple fields (i.e. category and status):
db.items.aggregate(
// If 'online' and 'offline' are the only possible status values, this may be unnecessary
{ $match: {
'status' : { $in: [ 'online', 'offline' ] }
}},
// Group by category & status
{ $group: {
_id: { category: "$category", status: "$status" },
total_price: { $sum: "$price" },
}},
// Only find groups where total_price is > 50
{ $match: {
total_price: { $gt:50 }
}},
// Find the average price for the group
{ $group : {
_id: null,
avg_price: {$avg:"$total_price"},
}}
)