how to use $project with condition? - mongodb

I have a collection as below:
[
{
"a": 1,
"b": [
{
"a": 1,
"bb": 10
},
{
"a": 1,
"bb": 11
}
]
},
{
"a": 2,
"b": [
{
"a": 2,
"bb": 20
},
{
"a": 1,
"bb": 21
}
]
},
]
in each row of the top collection I have two keys :
a and b
'b' key is an array of objects with [a, bb] keys.
now, I want to retrieve items of subarray(b) that the key exists in each row.
I want the result to be like this:
[
{
"a": 1,
"bb": 10
},
{
"a": 2,
"bb": 20
},
]
Do you have any idea to solve this?
MongoDB Playground

use this aggregation
in result you have a value and your result as gg if you need something else notify me
db.collection.aggregate([
{
'$project': {
'a': 1,
'gg': {
'$filter': {
'input': '$b',
'as': 'z',
'cond': {
'$eq': [
'$$z.a', '$a'
]
}
}
}
}
}
])

Related

MongoDB - Increment a field using another array field

I have a collection with the document of this structure:
{
"_id": {
"$oid": "63e8af476a3674484ea14888"
},
"my_id": 321123,
"version": 0,
"parameters": [
{"a": 1},
{"a": 1, "b": 2}
]
}
Using a RESTful API the user can change the version field to any number between 0 and len(parameters). Another endpoint let the user push an object to parameters and set version to the new len(parameters) - 1. So using the previous example, after using this endpoint we will get:
{
"_id": {
"$oid": "63e8af476a3674484ea14888"
},
"my_id": 321123,
"version": 2,
"parameters": [
{"a": 1},
{"a": 1, "b": 2},
{"a": 1, "b": 2, "c": 3}
]
}
I tried many things, but nothing seems to work. Here are examples of what I tried:
db.parameters.findOneAndUpdate({"my_id": 321123}, {"$inc":{version: parameters}, "$push":{"a": 1, "b": 2, "c": 3}}, upsert=true)
db.parameters.findOneAndUpdate({"my_id": 321123}, {"$inc":{version: {"$size": parameters}}, "$push":{"a": 1, "b": 2, "c": 3}}, upsert=true)
As your update requires referring to another field, you have to use the update query with the aggregation pipeline.
For the first scenario,
Add a new parameter object by combining it with the current parameters array via $concatArrays.
I don't think that increment is what you need, but rather update the version value via $set stage. An example as below is to compare the version param between the range of 0 and len(parameters), if true, then update with the version param, else update with the size of the parameters array.
db.parameters.update({
"my_id": 321123
},
[
{
"$set": {
parameters: {
"$concatArrays": [
"$parameters",
[
{
"a": 1,
"b": 2,
"c": 3
}
]
]
}
}
},
{
"$set": {
version: {
$cond: {
if: {
$and: [
{
$gt: [
2,
// version
0
]
},
{
$lt: [
2,
// version
{
$size: "$parameters"
}
]
}
]
},
then: 2,
else: {
$size: "$parameters"
}
}
}
},
}
],
{
upsert: true
})
Demo (1st scenario) # Mongo Playground
For the second scenario,
Add a new parameter object by combining it with the current parameters array via $concatArrays.
Set the version with the size of parameters array minus 1.
db.parameters.update({
"my_id": 321123
},
[
{
"$set": {
parameters: {
"$concatArrays": [
"$parameters",
[
{
"a": 1,
"b": 2,
"c": 3
}
]
]
}
}
},
{
"$set": {
version: {
"$subtract": [
{
$size: "$parameters"
},
1
]
}
},
}
],
{
upsert: true
})
Demo (2nd scenario) # Mongo Playground

MongoDB aggregation to change the key based on value in each object

Is there an aggregation query available to convert the following:
after unwind I plan to change the key based on the value inside each object and append it too has---(type)
{
"rows": [
{
"type": "aaa",
"values": [
1,
2,
3
]
},
{
"type": "bbb",
"values": [
4,
5,
6
]
}
]
}
to
{
"hasaaa": {
"type": "aaa",
"values": [
1,
2,
3
]
},
"hasbbb": {
"type": "bbb",
"values": [
4,
5,
6
]
}
}
$map to iterate loop of rows array
$concat to prepare the custom key string
return key-value from map
$arrayToObject convert key-value array to object
db.collection.aggregate([
{
$project: {
rows: {
$arrayToObject: {
$map: {
input: "$rows",
in: {
k: { $concat: ["has", "$$this.type"] },
v: "$$this"
}
}
}
}
}
}
])
Playground

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

