How can I select a document where a maximum subdocument from an array has a certain property? - mongodb

My document contains an array of subdocuments. Each subdocument has two values - one is a number and the other is a string. One example of such documents would be:
[{
properties:
[{
"A": "Forest",
"B": 1
},
{
"A": "Mountain",
"B": 2
},
{
"A": "Lake",
"B": 3
}]
},{
properties:
[{
"A": "Forest",
"B": 1
},
{
"A": "Lake",
"B": 2
},
{
"A": "Mountain",
"B": 3
}]
},
]
where properties is the array of subdocuments. Subdocuments are indexed by both B and A. The maximum subdocument in this context is the one with the largest B.
If I query Lake, the result is supposed to be:
[{
properties:
[{
"A": "Forest",
"B": 1
},
{
"A": "Mountain",
"B": 2
},
{
"A": "Lake",
"B": 3
}]
}]
because that is a document where the maximum subdocument contains A equal to Lake.
How can I perform this query?
I found a similar question here - Highest value from sub-arrays in documents - but this question is about retrieving the documents with the maximum B. Mine is about filtering documents by property A of maximum B.
I would appreciate any pointers.

Related

MongoDB query: keys and their distinct values (each key independently)?

If you have this collection of objects:
{ "a": 10, "b": 20, "c": 30 }
{ "a": 11, "b": 20, "c": 31 }
{ "a": 10, "b": 20, "c": 31 }
There is a way to get distinct values, for example, for field "a":
[10, 11]
There is also a way to get distinct values of any tuple, for example, for pairs of ("b", "c"):
[
{"b": 20, "c": 30},
{"b": 20, "c": 31}
]
Is there a way to query distinct values for each field individually in a single query?
For example, I can simply use query 1 above 3 times for "a", "b", "c":
[10, 11]
[20]
[30, 31]
But I guess it might be less efficient and there should be a better option.
Bonus: How to do it if the list of fields is not known upfront?
Ideally, the single query should return all keys and their distinct values:
{
"a": [10, 11],
"b": [20],
"c": [30, 31]
}
Assuming you don't know the full list of the fields beforehand, you need to use $objectToArray to convert the $$ROOT document into an array of k-v tuples. Then group by the field name and $addToSet the values.
db.collection.aggregate([
{
"$project": {
_id: 0,
arr: {
"$objectToArray": "$$ROOT"
}
}
},
{
"$unwind": "$arr"
},
{
$match: {
"arr.k": {
$ne: "_id"
}
}
},
{
$group: {
_id: "$arr.k",
values: {
"$addToSet": "$arr.v"
}
}
}
])
Mongo Playground

Sum from array object value by mongodb

I am trying to solve a problem. I want to write a query that finds a document among my documents which one is greater by the sum of columns A and B's in an array. I write an example down here. I am new to MongoDB and I've been searching a lot but I could not find my solution. So can somebody help me to solve this problem? Here are my sample documents:
document1:
{
"_id" : "1",
"array": [
{
"user": "1",
"A": 2,
"B": 0
},
{
"user": "2",
"A": 3,
"B": 1},
{
"user": "3",
"A": 0,
"B": 5
}
]
}
and document 2:
{
"_id" : "2",
"array": [
{
"user": "4",
"A": 1,
"B": 1
},
{
"user": "5",
"A": 2,
"B": 2
}
]
}
for example, the sum of A and B's in all elements of an array in document 1 is 11 and the sum of A and B's in elements of an array in document 2 is 6. So I want to get document 1 for output because it is greater than 2 after summing all A and B's in all of the elements.
You can try this query:
Create an auxiliar field called total (or whatever name you want) and $add values. This add the $sum of the arrays. That means here you are adding all values from A and B together.
Then sort by the auxiliar field to get the greatest at first position
$limit to only one (the greatest)
And $project to not output the auxiliar field.
db.collection.aggregate([
{
"$addFields": {
"total": {
"$add": [
{
"$sum": "$array.A"
},
{
"$sum": "$array.B"
}
]
}
}
},
{
"$sort": {
"total": -1
}
},
{
"$limit": 1
},
{
"$project": {
"total": 0
}
}
])
Example here

How to group a collection to sum the properties of an object and generate a calculated field with MongoDB

