Appending a string to all values in an array field in MongoDB

Viewed 87

I have a collection filled with docs that contain an "ip_addresses" field, which is an array of strings (IPs). I want to append '/32' to all of these values in all of my docs that don't already have a CIDR range suffix.

Here's my issue:

  1. I don't know how to use the current value of the IP which is being iterated on.
  2. Even if I did, $concat doesn't seem to work and throws an error even with placeholder values (as in the query below) - The dollar ($) prefixed field '$concat' in 'ip_addresses.0.$concat' is not valid for storage.

Here is my current query which throws the error:

db.getCollection('docs').update(
    {},
    { $set: { "ip_addresses.$[ip]": { "$concat": [ "1.1.1.1", "/32" ] } } },
    {
        arrayFilters: [ { "ip": { $not: /.+\/\d{1,2}/ } } ],
        multi: true
    }
)

I'd appreciate help using the current values in the array in the $concat command and resolving the error.

1 Answers

Let's tackle each of the problems.

2 - $concat is an aggregation operator, hence it cannot be used for an update. you can view the list of available operators for an update here. You might notice that none of these "work" on actual document values which brings me back to the first question you had.

1 - In order you use a current document values within an update you have to use pipelined updates which is only available for Mongo v4.2+, if you're using a lesser Mongo version you have to fetch the documents into memory and do the update one by one in code. If you are on Mongo v4.2+ then the pipeline update syntax goes like this:

db.collection.updateMany(
    {ip_addresses: {$exists: true}},
    [
        {
            $set: {
                ip_addresses: {
                    $map: {
                        input: '$ip_addresses',
                        as: 'ipAddress',
                        in: {
                            $cond: [
                                {
                                    $regexMatch: {input: "$$ipAddress", regex: /.+\/\d{1,2}/}

                                },
                                '$$ipAddress',
                                {
                                    $concat: [
                                        '$$ipAddress',
                                        '/32'
                                    ]
                                }
                            ]
                        }
                    }
                }
            }
        }
    ]
);
Related