如何向Mongoose的Campaign.data.actions子数组推送Activity对象?
Got it, let's break down why your current code isn't working and get this fixed properly.
The Issues With Your Current Code
You've got two key problems here that are preventing the update from going through:
filter()returns an array, not a single object: When you runcampaign.data.filter(e => e.profileLink === target.profileLink), you're getting an array of matching elements (even if there's only one). Trying to accessdata.actionseither throws an error or does nothing, since arrays don't have anactionsproperty. You needfind()instead offilter()to grab the single matching item.- Mongoose can't track deep nested changes: Even if you did get the right data item, directly pushing to
actionsand callingsave()might not trigger Mongoose's change detection for deeply nested objects. It won't recognize thedataarray was modified, so it skips updating the database.
Solution 1: Use findOneAndUpdate (Recommended - Atomic Operation)
This is the best approach because it's an atomic database operation (no risk of concurrent updates messing things up) and you don't have to worry about manual change tracking.
Here's how to push the action to the correct actions array in one go:
const updatedCampaign = await Campaign.findOneAndUpdate( { browserKey: browserKey, 'data.profileLink': target.profileLink // Match the campaign with the right data element }, { $push: { 'data.$.actions': action // The $ refers to the first matching data element's position } }, { new: true } // Optional: returns the updated document instead of the original ); if (!updatedCampaign) { // Handle case where no campaign or matching data element was found console.log("Couldn't find the campaign or target data element"); }
If your data array might have multiple elements with the same profileLink (though your schema suggests this shouldn't happen), use arrayFilters to update all matching elements:
const updatedCampaign = await Campaign.findOneAndUpdate( { browserKey: browserKey }, { $push: { 'data.$[elem].actions': action } }, { arrayFilters: [{ 'elem.profileLink': target.profileLink }], new: true } );
Solution 2: Fix Your Original Code
If you prefer to fetch the document first and modify it in code, you need to fix the element lookup and manually trigger change detection:
const campaign = await Campaign.findOne({ browserKey }); if (!campaign) { console.log("Campaign not found"); return; } // Find the single matching data element (use find() instead of filter()) const targetDataItem = campaign.data.find(e => e.profileLink === target.profileLink); if (!targetDataItem) { console.log("Target data element not found"); return; } // Push the action to the actions array targetDataItem.actions.push(action); // Tell Mongoose the data array was modified (critical for deep nested changes) campaign.markModified('data'); // Save the updated document await campaign.save();
Key Notes
- Always prefer Solution 1 for production code—atomic operations are safer and more efficient.
- Make sure your
actionobject matches the structure defined in yourActivityschema, otherwise Mongoose will reject the update.
内容的提问来源于stack exchange,提问作者tpikachu