how to group a collection by a specific field and generate a field calculated with the sum of the properties of an object with mongoDB, I don't know if it's possible to do what I'm trying to do but I would like to know.
I want to group by duelId field to add the score
Collection:
{ "matches": [{
"secondParticipant": {"name": "Maria","battlesWon": 2},
"firstParticipant": {"name": "Fabio","battlesWon": 1},
"duelId": "6c3e532d-3c0e-4438-8289-c86a4a51a102",
},{
"secondParticipant": {"name": "Fabio","battlesWon": 1 },
"firstParticipant": {"name": "Maria","battlesWon": 1 },
"duelId": "6c3e532d-3c0e-4438-8289-c86a4a51a102"
},
{
"secondParticipant": {"name": "Luiz","battlesWon": 1},
"firstParticipant": {"name": "Jose","battlesWon": 1},
"duelId": "6c3e532d-3c0e-4438-8289-c86a4a51a666"
}]}
expected:
{[ [
{
"secondParticipant": {"name": "Maria","battlesWon": 2 },
"firstParticipant": {"name": "Fabio","battlesWon": 1 },
"duelId": "6c3e532d-3c0e-4438-8289-c86a4a51a102",
},{
"secondParticipant": {"name": "Fabio","battlesWon": 1},
"firstParticipant": {"name": "Maria","battlesWon": 1},
"duelId": "6c3e532d-3c0e-4438-8289-c86a4a51a102"
},
"score": { "Fabio": 2, "Maria": 3 } *// this field would be the sum of battlesWon after grouping*
],
[
{
"secondParticipant": {"name": "Luiz","battlesWon": 1},
"firstParticipant": {"name": "Jose","battlesWon": 1},
"duelId": "6c3e532d-3c0e-4438-8289-c86a4a51a666"
},
"score": { "Luiz": 1, "Jose": 1 } // *this field would be the sum of battlesWon after grouping*
]]}
You can do the following:
$unwind the matches array
rename firstParticipant and secondParticipant into an array of k-v tuple for further processing
group by duel and name to sum up the score of each participant
group by duel to get the requested name-score tuple for each duel
use $arrayToObject to convert the k-v tuple array in step 2 back to the requested form.
Here is the Mongo Playground for your reference.

How to add calculated fields inside subdocuments without using unwind?

I'm looking for a simple solution to add a field to a subdocument without using $unwind and $group.
I need only to calculate the sum and the size of a nested subdocuments and show it in a new field.
This is my starting collection:
{
"_id": ObjectId("5a934e000102030405000000"),
"subDoc": [
{
"a": 1,
"subArray": [1,2,3]
},
{
"b": 2
}
]
},
{
"_id": ObjectId("5a934e000102030405000001"),
"subDoc": [
{
"a": 1,
"subArray": [4,5,6]
},
{
"b": 2
},
{
"c": 3,
"subArray": [8,8,8]
}
]
}
And this is my desired result, where I've added sum (sum of subArray) and size (number of elements in subArray):
{
"_id": ObjectId("5a934e000102030405000000"),
"subDoc": [
{
"a": 1,
"subArray": [1,2,3],
"sum": 6,
"size": 3
},
{
"b": 2
"sum": 0,
"size": 0
}
]
},
{
"_id": ObjectId("5a934e000102030405000001"),
"subDoc": [
{
"a": 1,
"subArray": [4,5,6],
"sum": 15,
"size": 3
},
{
"b": 2,
"sum": 0,
"size": 0
},
{
"c": 3,
"subArray": [8,8],
"sum": 16,
"size": 2
}
]
}
I know how to obtain this result using $unwind and then $group, but I'd like to know if there is any other way (or a better way!) to achieve the same result. I've tried using $addFields and $map without success.
Working playground example: https://mongoplayground.net/p/fK8t6SLlOHa
$map to iterate loop of subDoc array
$sum to get total of subArray array of numbers
$ifNull to check if field is not present or null then return empty array because $size operator only allows array input
$size to get total elements in subArray array
$mergeObjects to merge current object with new added fields
db.collection.aggregate([
{
$addFields: {
subDoc: {
$map: {
input: "$subDoc",
in: {
$mergeObjects: [
"$$this",
{
sum: { $sum: "$$this.subArray" },
size: {
$size: { $ifNull: ["$$this.subArray", []] }
}
}
]
}
}
}
}
}
])
Playground

Get value from filed if another filed match condition MongoDB

for example i have such structure of document:
{
"_id": "1230987",
"Z": [{
"A": [{
"B": {
"C": [{
"E": "2104331180",
"D": "boroda.jpg"
}, {
"E": "1450987095",
"D": "small.PNG"
}]
},
}],
}]
}
How could i get value from field E if value in field D matches condition ?
Use an $elemMatch project:
db.collection.find({"Z.A.B.C.D":<condition>},{"Z,A,B,C":{$elemMatch:{D:<condition>} }})
Playground