MongoDB - How to modify the "key" element in the document - arrays

I am having the below document structure:
[
{
"network_type": "ex",
"rack": [
{
"xxxx": {
"asn": 111111,
"nodes": {
"business": [
"sk550abcc1eb01.abc.com",
"sk550abcc1eb10.abc.com",
"sk550abcc1eb19.abc.com",
"sk550abcc1eb28.abc.com"
]
},
"region": "ex-01",
"zone": "01a"
}
}
]
}
]
I need to rename/update the key array element "xxxx" to "details".
I tried the below command, but it doesn't seem to work.
db.collection.update({},
{
$rename: {
"rack.xxxx": "details"
}
})
Link: https://mongoplayground.net/p/9dcDP-VKZ55
Please help me.

You can't direct $rename the field name which is within the array.
Instead,
Iterate with document(s) in the rank array, create the details field with the value of xxxx and next append this field to each document.
Remove the path with $rank.xxxx to remove the xxxx field from the document(s) in the rank array.
db.collection.update({},
[
{
$set: {
rack: {
$map: {
input: "$rack",
in: {
$mergeObjects: [
{
"details": "$$this.xxxx"
},
"$$this"
]
}
}
}
}
},
{
$unset: "rack.xxxx"
}
])
Sample Mongo Playground

Related

Mongo Query to modify the existing field value with new value + array of objects

I want to update many documents based on the condition in MongoDB.
MODEL is the collection which has the document with below information.
"info": [
{
"field1": "String1",
"field2": "String2"
},
{
"field1": "String1",
"field2": "String_2"
}
],
"var": "x"
I need to update all the "String1" value of field1 with "STRING_NEW". I used the below query to update but not working as expected.
db.model.updateMany(
{ "info.field1": { $exists: true } },
[
{ "$set": {
"info": {
"$map": {
"input": "$info.field1",
"in": {
"$cond": [
{ "$eq": ["$$this.field1", "String1"] },
"STRING_NEW",
$$this.field1
]
}
}
}
} }
]
)
Please have a look and suggest if anything is to be modified in the above query.
Solution 1
With the update with aggregation pipeline, you should iterate the object in info array and update the iterated object by merging the current object with new field1 field via $mergeObjects.
db.model.updateMany({
"info.field1": "String1"
},
[
{
"$set": {
"info": {
"$map": {
"input": "$info",
"in": {
"$cond": [
{
"$eq": [
"$$this.field1",
"String1"
]
},
{
$mergeObjects: [
"$$this",
{
"field1": "STRING_NEW"
}
]
},
"$$this"
]
}
}
}
}
}
])
Demo Solution 1 # Mongo Playground
Solution 2
Can also work with $[<identifier>] positional filtered operator and arrayFilters.
db.model.updateMany({
"info.field1": "String1"
},
{
"$set": {
"info.$[info].field1": "STRING_NEW"
}
},
{
arrayFilters: [
{
"info.field1": "String1"
}
]
})
Demo Solution 2 # Mongo Playground

Update nested array object value by replacing substring of that object property

I have the following set of data in my MongoDB collection called orders:
{
"_id" : ObjectId("618e0e1b17687316dcdd6246"),
"groupUID": "abc",
"orderData" : {
"charges" : {
"total" : 18480.0,
"subtotal" : 13980.0
},
"items" : [
{
"name" : "Chocolate cookies",
"imageURL": "domainURL2.com/cookies"
},
{
"name" : "Chocolate muffins",
"imageURL": "domainURL2.com/muffins"
}
]
}
}
Now I want to update the imageURL substring part of "domainURL2" to "domainURL1" field in every document of this collection. I have the following query so far:
db.orders.update(
{
"groupUID" : "abc"
},
{ "$set": { "orderData.items.$.imageURL":"myURL.com" } } )
I also have the query in JavaScript form but I want this to be in pure Mongo query. So the query below will not do it for me unfortunately.
db.getCollection("orders").find({"groupUID" : "abc"}).forEach(function(aRow) {
if (aRow.orderDetails !== undefined) {
var updated = false;
aRow.orderData.items.forEach(function(item) {
item.imageURL = item.imageURL.replace("eddress/", "noknok-app/");
})
db.getCollection("orders").save(aRow);
}
});
I want to update all records' imageURL field's substring part. I am unable to figure out the rest of the query. Can anyone please help me?
My answer may look complex (Welcome for suggestion/improvement).
Work the update with Aggegration Pipeline.
$set - Update orderData.items field.
1.1. $map - Iterate orderData.items and returns new array.
1.1.1. $mergeObjects - Merge current object and imageURL field from 1.1.1.1.
1.1.1.1. $cond - With $regexMatch to find the imageURL starts with "domainURL2.com".
1.1.1.2. If true, then replace "domainURL2.com" with "domainURL1.com".
1.1.1.3. If false, remain existing value.
db.collection.update({
"groupUID": "abc"
},
[
{
"$set": {
"orderData.items": {
$map: {
input: "$orderData.items",
in: {
$mergeObjects: [
"$$this",
{
imageURL: {
$cond: {
if: {
$regexMatch: {
input: "$$this.imageURL",
regex: "^domainURL2.com"
}
},
then: {
$concat: [
"domainURL1.com",
{
$arrayElemAt: [
{
$split: [
"$$this.imageURL",
"domainURL2.com"
]
},
-1
]
}
]
},
else: "$$this.imageURL"
}
}
}
]
}
}
}
}
}
])
Sample Mongo Playground
Another approach is using $replaceOne (suggested by #rickhg12hs) which will be much easier.
$replaceOne to replace for 1.1.1.1.
db.collection.update({
"groupUID": "abc"
},
[
{
"$set": {
"orderData.items": {
$map: {
input: "$orderData.items",
in: {
$mergeObjects: [
"$$this",
{
imageURL: {
$replaceOne: {
input: "$$this.imageURL",
find: "domainURL2.com",
replacement: "domainURL1.com"
}
}
}
]
}
}
}
}
}
])
Sample Mongo Playground ($replaceOne)

MongoDB - Iterate to Array of objects and update the field as array

I am new to MongoDB. I have a collection that has multiple documents in which a particular document has a nested array structure. I need to iterate through this nested array and change the data type of the value of the iterated field.
Nested Array structure:
[
{
"identifier":{
"type":"xxxx",
"value":"1111"
},
"origin":"example.com",
"score":8.0
},
{
"identifier":{
"type":"yyyyy",
"value":"222"
},
"origin":"example.com",
"score":8.0
},
{
"identifier":{
"type":"zzzzz",
"value":"3333"
},
"origin":"https://olkdghf.com",
"score":8.0
}
]
The problem is I need to change the datatype without replacing the existing field value. But I am getting a new empty value instead of the original value.
My query:
db.SourceEntityv8test.find({"hasIdentifier.identifier.type": {$exists:true}}).sort({_id:1}).skip(0).limit(100).forEach(function(x)
{
db.SourceEntityv8test.update({_id: x._id}, {$set:{"hasIdentifier.$[].identifier.type":[]}} );
});
Expected output:
[
{
"identifier":{
"type":[xxxx],
"value":"1111"
},
"origin":"example.com",
"score":8.0
},
{
"identifier":{
"type":[yyyyy],
"value":"222"
},
"origin":"example.com",
"score":8.0
},
{
"identifier":{
"type":[zzzzz],
"value":"3333"
},
"origin":"example.com",
"score":8.0
}
]
Achieved output:
[
{
"identifier":{
"type":[],
"value":"1111"
},
"origin":"example.com",
"score":8.0
},
{
"identifier":{
"type":[],
"value":"222"
},
"origin":"example.com",
"score":8.0
},
{
"identifier":{
"type":[],
"value":"3333"
},
"origin":"example.com",
"score":8.0
}
]
A bit complex. Expect that you need to achieve it by update with aggregation pipeline.
Concept:
Update whole hasIdentifier array by 1.1.
1.1. Merge each document in hasIdentifier array with 1.1.1.
1.1.1. Merge identifier object with the document with type array.
db.SourceEntityv8test.update({_id: x._id},
[
{
$set: {
hasIdentifier: {
$map: {
input: "$hasIdentifier",
in: {
$mergeObjects: [
"$$this",
{
"identifier": {
$mergeObjects: [
"$$this.identifier",
{
type: [
"$$this.identifier.type"
]
}
]
}
}
]
}
}
}
}
}
])
Sample Mongo Playground

Concat a string prefix to the field of an object in an array in MongoDb

I have many mongoDb documents like so
{
store:"Jacks Pizza",
storeNumbers:[
{
"chef":"Giovanni",
"number":"7203305544"
}
]
},
store:"Felicias Kitchen",
storeNumbers:[
{
"chef":"Gina",
"number":"+19161214594"
}
]
I would like to append a "+1" prefix to all such numbers that don't have a +1 country code attached to them.
Here's what I have tried-
db.users.updateMany({
"storeNumbers.number" : {
$exists: true,
$ne:"",
$regex: /^(?!\+)^(?![a-zA-Z])/
}
},
[ {
$set : {
"storeNumbers.$.number" : {
"$concat": [ "+1" , "$storeNumbers.$.number"]
}
}
}
]
);
This gives me an error saying that I cannot perform concat on elements in an array.
How would you do this?
There is no straight way to do this, you can use update with aggregation pipeline starting from MongoDB 4.2,
match query if regex does not match "+1" string at the start of the number
$map to iterate loop of storeNumbers
$cond check condition if number does not match "+1" string at the start of the number and number field is not string and string type then concat "+1" before number using $concat otherwise do nothing
$mergeObjects to merge current object with new update number field
db.users.updateMany({
"storeNumbers.number": {
$exists: true,
$ne: "",
$not: {
$regex: "^\\+1"
}
}
},
[
{
$set: {
storeNumbers: {
$map: {
input: "$storeNumbers",
in: {
$mergeObjects: [
"$$this",
{
$cond: [
{
$and: [
{
$not: {
$regexMatch: {
input: "$$this.number",
regex: "^\\+1"
}
}
},
{
$ne: ["$$this.number", ""]
},
{
$eq: [{ $type: "$$this.number" }, "string"]
}
]
},
{
number: {
$concat: ["+1", "$$this.number"]
}
},
{}
]
}
]
}
}
}
}
}
])
Playground

Update array content within another array that don't have key

I have mongoDB content as below:
[
{
"_id":{
"$oid":"57c6699711bd6a0976cabe8a"
},
"ID":"1111",
"FullName":"AAA",
"Category":[
{
"CategoryId":{
"$oid":"57c66ebedcba0f63c1ceea51"
},
"_id":{
"$oid":"57e38a8ad190ea1100649798"
},
"Value":[
{
"Name":""
}
]
},
{
"CategoryId":{
"$oid":"57c3df061eb1e59d3959cc40"
},
"_id":{
"$oid":"57e38a8ad190ea1100649797"
},
"Value":[
[
"111",
"XXXX",
"2005"
],
[
"1212",
"YYYY",
"2000"
],
[
"232323",
"ZZZZZ",
"1999"
]
]
}
]
},
{
"_id":{
"$oid":"57c6699711bd6a0976cabe8a"
},
"ID":"1111",
"FullName":"BBB",
"Category":[
{
"CategoryId":{
"$oid":"57c66ebedcba0f63c1ceea51"
},
"_id":{
"$oid":"57e38a8ad190ea1100649798"
},
"Value":[
{
"Name":""
}
]
},
{
"CategoryId":{
"$oid":"57c3df061eb1e59d3959cc40"
},
"_id":{
"$oid":"57e38a8ad190ea1100649797"
},
"Value":[
[
"4444",
"XXXX",
"2005"
],
[
"7777",
"GGGG",
"2000"
],
[
"8888",
"ZZZZZ",
"1999"
]
]
}
]
}
]
Here I have an array named 'Category' where it contains objects with different category id.
I need to
select a particular category id - '57c3df061eb1e59d3959cc40'
From the above selected Category, we get 'Value' array
From Value array need to find if the second value is equal to 'ZZZZZ' ie. value[1] == 'ZZZZZ'
And now, update the matched value arrays with a new value at the end
Eg:
[
"232323",
"ZZZZZ",
"1999"
]
should be updated to
[
"232323",
"ZZZZZ",
"1999",
"update1"
]
and
[
"8888",
"ZZZZZ",
"1999"
]
should be updated to
[
"8888",
"ZZZZZ",
"1999",
"update1"
]
I have tried as below:
resume.update({
"Category.CategoryId": new ObjectId('57c3df191eb1e59d3959cc43'),
"Category.Value.$.1": 'ZZZZZ'
},
{"$set": {"Category.Value.$.3": "update1"}
}, function(err, resData){
res.send(resData);
});
But, nothing gets updated. Its there any way to get this work. Please help to update the inner array.
Thanks in advance.
Your goal is not possible at the moment since you need to update two positional elements.
There is a JIRA trackable for the sort of behaviour you want here: https://jira.mongodb.org/browse/SERVER-831
It's a problem since you need to match two elements positions:
the Category element with the matched CategoryId
the Value element in the Value array of arrays
If one of these wouldn't be an array it would have been possible.
Anyway, Your update try above was wrong. IF this feature was possible (and it is not!!!) it would have been something like this:
db.resume.update(
{
Category: {
$elemMatch: {
CategoryId: ObjectId('57c3df061eb1e59d3959cc40'),
Value: {
$elemMatch: {
'1': 'ZZZZZ'
}
}
}
}
},
{
$push: {
'Category.$.Value.$': 'update1'
}
}
)
The positional $ operator should be used during the update and not the find like you did, and it will update the first element that matched the query.
Doing the above will return the error:
Too many positional (i.e. '$') elements found in path 'Category.$.Value.$'
Because of the missing feature I explained at the top.
So, currently (version 3.2) you will not be able to do this unless you change your schema.

Resources