Push to array based on other attributes - arrays

Object structure (example only, there are thousands of callbacks in one object):
{
"_id" : ObjectId("5ce82dd1eed5a17f33ec025a"),
"id" : "AAA",
"pac" : "",
"callbacks" : [
{
"_id" : ObjectId("5ce82e29eed5a17f33ec0287"),
"timestamp" : 1558720040,
"type" : "data_bidir",
"seqNumber" : 49,
"messages" : [
{
"isCode" : true,
"_id" : ObjectId("5ce82e29eed5a17f33ec0294"),
"key" : "action",
"value" : "extra_long_press"
},
{
"isCode" : false,
"_id" : ObjectId("5ce82e29eed5a17f33ec0293"),
"key" : "version",
"value" : "6"
},
{
"isCode" : false,
"_id" : ObjectId("5ce82e29eed5a17f33ec0292"),
"key" : "firmware",
"value" : "0.45"
}
],
"created" : ISODate("2019-05-24T17:47:21.476Z")
},
{
"_id" : ObjectId("5ce82e42eed5a17f33ec0295"),
"timestamp" : 1558720040,
"type" : "service_geoloc",
"seqNumber" : 49,
"messages" : [
{
"isCode" : true,
"_id" : ObjectId("5ce82e42eed5a17f33ec029b"),
"key" : "lqi",
"value" : "Good"
},
{
"isCode" : false,
"_id" : ObjectId("5ce82e42eed5a17f33ec029a"),
"key" : "latitude",
"value" : "50.67593721530616"
},
{
"isCode" : false,
"_id" : ObjectId("5ce82e42eed5a17f33ec0299"),
"key" : "longitude",
"value" : "14.03118116624828"
},
{
"isCode" : false,
"_id" : ObjectId("5ce82e42eed5a17f33ec0298"),
"key" : "radius",
"value" : "8514"
},
{
"isCode" : true,
"_id" : ObjectId("5ce82e42eed5a17f33ec0297"),
"key" : "da_source",
"value" : "2"
},
{
"isCode" : true,
"_id" : ObjectId("5ce82e42eed5a17f33ec0296"),
"key" : "da_status",
"value" : "1"
}
],
"created" : ISODate("2019-05-24T17:47:46.547Z")
}
]
}
I need to push object to all messages only if a) callbacks.type is "service_geoloc" and b) if messages contains key = da_source and value = 2 (in sample data - the second callback would be affected).
The condition a) is easily managed by arrayFilters:
db.getCollection("devices").updateMany(
{ id: 'AAA'},
{ $push: {
"callbacks.$[c].messages" : {
$each: [{key: "action", value: "atlas_position_network", isCode: true}],
$position: 0
}
}
},
{ arrayFilters: [{"c.type": "service_geoloc"}], multi: true}
)
But I cannot figure out how to apply the condition b). I also tried elemMatch - it worked with both conditions but it updated only one message.

I was able to do it in 2 steps. Maybe it is impossible to do it in one step using shell.
Step 1 = push the item into messages array with general value:
db.getCollection("devices").updateMany(
{ id: 'AAA'},
{ $push: {
"callbacks.$[c].messages" : {
$each: [{key: "action", value: "atlas_position_network", isCode: true}],
$position: 0
}
}
},
{ arrayFilters: [{"c.type": "service_geoloc"}], multi: true}
)
Step 2 = Update the value based on other item on fixed position:
db.getCollection('devices').updateMany(
{ id: 'AAA' },
{ $set: { "callbacks.$[c].messages.0.value": "atlas_position_wifi"} },
{ arrayFilters: [{ "c.type": "service_geoloc", "c.messages.5.value": "6", "c.messages.5.key": "da_source" }], multi: true }
)

Related

How can I specific array values as out as given below?

I was get an result of aggregate query from collection using $facet and $group aggregate but our development team does not satisfies with this result they are asked result must not be an objects given end
{
"_id" : ObjectId("6316e1b9a9ac1f9acceaf488"),
"status_counts" : [
{
"_id" : {
"status" : "Closed"
},
"count" : 59662.0
},
{
"_id" : {
"status" : "Open"
},
"count" : 695.0
},
{
"_id" : {
"status" : "Pending"
},
"count" : 4039.0
}
],
"Priority_counts" : [
{
"_id" : {
"Priority" : "Escalated"
},
"count" : 4722.0
},
{
"_id" : {
"Priority" : "Low"
},
"count" : 11.0
},
{
"_id" : {
"Priority" : "High"
},
"count" : 1.0
},
{
"_id" : {
"Priority" : "Medium"
},
"count" : 1.0
}
],
"assigned" : [
{
"_id" : true,
"count" : 4351.0
},
{
"_id" : false,
"count" : 383.0
}
],
"transfered" : [
{
"_id" : true,
"count" : 245.0
}
],
"cc" : [
{
"_id" : true,
"count" : 4.0
}
]
}
===================================================================================
From the above document expected output as below.. can anyone suggest me please..
{
"_id" : ObjectId("6316e1b9a9ac1f9acceaf488"),
"status_counts" : [
{
"status" : "Closed",
"count" : 59662.0
},
{
"status" : "Open"
"count" : 695.0
},
{
"status" : "Pending"
"count" : 4039.0
}
],
"Priority_counts" : [
{
"Priority" : "Escalated"
"count" : 4722.0
},
{
"Priority" : "Low"
"count" : 11.0
},
{
"Priority" : "High"
"count" : 1.0
},
{
"Priority" : "Medium"
"count" : 1.0
}
],
"assigned" : [
{
"_id" : true,
"count" : 4351.0
},
{
"_id" : false,
"count" : 383.0
}
],
"transfered" : [
{
"_id" : true,
"count" : 245.0
}
],
"cc" : [
{
"_id" : true,
"count" : 4.0
}
]
}

