Mongo Aggs and total count - mongodb

I experiencing some trouble in mongodb to do a query on collection to get all documents filtered, paginate(with skip and limit) and get the total count like that
The chat document look like that:
{
"_id": "c603db3e75ec76f5a4dff559bc525e29",
"members": [{
"user": "5b2b0d5135cfd60185034bf6",
"status": "creator"
}, {
"user": "5b31dc3f88284a009fa847e5",
"status": "member"
}],
"messages": [{
"_id": 2,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "fffasdfsa",
"attachment": null,
"time": 1529995684119,
"type": "text"
},
{...},
{...}
]}
And i would like to get result like that
{
total_count: 3,
chats: [{
"_id": "c603db3e75ec76f5a4dff559bc525e29",
"members": [{
"user": "5b2b0d5135cfd60185034bf6",
"status": "creator"
}, {
"user": "5b31dc3f88284a009fa847e5",
"status": "member"
}],
"creator": {
"user": "5b2b0d5135cfd60185034bf6",
"status": "creator"
},
"lastMessage": {
"_id": 4,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "aasdas",
"attachment": null,
"time": 1529995698736,
"type": "text"
},
"messages": [{
"_id": 2,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "fffasdfsa",
"attachment": null,
"time": 1529995684119,
"type": "text"
}, {
"_id": 3,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "weqw",
"attachment": null,
"time": 1529995690015,
"type": "text"
}, {
"_id": 4,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "aasdas",
"attachment": null,
"time": 1529995698736,
"type": "text"
}]
},
{...},
{...}
]
}
My query is
collection<DbChat>('chats').aggregate([
{
$match: {
messages: { $exists: true }
}
},
{ $unwind: '$messages' },
{ $unwind: '$members' },
{ $group: {_id: '$_id', members: { $push: '$members' }, member: { $last: '$members' }, creator: { $first: '$members' }, messages: {$push: '$messages' } , lastMessage: {$last: '$messages'}}},
{ $sort: this.sort},
]}.skip(skip).limit(size)
with this query i dont get the total_count I tried lot of things but i didnt found a way to get the result and the count in the same query.
Thanks

Related

MongoDB Unwind Error: cannot encode object of type: <class 'set'>

