Search code examples
regexmongodbgomongo-go

I am having an issue with replaceAll in MongoDB


I'm trying to append the text "[retrying: " + time.Now().Format("15:04") + "]" in such a way that if it is missing in the field, it will be added; if it is present, it will be replaced.

For example (adding text):

{
  "_id": {
    "$oid": "66f134e7ad3dc5c40ced7b2a"
  },
  "CreatedAt": {
    "$date": "2024-09-23T09:29:11.276Z"
  },
  "UpdatedAt": {
    "$date": "2024-09-24T22:15:50.679Z"
  },
  "Step": "Waiting to receive information from the network"
}

Target:

{
  "_id": {
    "$oid": "66f134e7ad3dc5c40ced7b2a"
  },
  "CreatedAt": {
    "$date": "2024-09-23T09:29:11.276Z"
  },
  "UpdatedAt": {
    "$date": "2024-09-24T22:15:50.679Z"
  },
  "Step": "Waiting to receive information from the network [retrying 12:00]"
}

Or (replacing text)

{
  "_id": {
    "$oid": "66f134e7ad3dc5c40ced7b2a"
  },
  "CreatedAt": {
    "$date": "2024-09-23T09:29:11.276Z"
  },
  "UpdatedAt": {
    "$date": "2024-09-24T22:15:50.679Z"
  },
  "Step": "Waiting to receive information from the network [retrying 12:00]"
}

Target:

{
  "_id": {
    "$oid": "66f134e7ad3dc5c40ced7b2a"
  },
  "CreatedAt": {
    "$date": "2024-09-23T09:29:11.276Z"
  },
  "UpdatedAt": {
    "$date": "2024-09-24T22:15:50.679Z"
  },
  "Step": "Waiting to receive information from the network [retrying 12:05]"
}

Currently I have this code that I am trying with:

fieldName := "$Step" 
retryingRegex := `\s\[retrying:.*\]`
retryingInfo := " [retrying: " + time.Now().Format("15:04") + "]"
update := bson.A{
    bson.M{
        "$set": bson.M{
            "Step": bson.M{
                "$cond": bson.M{
                    "if": bson.M{
                        "$regexMatch": bson.M{
                            "input": fieldName,
                            "regex": retryingRegex,
                        },
                    },
                    "then": bson.M{
                        "$replaceAll": bson.M{
                            "input":       fieldName,
                            "find":        retryingRegex,
                            "replacement": retryingInfo,
                        },
                    },
                    "else": bson.M{
                        "$concat": []string{
                            fieldName,
                            retryingInfo,
                        },
                    },
                },
            },
        },
    },
}
filter := bson.M{"_id": id}
collection.UpdateOne(ctx, filter, update)

But it doesn't seems to work. DB version v7.0.8 Go version 1.22


Solution

  • The problem is that in the $replaceAll operation the find field must be a "plain" string to find (and replace). It can't be a regular expression (more precisely it's not used as a regular expression).

    What you may do is execute a $regexFind to find the part that occurs in the value of the updatable Step field, and replace that.

    But $regexpFind doesn't return the matching substring, it returns an object whose match field contains the matching substring. To get its match field, you may use $getField.

    This is how it may look like (only the "then" branch, the rest is unchanged and omitted for brevity):

    "then": bson.M{
        "$replaceAll": bson.M{
            "input": fieldName,
            "find": bson.M{
                "$getField": bson.M{
                    "field": "match",
                    "input": bson.M{
                        "$regexFind": bson.M{
                            "input": fieldName,
                            "regex": retryingRegex,
                        },
                    },
                },
            },
            "replacement": retryingInfo,
        },
    },
    

    Note that personally I would just store retryingInfo in a separate field, which would result in much simpler and cleaner code. Even that formatting is unnecessary, just store the timestamp, and it can be formatted when displayed to the user.