Update value of key in Object in nested array of objects in MongoDB

I am trying to update data of "array1.array2._id": ObjectId("627a6fab60dc3c523b396af1") and Set Name to John But it's updating in all array2's first element's name to John.
db.getCollection('tests')
.updateOne({ "array1.array2._id": ObjectId("627a6fab60dc3c523b396af1") },{ $set: { "array1.$[].array2.$.name" : "John" } })
{
"_id" : ObjectId("627a6fab60dc3c523b396aec"),
"array1" : [
{
"array2" : [
{
"_id" : ObjectId("627a6fab60dc3c523b396af1"),
"name" : "test"
},
{
"_id" : ObjectId("627a6fab60dc3c523b396af2"),
"name" : "ABC"
}
],
"_id" : ObjectId("627a6fab60dc3c523b396aed")
},
{
"array2" : [
{
"_id" : ObjectId("627a6fab60dc3c523b396af3"),
"name" : "XYZ"
},
{
"_id" : ObjectId("627a6fab60dc3c523b396af4"),
"name" : "Testing"
}
],
"_id" : ObjectId("627a6fab60dc3c523b396aee")
}
]
}
Based on this great answer by #R2D2, you can do:
db.collection.update({
"array1.array2._id": ObjectId("627a6fab60dc3c523b396af1")
},
{
$set: {
"array1.$[].array2.$[y].name": "John"
}
},
{
arrayFilters: [
{
"y._id": ObjectId("627a6fab60dc3c523b396af1")
}
]
})
As you can see on this playground example

How to make new array from another array during aggregation?

I have following document:
{
"subscriptionIds" : [
ObjectId("60c312c6dbb5a49fbbf560ea")
],
"gps" : {
"type" : "Point",
"coordinates" : [
23.942706,
54.932539
]
},
"online" : false,
"suspended" : false,
"hwModel" : "",
"fw" : "",
"lastSeen" : ISODate("2021-06-16T04:43:36.682Z"),
"lastSimRequest" : ISODate("2021-06-16T04:34:59.749Z"),
"lastLocation" : "LT",
"lastLocationType" : "gps",
"createdAt" : ISODate("2021-05-20T10:37:16.025Z"),
"updatedAt" : ISODate("2021-06-11T07:37:56.981Z"),
"notes" : "",
"psk_seed" : "QTAebOeNP4nIs-JJSNNlkAQ78N_VaxOq98-_lQPCyZQ=",
"lastOnline" : ISODate("2021-06-15T08:01:59.886Z"),
"lastOffline" : ISODate("2021-06-16T04:43:36.682Z"),
"onlineReason" : "deviceOnlineStatusFromAC",
"offlineReason" : "deviceOfflineStatusTimeout",
"allocationSettings" : "dataplan",
"subscriptionDataplans" : [
{
"_id" : ObjectId("5fae82fc1224cc8d62b5bf17"),
"organizationId" : ObjectId("5dd63d1c1d042f3018e8374e"),
"organizationName" : "",
"name" : "Naujas plan Telia 75GB",
"enabled" : true,
"contractsId" : [
ObjectId("5e32847ab8013befcc14bb1b"),
ObjectId("5e32847ab8013befcc14bb1b")
],
"simQuota" : 0,
"periodQuota" : NumberLong(0),
"allocationRules" : null,
"createdAt" : ISODate("2020-11-13T12:58:36.650Z"),
"updatedAt" : ISODate("2021-06-14T08:08:28.728Z"),
"notes" : "",
"allowRoaming" : false,
"enablePriorityOrdering" : false,
"priorityOrdering" : ""
},
{
"_id" : ObjectId("5fcf25662b1c7d9bab8c1f7d"),
"organizationId" : ObjectId("5dd63d1c1d042f3018e8374e"),
"organizationName" : "",
"name" : "London test",
"enabled" : true,
"contractsId" : [
ObjectId("5e5dfea1efcf754767408eae")
],
"simQuota" : 0,
"periodQuota" : NumberLong(0),
"createdAt" : ISODate("2020-12-08T07:04:06.255Z"),
"updatedAt" : ISODate("2021-06-15T09:28:07.472Z"),
"notes" : "",
"allowRoaming" : true,
"enablePriorityOrdering" : false,
"priorityOrdering" : ""
}
],
}
Is there a way to make following array using "_id" and "allowRoaming" fields:
"dataplanRoaming": [
{
"_id" : ObjectId("5fae82fc1224cc8d62b5bf17"),
"allowRoaming" : false,
},
{
"_id" : ObjectId("5fcf25662b1c7d9bab8c1f7d"),
"allowRoaming" : true,
}
]
My best result was, I tried using project, addFields etc still can't get structure which I want. Rest of query works just fine just missing this part
"dataplanRoaming" : [
[
false,
true
],
[
ObjectId("5fae82fc1224cc8d62b5bf17"),
ObjectId("5fcf25662b1c7d9bab8c1f7d")
]
],
I hoped that {$addFields:{dataplanRoaming:["$subscriptionDataplans.allowRoaming", "$subscriptionDataplans._id"]}},
would give me wanted result it just made array with _id and allowRoaming as separates fields?
Is there a way to create my wanted result using aggregation etc?
$map to iterate loop of subscriptionDataplans and return needed feidls
db.collection.aggregate([
{
$addFields: {
dataplanRoaming: {
$map: {
input: "$subscriptionDataplans",
in: {
_id: "$$this._id",
allowRoaming: "$$this.allowRoaming"
}
}
}
}
}
])
Playground

