简体   繁体   中英

Update a field in all documents and embedded documents based on matching query MongoDB NodeJS

I have pretty complex query that I am trying to write and can't seem to wrap my head around the best way to write it. Ultimately I am trying to avoid having to write it all out manually. What I am trying to do is to take a dynamic field and go through each document to see if that field exists and if it does then update it. The issue is that the field can exist more than once in document since it can exist on an embedded level in multiple embedded documents per single parent document.

Here is what a typical object would look like:

applications: {
   _id: 5368hss76sd9f,
   ProgramAreaId: 1,
   ProgramArea: 'Education',
   StrategyId: 16,
   Strategy: 'Graduate Program',
   AlternateMapping: [{
         ProgramAreaId: 2,
         ProgramArea: 'Private Sector',
         StrategyId: 13,
         Strategy: 'Higher Education Fund'
   }],
   Funding: [{
         Amount: '500000'
         StrategyId: 16
         Strategy: 'Graduate Program'
      },
      {
         Amount: '500000'
         StrategyId: 13
         Strategy: 'Higher Education Fund'
      }
   ]
}

I may have several thousand of these that I will need to update at a time. The ultimate goal would be to do it in one query. I have made it work for a single field at the base level but was wondering if there was a way to make it work for all of the fields that match the dynamic name even in embedded documents.

Here is what I have tried so far:

     var fieldId = obj.Type + 'Id'; //obj.Type could equal 'Strategy' or 'ProgramArea'
     var field = obj.Type;
     var id = 13; //This could be any number of ids and ultimately was what I want to match on.
        var qry = {
            $where: function() {
                var deepIterate = function(o, value) {
                    for (var field in o) {
                        if (field == fieldId && obj[field] == value) {
                            console.log()
                            return true;
                        }
                        var found = false;
                        if (typeof o[field] === 'object') {
                            found = deepIterate(o[field], value)
                            if (found) {
                                return true;
                            }
                        }
                    }
                    return false;
                };
                return deepIterate(this, id)
            }
        };

        var setQry = { $set: {} };
        setQry.$set[field] = obj.Name;//Field here would be 'Strategy' or 'ProgramArea' or any number of other fields I could be updateing and Name is what I want the value of that field to equal.

        mongo.collection('applications').updateMany(qry, setQry, function(err, result) {
            if (err)
                callback(null);
            else
                callback(result);
        });

The above query will find me any 'application' that contains the field name equal to the field name I am asking for and it will even search through embedded documents to see if that field is there. The issue with the above query is that it will only update that field on the parent level rather than updating the children as well.

So I think I have found the best solution to the above code. I created this following code to accomplish the above issue and it works wonderfully!

   var resultsArray = [];
var fieldId = obj.Type + 'Id';
var field = obj.Type;
if (coll == 'statuses') {
    fieldId = "StatusId";
    field = "Status";
}

var altmapField = 'AltMaps.' + fieldId,
    altfundField = 'AltFunds.' + fieldId,
    paymentField = 'Payments.' + fieldId,
    reportField = 'Reports.' + fieldId,
    crosswalkField = 'Crosswalk.' + fieldId,
    regionField = 'RegionBreakdowns.' + fieldId,
    sectorField = 'SectorBreakdowns.' + fieldId;
var qry = [];
qry.push({
    $match: {
        $or: [{
        }, {
        }, {
        }, {
        }, {
        }, {
        }, {
        }, {
        }]
    }
});

qry[0].$match.$or[0][fieldId] = id;
qry[0].$match.$or[1][altmapField] = id;
qry[0].$match.$or[2][altfundField] = id;
qry[0].$match.$or[3][paymentField] = id;
qry[0].$match.$or[4][reportField] = id;
qry[0].$match.$or[5][crosswalkField] = id;
qry[0].$match.$or[6][regionField] = id;
qry[0].$match.$or[7][sectorField] = id;


var cursor = mongo.collection(collectionPrefix + 'applications').aggregate(qry, { allowDiskUse: true, explain: false }, null);

Essentially all I did was build out a query dynamically and then pass that into mongo aggregation and it read it like a champ.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM