How to get result using stable sort using mongodb - mongodb

I am working on making a query which can sort the result after grouping keys in MongoDB.
Following is the example data in DB
[
{
"_id": ObjectId("5a934e000102030405000000"),
"code": "code",
"groupId": "L0LV7ENT",
"version": {
"id": "1.0.0.0"
},
"status": "Done",
"type": "main"
},
{
"_id": ObjectId("5a934e000102030405000001"),
"code": "code",
"groupId": "L0LV7ENT",
"version": {
"id": "2.0.0.0"
},
"status": "Done",
"type": "main"
},
{
"_id": ObjectId("5a934e000102030405000002"),
"code": "code",
"groupId": "F6WJ9QP7",
"version": {
"id": "1.1.0.0"
},
"status": "Done",
"type": "main"
}
]
Here, I would like to sort the result in ascending order according to the version.id and to group the result according to the groupId.
Hence, I used the following query
db.collection.aggregate([
{
"$match": {
"$and": [
{
"type": "main",
"code": {
"$in": [
"code"
]
},
"status": {
"$in": [
"Done",
"Completed"
]
},
"groupId": {
"$in": [
"L0LV7ENT",
"F6WJ9QP7"
]
}
}
]
}
},
{
"$sort": {
"_id": 1,
"version.id": 1
}
},
{
"$group": {
"_id": {
"groupId": "$groupId"
},
"services": {
"$push": "$$ROOT"
}
}
}
])
But the result I am getting is not stable. Sometimes I see, the data with "_id": ObjectId("5a934e000102030405000002") coming first then ObjectId("5a934e000102030405000000") and ObjectId("5a934e000102030405000001").
It seems intermmitent. Is there any way to get a stable result?
EDIT
You can try it here

From the documentation:
$group does not order its output documents.
So you will need to sort after the group stage to have a deterministic output order.

Related

Enrich array of objects with another document based on a condition

Given two collections users and activities, I want to enrich users.friends.user with all relevant activities, such that users.friends.user matches doc2.invitations.userId.
Below describes exactly what I would like:
user f24b189f-9e00-4d2b-b7f2-148a265fae7c is not a part of any activity, so their activities array is [].
user 223f2e04-6f0c-40a3-a88a-69d0127f2e92 is a part of an activity, so their activities array is populated with the activity object.
Raw:
db={
"users": [
{
"_id": "9c69a57f-e90a-43be-b559-449e973c6cba",
"__v": {
"$numberInt": "0"
},
"friends": [
{
"status": "ACCEPTED",
"user": "f24b189f-9e00-4d2b-b7f2-148a265fae7c"
},
{
"status": "ACCEPTED",
"user": "223f2e04-6f0c-40a3-a88a-69d0127f2e92"
}
]
}
],
"activities": [
{
"_id": {
"$oid": "63a92531bc6dea668f93bb06"
},
"invitations": [
{
"_id": {
"$oid": "63a92531bc6dea668f93bb07"
},
"userId": "9c69a57f-e90a-43be-b559-449e973c6cba",
"status": "ACCEPTED"
},
{
"_id": {
"$oid": "63a92544bc6dea668f93bb09"
},
"userId": "223f2e04-6f0c-40a3-a88a-69d0127f2e92",
"status": "ACCEPTED"
},
],
"__v": {
"$numberInt": "0"
}
}
]
}
Expected result:
"expectedResult": [
{
"_id": "9c69a57f-e90a-43be-b559-449e973c6cba",
"__v": {
"$numberInt": "0"
},
"friends": [
{
"status": "ACCEPTED",
"activities": [{
"_id": {
"$oid": "63a92531bc6dea668f93bb06"
},
"invitations": [
{
"_id": {
"$oid": "63a92531bc6dea668f93bb07"
},
"userId": "9c69a57f-e90a-43be-b559-449e973c6cba",
"status": "ACCEPTED"
},
{
"_id": {
"$oid": "63a92544bc6dea668f93bb09"
},
"userId": "223f2e04-6f0c-40a3-a88a-69d0127f2e92",
"status": "ACCEPTED"
},
],
"__v": {
"$numberInt": "0"
}
}]
},
{
"status": "ACCEPTED",
"activities": []
}
]
}
]
And here's a link to the playground.
I played around with aggregate and populate, but this use case is unlike a regular "join", which is throwing me off. Any pointers will be appreciated.

springboot monogdb update nested document

Document Structure
{
"_id": {
"$oid": "61e6e300f78f707b9c3ec32f"
},
"userId": "61d51daa0e09c2a97f11c81d",
"services": [
{
"sId": "62036c3cde7ac3b60203bdb5",
"status": 1,
"permissionGroups": [
{
"pgId": "61e52858b6d31433bb7faf6c",
"status": 1,
"permissions": [
{
"pId": "61e3f5891e0b130b3a228ff0",
"status": 1
},
{
"pId": "61e4ec54974ad2600b58f2ad",
"status": 1
},
{
"pId": "61e4ec54974ad2600b58f2ae",
"status": 1
}
]
},
{
"pgId": "61e8456e5b1359cb2b89888c",
"status": 1,
"permissions": [
{
"pId": "61e5086fd3af37389f1ba2a0",
"status": 1
},
{
"pId": "61e50870d3af37389f1ba2a1",
"status": 1
},
{
"pId": "61e52313f1a85a169f4f9afd",
"status": 1
},
{
"pId": "61e525c4f1a85a169f4f9b00",
"status": 1
}
]
}
]
}
]
}
Im trying to update a field in the nested document (above mentioned) using mongo query in springboot. my purpose is to update the field called status inside permissions array. i know the query to update it using mongo shell, can someone help me convert the query to java or suggest me a way to do the same.
db.customer_service_details.update(
{
"userId": "61d51daa0e09c2a97f11c81d",
"services": {
"$elemMatch": {
"sId": "62036c3cde7ac3b60203bdb5","permissionGroups.pgId": "61e52858b6d31433bb7faf6c","permissionGroups.permissions.pId":"61e3f5891e0b130b3a228ff0"
}
}
},
{
"$set": { "services.$[outer].permissionGroups.$[inner].permissions.$[inner3].status": "7899" }
},
{
"arrayFilters": [{ "outer.sId": "62036c3cde7ac3b60203bdb5" },{ "inner.pgId": "61e52858b6d31433bb7faf6c" },{"inner3.pId":"61e3f5891e0b130b3a228ff0"}]
}
)

