简体   繁体   中英

Mongodb updateMany property to exact value of other property

I have documents representing Users with onboarding data stored as a nested object:

{
    "_id" : ObjectId("5c7eb0132e6f793bcc7f4bf7"),
    "userName" : "sample_user_name",
    "onBoarding" : {
        "completed" : ISODate("2019-03-05T17:46:28.803Z"),
        "stepId" : 8,
        "started" : null
    }
}

But due to a bug we are missing a date when onboarding was started, I would like to "retrieve" this information by running an update operation where "started" will be set to the same date as "completed". I have a query like:

db.getCollection('user').updateMany(
{ 
  $and: [
      {"onBoarding.started": {$exists: false}},
      {"onBoarding.completed": {$exists: true}}
]}, 
{
  $set: { "onBoarding.started": "$onBoarding.completed" }
})

This however, sets "started" to "$onBoarding" literally (as a string).

{
    "_id" : ObjectId("5c7eb0132e6f793bcc7f4bf7"),
    "userName" : "sample_user_name",
    "onBoarding" : {
        "completed" : ISODate("2019-03-05T17:46:28.803Z"),
        "stepId" : 8,
        "started" : "$onBoarding"
    }
}

How should I write it for mongo to take a value from "onBoarding.completed" and copy this value to "onBoarding.started"?? Expected result document should look like:

{
    "_id" : ObjectId("5c7eb0132e6f793bcc7f4bf7"),
    "userName" : "sample_user_name",
    "onBoarding" : {
        "completed" : ISODate("2019-03-05T17:46:28.803Z"),
        "stepId" : 8,
        "started" : ISODate("2019-03-05T17:46:28.803Z")
    }
}

The exists operator checks for existence of a field. If a field has a value of null , the query still returns true (because the field still exists, only its value is null).

The following queries behave differently with this input document: { _id: 1, fld1: 123, fld2: null }

  • db.test.find( { fld2: { exists: false } } ) returns false .
  • db.test.find( { fld2: null } } ) returns true .

Coming back to the data in question - the following query / script will update all the documents with following condition: ( "onBoarding.started" is null or the field doesn't exist ) and ( "onBoarding.completed" field exists and is not null ).

db.test.find( { $and: [ { $or: [ { "onBoarding.started": null },  { "onBoarding.started": { $exists: false } } ] },  { $and: [ { "onBoarding.completed": { $exists: true } }, { "onBoarding.completed": { $ne: null } } ] } ] } ).forEach( doc => db.test.updateOne( { _id: doc._id }, { $set: { "onBoarding.started" : doc.onBoarding.completed } } ) )



Using MongoDB version 4.2 , the same update can be run as follows. Note the update uses an Aggregation stage (see documetation on update ).

 db.test.updateMany( { $and: [ { $or: [ { "onBoarding.started": null }, { "onBoarding.started": { $exists: false } } ] }, { $and: [ { "onBoarding.completed": { $exists: true } }, { "onBoarding.completed": { $ne: null } } ] } ] }, [ { $addFields: { "onBoarding.started" : "$onBoarding.completed" } } ] )

You need to use an aggregation pipeline to be able to use the value of another field :

db.user.updateMany(
   { <your query selector > },
   [
      { $set: { onBoarding.started: "$onBoarding.completed" } },
   ]
)

Be aware that here, $set refers to the aggregation pipeline stage and not the update operator $set : https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/index.html#update-with-aggregation-pipeline

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