How to get argmax/argmin of multiple fields simultaneously in mongodb?

Here's the data example I'm working with.
[
{
"uid": "111",
"a": 1,
"b": 3,
"c": 1,
},
{
"uid": "222",
"a": 2,
"b": 2,
"c": 2
},
{
"uid": "333",
"a": 3,
"b": 1,
"c": 3
}
]
Then I want to perform argmax on fields "a" and "b", and argmin on field "c" and return the "uid" as the result.
For example:
For "a", it's maximum value is 3, the corresponding "uid" is "333", so argmax of "a" should be "uid" : "333".
The question is what query should be executed so that I can get the result as below?
[
{
"argmax_of_a": "333",
"argmax_of_b": "111",
"argmin_of_c": "111",
}
]
Here's the code snipped I'm playing with https://mongoplayground.net/p/gEDuHd-aCiZ
I can find someway to get argmax/argmin of one specific field, but I have no idea how to work on multiple fields simultaneously.
Thanks in advance!
give this aggreation pipeline a try:
db.collection.aggregate(
[
{
$group: {
_id: null,
a: { $push: { uid: '$uid', val: '$a' } },
b: { $push: { uid: '$uid', val: '$b' } },
c: { $push: { uid: '$uid', val: '$c' } }
}
},
{
$project: {
_id: 0,
max_of_a: { $arrayElemAt: ["$a", { $indexOfArray: ["$a.val", { $max: '$a.val' }] }] },
max_of_b: { $arrayElemAt: ["$b", { $indexOfArray: ["$b.val", { $max: '$b.val' }] }] },
max_of_c: { $arrayElemAt: ["$c", { $indexOfArray: ["$c.val", { $max: '$c.val' }] }] }
}
},
{
$project: {
arg_max_of_a: '$max_of_a.uid',
arg_max_of_b: '$max_of_b.uid',
arg_max_of_c: '$max_of_c.uid'
}
}
])

Mongodb Update subdocument values matching subdocument and without altering other fields

I have a schema like this
{
"_id": "5f86da4b5bb9a62742371409",
"status" : true,
"doc": [
{
"_id": "5f86cacbe4d7c423f21faf50",
"a": 4,
"b": null
},
{
"_id": "5f86cb01a1ca5124063299c1",
"a": 6,
"b": null
}
]
}
And I have an update in the form of
[
{
"_id": "5f86cacbe4d7c423f21faf50",
"b": 90
},
{
"_id": "5f86cb01a1ca5124063299c1",
"b": 45
}
]
How can I update the collection to end up like this?
{
"_id": "5f86da4b5bb9a62742371409",
"status" : true,
"doc": [
{
"_id": "5f86cacbe4d7c423f21faf50",
"a": 4,
"b": 90
},
{
"_id": "5f86cb01a1ca5124063299c1",
"a": 6,
"b": 45
}
]
}
Basically I want to update the subdocument with specific keys only (leaving other keys intact)
You can use update with aggregation pipeline from MongoDB 4.2,
$map to iterate loop of array doc and inside it iterate through updateDocs array, it will return matching b, and then $mergeObjects will return updated document,
let updateDocs = [
{ "_id": mongoose.Types.ObjectId("5f86cacbe4d7c423f21faf50"), "b": 90 },
{ "_id": mongoose.Types.ObjectId("5f86cb01a1ca5124063299c1"), "b": 45 }
];
db.collection.updateMany({},
[
{
$set: {
doc: {
$map: {
input: "$doc",
as: "d",
in: {
$mergeObjects: [
"$$d",
{
$reduce: {
input: updateDocs,
initialValue: {},
in: {
$cond: [
{ $eq: ["$$this._id", "$$d._id"] },
{ b: "$$this.b" },
"$$value"
]
}
}
}
]
}
}
}
}
}
]
)
Playground