MongoDB - Update field in an array object based on nested array's field value

I am trying to update a field inside array of objects, where field in nested array is equal to a value.
My goal here is to set the picture field a new url, where value field in valueList is oldRed
Product schema:
{
variations: [{
id: 1,
picture: 'https://example.picture.com',
valueList: [{
name: 'color',
value: 'oldRed'
}, {
name: 'size',
value: 'M'
}]
}, {
id: 2,
picture: 'https://example.picture.com',
valueList: [{
name: 'color',
value: 'black'
}, {
name: 'size',
value: 'M'
}]
}]
}
The closest I get is thanks to this answer, where I update all my nested array fields that contains :'oldRed' . But my final goal is to update other field picture, based on nested array field.
db.Product.findOneAndUpdate({
_id: '123'
}, {
$set: {
'variations.$[].valueList.$[nameField].value': 'newRed'
}
}, {
arrayFilters: [{
'nameField.value': 'oldRed'
}],
new: true
}
});
Please try this :
db.Product.findOneAndUpdate(
{ _id: 123 },
{
$set: {
'variations.$[item].valueList.$[nameField].value': 'newRed',
'variations.$[item].picture': 'newURL' // item is each object in variations which is being checked in arrayFilters.
}
},
{
arrayFilters: [{ 'item.valueList.value': 'oldRed' }, { 'nameField.value': 'oldRed' }],
new: true
}
)
Colletion Data :
{
"_id" : 123,
"variations" : [
{
"id" : 1,
"picture" : "https://example.picture.com",
"valueList" : [
{
"name" : "color",
"value" : "oldRed"
},
{
"name" : "size",
"value" : "M"
},
{
"name" : "color",
"value" : "oldRed"
}
]
},
{
"id" : 2,
"picture" : "https://example.picture.com",
"valueList" : [
{
"name" : "color",
"value" : "black"
},
{
"name" : "size",
"value" : "M"
}
]
},
{
"id" : 3,
"picture" : "https://example3.picture.com",
"valueList" : [
{
"name" : "color",
"value" : "oldRed"
},
{
"name" : "size",
"value" : "M"
}
]
}
]
}
Result :
/* 1 */
{
"_id" : 123,
"variations" : [
{
"id" : 1,
"picture" : "newURL",
"valueList" : [
{
"name" : "color",
"value" : "newRed"
},
{
"name" : "size",
"value" : "M"
},
{
"name" : "color",
"value" : "newRed"
}
]
},
{
"id" : 2,
"picture" : "https://example.picture.com",
"valueList" : [
{
"name" : "color",
"value" : "black"
},
{
"name" : "size",
"value" : "M"
}
]
},
{
"id" : 3,
"picture" : "newURL",
"valueList" : [
{
"name" : "color",
"value" : "newRed"
},
{
"name" : "size",
"value" : "M"
}
]
}
]
}

How to update field in document sub-array in mongo

In a database with these documents
{
"_id" : ObjectId("5448f241e47010336375bb21"),
"lid" : "Ridali",
"items" : [
{
"product" : "dogfood", ,
"done" : false
}
],
}
{
"_id" : ObjectId("5448fc15e47010336375bb23"),
"lid" : "Qitula",
"items" : [
{
"product" : "measure kitchen cab",
"done" : false,
"tags" : [
"11x30"
]
},
{
"product" : "radiators",
"done" : true
},
{
"product" : "dogfood",
"done" : true
}
],
}
How would I update dogfood to catfood for lid:"Qitula" ? I have tried variations of
db.lists.update({lid: "Qitula","items: {$elemMatch: {product: "dogfood"}}},{$set: {"items.product": "catfood"}})
without success.
db.lists.update({lid: "Qitula","items.product": "dogfood" }, { $set : { "items.$.product" : "catfood" }})
Check documentation - http://docs.mongodb.org/manual/reference/operator/update/positional/#up.S
To update subdocument you need to use the positional $ operator
db.lists.update({
lid: "Qitula",
items: {$elemMatch: {product: "dogfood"}}
},
{$set: {"items.$.product": "catfood"}
})

Resources