Merge document to an array field using mongodb aggregation

I have a collection like;
{
"_id": ObjectId("5f8069b848e54248f8302b18"),
"topics": [{
"_id": "123",
"name": "ABC",
"type": 0
}, {
"_id": "455",
"name": "DFR",
"type": 0
}
],
"topic": {
"_id": "777",
"name": "FFG",
"type": 123
}
}
Ho can I write an aggregation query to merge topic to topics?
Is topic always unique? (Ie: not already included in topics) If so you can just add it to the array.
https://mongoplayground.net/p/vKv_qjm3VNp
db.collection.aggregate([
{
"$project": {
topics: {
"$concatArrays": [
"$topics",
[
"$topic"
]
]
}
}
}
])

Combining unique elements of arrays without $unwind

I would like to get the unique elements of all arrays in a collection. Consider the following collection
[
{
"collection": "collection",
"myArray": [
{
"name": "ABC",
"code": "AB"
},
{
"name": "DEF",
"code": "DE"
}
]
},
{
"collection": "collection",
"myArray": [
{
"name": "GHI",
"code": "GH"
},
{
"name": "DEF",
"code": "DE"
}
]
}
]
I can achieve this by using $unwind and $group like this:
db.collection.aggregate([
{
$unwind: "$myArray"
},
{
$group: {
_id: null,
data: {
$addToSet: "$myArray"
}
}
}
])
And get the output:
[
{
"_id": null,
"data": [
{
"code": "GH",
"name": "GHI"
},
{
"code": "DE",
"name": "DEF"
},
{
"code": "AB",
"name": "ABC"
}
]
}
]
However, the array "myArray" will have a lot of elements (about 6) and the number of documents passed into this stage of the pipeline will be about 600. So unwinding the array would give me a total of 3600 documents being processed. I would like to know if there's a way for me to achieve the same result without unwinding
You can use below aggregation
db.collection.aggregate([
{ "$group": {
"_id": null,
"data": { "$push": "$myArray" }
}},
{ "$project": {
"data": {
"$reduce": {
"input": "$data",
"initialValue": [],
"in": { "$setUnion": ["$$this", "$$value"] }
}
}
}}
])
Output
[
{
"_id": null,
"data": [
{
"code": "AB",
"name": "ABC"
},
{
"code": "DE",
"name": "DEF"
},
{
"code": "GH",
"name": "GHI"
}
]
}
]

Mongodb 3.2 (Aggregation) : Group by multiple values of the same attribute

I have the following count queries :
db.collection.count({code : {$in : ['delta', 'beta']}});
db.collection.count({code : 'alpha'});
for the given data set
[
{
"code": "detla",
"name": "delta1"
},
{
"code": "detla",
"name": "delta2"
},
{
"code": "detla",
"name": "delta3"
},
{
"code": "detla",
"name": "delta4"
},
{
"code": "beta",
"name": "beta1"
},
{
"code": "beta",
"name": "beta2"
},
{
"code": "beta",
"name": "beta3"
},
{
"code": "beta",
"name": "beta4"
},
{
"code": "beta",
"name": "beta5"
},
{
"code": "alpha",
"name": "alpha1"
},
{
"code": "alpha",
"name": "alpha2"
},
{
"code": "alpha",
"name": "alpha3"
}
]
Is there any way to achieve this using single aggregation() and nested $group in mongodb#3.2?
I understand that this is a classic usecase for mapReduce(), but I am out of option here as the $group by parameters (code in the above example), is a dynamically generated attribute, hence the aggregation stages building is also dynamic.
Expected output is same as the result of the two count queries.
-> First count query (delta, beta combined count) -> 9
-> Second count query (alpha count) -> 3
You can use below $group aggregation
db.collection.aggregate([
{ "$group": {
"_id": null,
"first": {
"$sum": {
"$cond": [{ "$in": ["$code", ["detla", "beta"]] }, 1, 0]
}
},
"second": {
"$sum": {
"$cond": [{ "$eq": ["alpha", "$code"] }, 1, 0]
}
}
}}
])
But I will prefer to go with the two count queries for the better performance
$in is also released in version 3.4. You can either use $setIsSubset for the prior versions
db.collection.aggregate([
{ "$group": {
"_id": null,
"first": {
"$sum": {
"$cond": [{ "$setIsSubset": [["$code"], ["detla", "beta"]] }, 1, 0]
}
},
"second": {
"$sum": {
"$cond": [{ "$eq": ["alpha", "$code"] }, 1, 0]
}
}
}}
])