Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performing Data Cleanup In Mongodb

My application tracks the movements of data throughout the system. When a movement is recorded it is placed in a separate collection that determines whether the document is enroute, available or out of service. I used $addToSet to place the _id, and $pullAll to make sure that when a doc is moved from enroute to available, it is not duplicated. But when the _id is moved to a new location entirely, I need to remove the old data from the old location and insert it into the new location. The insertion works but I cannot figure out how to properly remove the data from the old location. These are all down within Meteor Calls and Mongodb

          if last.status is "Enroute"
            LastLocation.update locationId: last.locationId,partId: last.partId,
                $addToSet:
                    enroutePurchaseIds: lastPurchaseId
                $pullAll:
                    availiblePurchaseIds: lastPurchaseId
                    outOfServicePurchaseIds: lastPurchaseId

like image 559
Geoffrey Hutson Avatar asked Mar 11 '20 17:03

Geoffrey Hutson


1 Answers

Update

You can run the merge command from upcoming 4.4 version which allows updating the same collection the aggregation is running on. Pass the array as old location and new location

db.collection.aggregate([
  {"$match":{"location":{"$in":[oldLocation,newLocation]}}},
  {"$addFields":{"sortOrder":{"$indexOfArray":[[oldLocation,newLocation],"$location"]}}},
  {"$sort":{"sortOrder":1}},
  {"$group":{
    "_id":null,
    "oldLocationDoc":{"$first":"$$ROOT"},
    "newLocationDoc":{"$last":"$$ROOT"}
  }},
  {"$addFields":{
    "oldLocationDoc.old":{
      "$filter":{
        "input":"$oldLocationDoc.old",
        "cond":{"$ne":["$$this",oldLocation]}
      }
    },
    "newLocationDoc.new":{"$concatArrays":["$newLocationDoc.new",[newLocation]]}
  }},
  {"$project":{"locations":["$oldLocationDoc","$newLocationDoc"]}},
  {"$unwind":"$locations"},
  {"$replaceRoot":{"newRoot":"$locations"}},
  {"$merge":{
    "into":{"db":"db","coll":"collection"},
    "on":"_id",
    "whenMatched":"merge",
    "whenNotMatched":"failed"
  }}
]

Original

Not possible to move array/field value from one document to another document in a single update operation.

You would want to use transactions to perform multi document updates in a atomic way. Requires replica set.

var session = db.getMongo().startSession();
var collection = session.getDatabase('test').getCollection('collection');
session.startTransaction({readConcern: {level:'snapshot'},writeConcern: {w:'majority'}});
collection.update({location:oldLocation},{$pull:{availiblePurchaseIds:lastPurchaseId}});
collection.update({location:newLocation},{$push:{enroutePurchaseIds:lastPurchaseId}});
session.commitTransaction()
session.endSession()

Other options would be to perform bulk updates in case of standalone mongod instance.

var bulk = db.getCollection('collection').initializeUnorderedBulkOp();
bulk.find({location:oldLocation}).updateOne({$pull:{availiblePurchaseIds:lastPurchaseId}});
bulk.find({location:newLocation}).updateOne({$push:{enroutePurchaseIds:lastPurchaseId}});  
bulk.execute();
like image 57
s7vr Avatar answered Sep 27 '22 00:09

s7vr