hope you're fine.
I cannot seem to find a way to aggregate the following document by 'equity id'.
{
"_id": {
"$oid": "6001dc246192c700013e8252"
},
"user": "blablabla",
"_type": "User::Individual",
"created_at": {
"$date": "2021-01-15T18:17:11.130Z"
},
"integrations": [{
"_id": {
"$oid": "6001dc62e7a0970001258da8"
},
"status": "completed",
"authentication_failed_msg": null
}],
"portfolios": [{
"_id": {
"$oid": "6001dc62e7a0970001258da9"
},
"_type": "SimplePortfolio",
"transactions": [{
"_id": {
"$oid": "6001dc62e7a0970001258daa"
},
"settlement_period": 2,
"expenses": 0,
"source": "integration",
"_type": "Transaction::Equity::Buy",
"date": {
"$date": "2020-03-02T00:00:00.000Z"
},
"shares": 100,
"price": 13.04,
"equity_id": "abcd"
}, {
"_id": {
"$oid": "6001dc62e7a0970001258dab"
},
"settlement_period": 2,
"expenses": 0,
"source": "integration",
"_type": "Transaction::Equity::Buy",
"date": {
"$date": "2020-03-02T00:00:00.000Z"
},
"shares": 1000,
"price": 1.03,
"equity_id": "efgh"
I tried something like
db.collection.aggregate([{"$unwind": {'$portfolios.transactions'}},
{"$group" : {"_id": "$equity_id"}}])
Got error InvalidDocument: cannot encode object: {'$portfolios.transactions'}, of type: <class 'set'>
Ideally what I want a list grouped by user and equity_id and a sum of its shares. Does anyone know if the error is caused by my aggregation or the document structure?
You should $unwind twice.
db.collection.aggregate([
{
"$unwind": "$portfolios"
},
{
"$unwind": "$portfolios.transactions"
},
{
"$group": {
"_id": "$portfolios.transactions.equity_id"
}
}
])
mongoplayground

MongoDB Ignoring duplicate documents using unique key in aggregate

Documents looks like this.
{
"sId": "s1",
"language": "hindi",
"service": "editing",
"count": 5,
},
{
"sId": "s2",
"language": "hindi",
"service": "editing",
"count": 6,
},
{
"sId": "s2",
"language": "hindi",
"service": "reading",
"count": 6,
},
{
"sId": "s3",
"language": "english",
"service": "reading",
"count": 10,
}
I want the result should be like this
{
"language":"hindi",
"count": 11
},
{
"language":"english",
"count": 10
}
I tried with the aggregate query like this
{
"$group": {
"_id": {
"lang": "$language",
"sId": "$sId"
},
"count": {"$sum": "$count"}
}
}
In sId: s2 it should ignore the second object.
Can anyone please give me a hint how I can achieve the above.
You can use $first to get the first element of each group. You can then use $group again to sum by language.
{
"$group": {
"_id": {
"language": "$language",
"sId": "$sId"
},
"count": {
"$first": {
"$sum": "$count"
}
}
}
}
https://mongoplayground.net/p/3_RjSt1wtRS

Mongo Aggregation using $Max

I have a collection that stores history, i.e. a new document is created every time a change is made to the data, I need to extract fields based on the max value of a date field, however my query keeps returning either all of the dates or requires me to push the fields into an array which make the data hard to analyze for an end-user.
Expected output as CSV:
MAX(DATE), docID, url, type
1579719200216, 12371, www.foodnetwork.com, food
1579719200216, 12371, www.cnn.com, news,
1579719200216, 12371, www.wikipedia.com, info
Sample Doc:
{
"document": {
"revenueGroup": "fn",
"metaDescription": "",
"metaData": {
"audit": {
"lastModified": 1312414124,
"clientId": ""
},
"entities": [],
"docId": 1313943,
"url": ""
},
"rootUrl": "",
"taggedImages": {
"totalSize": 1,
"list": [
{
"image": {
"objectId": "woman-reaching-for-basket",
"caption": "",
"url": "",
"height": 3840,
"width": 5760,
"owner": "Facebook",
"alt": "Woman reaching for basket"
},
"tags": {
"totalSize": 4,
"list": []
}
}
]
},
"title": "The 8 Best Food Items of 2020",
"socialTitle": "The 8 Best Food Items of 2020",
"primaryImage": {
"objectId": "woman-reaching-for-basket.jpg",
"caption": "",
"url": "",
"height": 3840,
"width": 5760,
"owner": "Hero Images / Getty Images",
"alt": "Woman reaching for basket in laundry room"
},
"subheading": "Reduce your footprint with these top-performing diets",
"citations": {
"list": []
},
"docId": 1313943,
"revisionId": "1313943_1579719200216",
"templateType": "LIST",
"documentState": {
"activeDate": 579719200166,
"state": "ACTIVE"
}
},
"url": "",
"items": {
"totalSize": "",
"list": [
{
"type": "recipe",
"data": {
"comInfo": {
"list": [
{
"type": "food",
"id": "https://www.foodnetwork.com"
}
]
},
"type": ""
},
"id": 4,
"uuid": "1313ida-qdad3-42c3-b41d-223q2eq2j"
},
{
"type": "recipe",
"data": {
"comInfo": {
"list": [
{
"type": "news",
"id": "https://www.cnn.com"
},
{
"type": "info",
"id": "https://www.wikipedia.com"
}
]
},
"type": "PRODUCT"
},
"id": 11,
"uuid": "318231jc-da12-4475-8994-283u130d32"
}
]
},
"vertical": "food"
}
Below query:
db.collection.aggregate([
{
$match: {
vertical: "food",
"document.documentState.state": "ACTIVE",
"document.templateType": "LIST"
}
},
{
$unwind: "$document.items"
},
{
$unwind: "$document.items.list"
},
{
$unwind: "$document.items.list.contents"
},
{
$unwind: "$document.items.list.contents.list"
},
{
$match: {
"document.items.list.contents.list.type": "recipe",
"document.revenueGroup": "fn"
}
},
{
$sort: {
"document.revisionId": -1
}
},
{
$group: {
_id: {
_id: {
docId: "$document.docId",
date: {$max: "$document.revisionId"}
},
url: "$document.items.list.contents.list.data.comInfo.list.id",
type: "$document.items.list.contents.list.data.comInfo.list.type"
}
}
},
{
$project: {
_id: 1
}
},
{
$sort: {
"document.items.list.contents.list.id": 1, "document.revisionId": -1
}
}
], {
allowDiskUse: true
})
First of all, you need to go through the documentation of the $group aggregation here.
you should be doing this instead:
{
$group: {
"_id": "$document.docId"
"date": {
$max: "$document.revisionId"
},
"url": {
$first: "$document.items.list.contents.list.data.comInfo.list.id"
},
"type": {
$first:"$document.items.list.contents.list.data.comInfo.list.type"
}
}
}
This will give you the required output.

Aggregate nested arrays

I have multiple documents, and I'm trying to aggregate all documents with companyId = xxx and return one array with all the statuses.
So it will look like this:
[
{
"status": "created",
"date": "2019-03-16T10:59:59.200Z"
},
{
"status": "completed",
"date": "2019-03-16T11:00:37.750Z"
},
{
"status": "created",
"date": "2019-03-16T10:59:59.200Z"
},
{
"status": "completed",
"date": "2019-03-16T11:00:37.750Z"
},
{
"status": "created",
"date": "2019-03-16T10:59:59.200Z"
},
{
"status": "completed",
"date": "2019-03-16T11:00:37.750Z"
},
{
"status": "created",
"date": "2019-03-16T10:59:59.200Z"
},
{
"status": "completed",
"date": "2019-03-16T11:00:37.750Z"
}
]
The document look like this:
[
{
"companyId": "xxx",
"position": "",
"section": "",
"comment": "",
"items": [
{
"any": "111",
"name": "some name",
"description": "some description",
"version": "3",
"status": [
{
"status": "created",
"date": "2019-03-16T10:59:59.200Z"
},
{
"status": "completed",
"date": "2019-03-16T11:00:37.750Z"
}
]
},
{
"any": "222",
"name": "some name",
"description": "some description",
"version": "3",
"status": [
{
"status": "created",
"date": "2019-03-16T10:59:59.200Z"
},
{
"status": "completed",
"date": "2019-03-16T11:00:37.750Z"
}
]
}
]
},
{
"companyId": "xxx",
"position": "",
"section": "",
"comment": "",
"items": [
{
"any": "111",
"name": "some name",
"description": "some description",
"version": "3",
"status": [
{
"status": "created",
"date": "2019-03-16T10:59:59.200Z"
},
{
"status": "completed",
"date": "2019-03-16T11:00:37.750Z"
}
]
},
{
"any": "222",
"name": "some name",
"description": "some description",
"version": "3",
"status": [
{
"status": "created",
"date": "2019-03-16T10:59:59.200Z"
},
{
"status": "completed",
"date": "2019-03-16T11:00:37.750Z"
}
]
}
]
}
]
Any suggestion, how to implement this?
Then I want to loop over the array (in code) and count how many items in status created, and completed. maybe it could be done with the query?
Thanks in advance
You can use below aggregation:
db.col.aggregate([
{
$match: { companyId: "xxx" }
},
{
$unwind: "$items"
},
{
$unwind: "$items.status"
},
{
$replaceRoot: {
newRoot: "$items.status"
}
},
{
$group: {
_id: "$status",
count: { $sum: 1 }
}
}
])
Double $unwind will return single status per document and then you can use $replaceRoot to promote each status to root level of your document.
Additionally you can add $group stage to count documents by status.
In addition to the #mickl answer, you can add $project pipeline to get the result as a flat list of status and count.
db.collectionName.aggregate([
{
$match: { companyId: "xxx" }
},
{
$unwind: "$items"
},
{
$unwind: "$items.status"
},
{
$replaceRoot: {
newRoot: "$items.status"
}
},
{
$group: {
_id: "$status",
count: { $sum: 1 }
}
},
{
$project: {
"status":"$_id",
"count":1,
_id:0
}
}
])
If the number of documents on which you are executing the above query is too much then you should avoid using $unwind in the initial stage of aggregation pipeline.
Either you should use $project after $match to reduce the selection of fields or you can use below query:
db.col.aggregate([
{
$match: {
companyId: "xxx"
}
},
{
$project: {
_id: 0,
data: {
$reduce: {
input: "$items.status",
initialValue: [
],
in: {
$concatArrays: [
"$$this",
"$$value"
]
}
}
}
}
},
{
$unwind: "$data"
},
{
$replaceRoot: {
newRoot: "$data"
}
}
])

MongoDB Aggregation Error Returning wrong result

I have my json object like this
{
"_id": "5c2e811154855c0012308f00",
"__pclass": "QXRzXFByb2plY3RcTW9kZWxcUHJvamVjdA==",
"id": 44328,
"name": "Test project via postman2//2",
"address": "some random address",
"area": null,
"bidDate": null,
"building": {
"name": "Health Care Facilities",
"type": "Dental Clinic"
},
"collaborators": [],
"createdBy": {
"user": {
"id": 7662036,
"name": "Someone Here"
},
"firm": {
"id": 2520967,
"type": "ATS"
}
},
"createdDate": "2019-01-03T21:39:29Z",
"customers": [],
"doneBy": null,
"file": null,
"firm": {
"id": 1,
"name": "MyFirm"
},
"leadSource": {
"name": "dontknow",
"number": "93794497"
},
"location": {
"id": null,
"city": {
"id": 567,
"name": "Bahamas"
},
"country": {
"id": 38,
"name": "Canada"
},
"province": {
"id": 7,
"name": "British Columbia"
}
},
"modifiedBy": null,
"modifiedDate": null,
"projectPhase": {
"id": 1,
"name": "pre-design"
},
"quotes": [{
"id": 19,
"opportunityValues": {
"Key1": 100,
"Key2 Key2": 100,
"Key3 Key3 Key3": 200,
}
}],
"specForecast": [],
"specIds": [],
"tags": [],
"valuation": "something"
}
I am trying to aggregate using this query in MongoDB. My aggregation key is 4 level deep and also contains spaces. On all online examples shows me the aggregation at the first level. Looking to the online codes, I tried to re-iterate the same with my 4th level deep key.
db.mydata.aggregate([
{$match: {"id": 44328 } } ,
{$group: { _id: "$quotes.id",
totalKey2:{ $sum: "$quotes.opportunityValues.Key2 Key2"},
totalKey3:{ $sum: "$quotes.opportunityValues.Key3 Key3 Key3"}
}
}
]);
This should return
_id totalKey2 totalKey3
0 19 100 300
But it is returning
_id totalKey2 totalKey3
0 19 0 0
What am I doing Wrong?
Although it's not recommended to use space in field names in Mongo, it works as expected.
The problem with your query is that "quotes" is an array and you should first unwind it before grouping it.
This works as expected:
db.mydata.aggregate([
{ $match: { "id": 44328 } } ,
{ $unwind: "$quotes" },
{ $group: { _id: "$quotes.id",
totalKey2:{ $sum: "$quotes.opportunityValues.Key2 Key2" },
totalKey3:{ $sum: "$quotes.opportunityValues.Key3 Key3 Key3" } }
}
]);