Trigger match update on nested array object property

I am trying to execute a trigger function, on database update, only when a specific field in any of a document’s nested array subarray objects is changed.

in my case i have a document containing the members array, and each member contains an email_statuses array, i would like the trigger to execute only when the property prevalent_status changes in any of the member’s email_statuses

document:

{
    members: [
         { 
               _id: ....,
              email_statuses: [
                      {
                            _id: ....,
                           prevalent_status: 'accepted'
                      }
              ]
          }
     ]
}

I have understood that the updateDescription updatedFields key will contain the indexes of the arrays that hold the element that is updated, therefor i can’t set a $match expression as follows:

updateDescription.updatedFields.members.email_statuses.prevalent_status (doesn’t work)

Is there a way to formulate the match expresison to achieve what i am trying to do?

Do you have any sense of how much load you expect the trigger to have? I generally think that unless this is a trigger that is incredibly performance-sensitive (thousands per second), you will be better off doing your filtering within the function code itself. The reason is that different kinds of operations will appear in the ChangeEvent differently (adding new elements to the list, updating an element, removing an element, unsetting the field, truncating the array, etc).

For these reasons, defining the logic in simple JS code might be a lot easier for you if that is acceptable.

If not, and you know that the types of operations being made to the document are controlled entirely by you and unlikely to change, I think the best thing to do is to first make the code for your function console.Log(EJSON.Stringify(changeEvent.updateDescription), then remove the match expression and find a few example Change Events that you would like the event to fire for. Then if you want to post them here I can try to help formulate an expression, but without seeing the exact events its hard to write an expression on them.

Also, wanted to post this link in case you hadn’t read through it: https://www.mongodb.com/docs/atlas/app-services/triggers/database-triggers/#use-match-expressions-to-limit-trigger-invocations

Best,
Tyler

Thank you for your response Tyler, atm I am s truggling to get the actual update operations working, (opened another thread about it).
As soon as i solve that and have the updates working i’ll get back to this topic and paste some updateFields examples.

A

Here a few examples of the updateFields

{
	"updatedFields": {
		"members.1.email_status.5.prevalent_status": "ACCEPTED",
		"members.1.email_status.5.statuses.accepted": {
			"at": "2023-06-16 16:29:49",
			"id": "AAAAAQAAKY4"
		}
	}
}
{
	"updatedFields": {
		"members.2.email_status.5.prevalent_status": "SENT",
		"members.2.email_status.5.statuses.sent": {
			"at": "2023-06-16 16:29:47"
		}
	}
}

The trigger should fire only when pervalent_status or any param inside statutes change within a email_status of a member

Hi, I played around with this a bit and you can accomplish this via a normal aggregation pipeline like this:

[
  {
    $addFields:
      {
        updatedFieldArr: {
          $objectToArray: "$updateDescription.updatedFields",
        },
      },
  },
  {
    $match:
      {
        $or: [
          {
            "updatedFieldArr.k": {
              $regex:
                "members.[0-9]+.email_status.[0-9]+.prevalent_status",
            },
          },
          {
            "updatedFieldArr.k": {
              $regex:
                "members.[0-9]+.email_status.[0-9]+.statuses",
            },
          },
        ],
      },
  },
]

Therefore, I think you can add the first bit to the “project” and the second bit to the “match expression”. I have not tried this on a trigger through admitedly, so let me know if this works?

As an aside, I found the following process helpful for this:

  1. Download MongoDB Compass
  2. Insert some documents into a collection in the shape of your change events
  3. Use the aggregation builder

Best,
Tyler

Seems like I am unable tu use this inside the trigger match expression in the MongoDB realm gui

Hi, it does seem like it is possible to do if you manage your own change stream and use the “watch()” API, but unfortunately, triggers allow you to specify a Match and a Project, but we apply the Project after the Match (and in your case, you want the opposite). I think given this complexity I would once again push you to either:

  1. Modify your data model to make these updates clearer (consider a new field that can be used as a signal for the trigger to fire)
  2. Do not use a Match Expression, and just write your function code to early exit on events it does not care about.

Let me know if that works for you, I think its the best option long term since it lets you more clearly define what event you want to react to in code as opposed to aggregation expressions.

Ty Tyler, now that I can confirm that it can’t be handled as I hoped, I will have to test the cost of allowing the trigger to always initiate (it is bound to aws lambda do every trigger even if useless will have a cost) or if to modify data model

A

1 Like

I figure out that you can filter stream like this:
entry is an array, sames goes for changes…

{
“fullDocument.entry.changes.value.messages.type”: “text”
}

but it works only if you know the equal value.

type = text
type = image
etc.

to check for NULL or NOT Null didnt work for me, using normal aggregation didnt work.

@Tyler_Kaye you dont need the exact events, he showed you simple example nested array, come on.