Reputation: 1755
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")
}
}
Upvotes: 3
Views: 2835
Reputation: 2282
Spring MongoDB version.
@Query("{<your query selector>}")
@Update(pipeline = {
"{ $set : {onBoarding.started: "$onBoarding.completed" } }"
})
void update();
Upvotes: 0
Reputation: 14317
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 } } ) )
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" }
}
]
)
Upvotes: 2
Reputation: 386
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
Upvotes: 4