How to filter the update not to include a parent field when using change stream in Mongodb

60 views Asked by At

My document example:

{
id: 139234234,
count: 12,
type: apple,
buyer: [
    {name: "peter", price: 1.23, max: 129}, 
    {name: "alex", price: 1.4, "max": 12146}
    ]
}

My update:

db.updateMany({ id: 139234234 }, [
    {
      $set: {
        buyer: {
          $concatArrays: [
            {
              $filter: {
                input: "$buyer",
                cond: {
                    { $ne: ["$$this.name", "john"] },
                },
              },
            },
            [
              {
                name: "john",
                price: 12,
                max: 234,
              },
            ],
          ],
        },
      },
    },
  ]);

I frequently do this kind of update in buyer field, what should I write for the change stream pipeline (db.collection.watch(changeStreamPipeline)) if I want any update not include buyer. For example: The current update description

 updateDescription: {
    updatedFields: {
      'buyer.2.name': "alex",
      'buyer.2.price': 1.4,
      'buyer.2.max': 12146,
      }}

I don't want to receive any message of the updateFields that start with buyer, for exmaple: buyer.2.name, buyer.2.price. I tried the following pipeline, but it only works for the update field that exactly is buyer:

db.collection.watch(
    [
        {
            $match: {
                operationType: "update",
                "updateDescription.updatedFields.buyer": {
                    $exists: false
                }
            }
        }
    ]
)
1

There are 1 answers

4
Tom Slabbaert On BEST ANSWER

What you can do is add an $addFields stage prior to your $match stage, in which we'll remove all "buyer" keys from the object before performing the match stage to see if any other fields were updated,

Here is how I did this using $objectToArray and $regexMatch:

const pipeline = [
    {
        $addFields: {
            "updateDescription.updatedFields": {
                $arrayToObject: {
                    $filter: {
                        input: {
                            $objectToArray: { $ifNull: ["$updateDescription.updatedFields", {}]}
                        },
                        cond: {
                            $not: { $regexMatch: {input: "$$this.k", regex: /^buyer\..*/,} }
                        }
                    }
                }
            }
        }
    },
    {
        $match: {
            $or: [
                {
                    operationType: {$in: ["update", 'replace']},
                    $expr: {
                        $gt: [
                            {$size: {$objectToArray: { $ifNull: ["$updateDescription.updatedFields", {}]}}},
                            0
                        ]
                    }
                },
                {
                    operationType: {$in: ["delete", 'insert']},
                }
            ]
        }
    }
];

const changeStream = collection.watch(pipeline);