Home > Software engineering >  fix records that contain the same field in MongoDB
fix records that contain the same field in MongoDB

Time:03-09

Incorrect records were created due to an error in the system.

Current :

{
   "_id" : ObjectId("5cb48b1875aca0626d0272db"),
   "similiarModels" : [
                 ObjectId("5cb48b1875aca0626d0272db"), -> the same as the _id field of the object, it must be removed from the array
                 ObjectId("9fg48f5325qwa0436h9433ae")
    ]
    "topModel" : ObjectId("5cb48b1875aca0626d0272db")  -> the same as the _id field of the object, should be removed
}

I want to :

{
   "_id" : ObjectId("5cb48b1875aca0626d0272db"),
   "similiarModels" : [
                 ObjectId("9fg48f5325qwa0436h9433ae")
    ]
}

CodePudding user response:

Here is a solution. Note the use of the pipeline form of update and the $$REMOVE special value.

db.foo.update(
    {},  // no filter; get all docs                                                           
    [ // use pipeline form of update expression for greater flexibility!
        {$set: {
          'similarModels': {$setDifference:['$similarModels', ['$_id'] ]},
          'topModel': {$cond: [
            {$eq:['$topModel','$_id']},  // if                                                
            '$$REMOVE',  // then get rid of field                                             
            '$topModel'  // else set it back to original                                      
          ]}
        }}
    ],
    {multi:true}
);

Alternatively, one can use the "merge onto self" capability introduced in v4.4. This allows aggregate to act as a giant update. See caveat in comments below:

db.foo.aggregate([
    // EXACT same expression as pipeline update above; nice.
    {$set: {
        'similarModels': {$setDifference: ['$similarModels', ['$_id'] ]},
        'topModel': {$cond: [
            {$eq:['$topModel','$_id']},  // if                                                
            '$$REMOVE',  // then get rid of field                                             
            '$topModel'  // else set it back to original                                      
        ]}
    }},

    // Sadly, the whenMatched:'merge' option will not "unset" fields; it only
    // adds or overwrites fields so this approach will not work for $topModel field.                                    
    // We can, however, use whenMatched:'replace' but this might present
    // a performance issue because the entire doc, not just similarModels
    // and topModel, is being written back.                                                                
    {$merge: {
        into: "foo",
        on: [ "_id" ],
        whenMatched: "replace",
        whenNotMatched: "fail"
    }}

]);
  • Related