I have a collection with documents with the following structure:
{
"_id": ObjectId("..."),
"rides": [
{
status_history: ["status1", "status2", "status3"]
},
{
status_history: ["status4", "status5"]
}
...
]
}
What I want to do is append a new status to the status_history
array of the the last ride in the rides
array, achieving something like:
{
"_id": ObjectId("..."),
"rides": [
{
status_history: ["status1", "status2", "status3"]
},
{
status_history: ["status4", "status5", "NEW_STATUS_HERE"]
}
...
]
}
But I can't quite figure out how to do that. I'm using the mongo-driver
package to connect to the db.
Here's the code I managed to come up with:
filter := bson.M{"_id": objectId}
arrayFilters := options.ArrayFilters{
Filters: bson.A{
bson.M{"s": bson.M{"rides": bson.A{"$slice", -1}}},
},
}
upsert := true
opts := options.UpdateOptions{
ArrayFilters: &arrayFilters,
Upsert: &upsert,
}
update := bson.M{
"$push": bson.M{
"rides.$[s].status_history": statusEntry,
},
}
result, err := col.UpdateOne(ctx, filter, update, &opts)
fmt.Println(fmt.Sprintf(
"Matched %v, Modified %v, Upserted %v",
result.MatchedCount,
result.ModifiedCount,
result.UpsertedCount,
))
The output of that Println
is Matched 1, Modified 0, Upserted 0
. Indeed, inspecting the item in the collection shows that it is unchanged.
What exactly am I getting wrong here?
CodePudding user response:
This Update With Aggregation Pipeline (feature requires MongoDB v4.2 or higher) will update the last element (nested document) of the rides
array, with a new status value added to the status_history
array field. The update runs in mongo
shell.
let NEW_VALUE = "new_status_999";
db.test.updateOne(
{ _id: 1 },
[
{
$set: {
rides: {
$concatArrays: [
{ $slice: [ "$rides", { $subtract: [ { $size: "$rides" }, 1 ] } ] },
[ {
$let: {
vars: { lastEle: { $arrayElemAt: [ { $slice: [ "$rides", -1 ] }, 0 ] } },
in: {
$mergeObjects: [
"$$lastEle",
{ status_history: { $concatArrays: [ "$$lastEle.status_history", [ NEW_VALUE ] ] } }
]
}
}
}]
]
}
}
}
])
The updated document:
{
"_id" : 1,
"rides" : [
{
"status_history" : [
"status1",
"status2",
"status3"
]
},
{
"status_history" : [
"status4",
"status5",
"new_status_999" // <--- the update result
]
}
]
}
CodePudding user response:
Query
- pipeline update requires MongoDB >= 4.2
- find the size of the array
- if size>1 slice to get the prv else prv=[]
- concat arrays prv with
{"status" "array_with_new_member"}
*i dont use go, for Java the update method accepts a pipeline also, so it works simple, in the worst case you can use it as update command.
update({},
[{"$set":{"size":{"$size":"$rides"}}},
{"$set":
{"rides":
{"$concatArrays":
[{"$cond":
[{"$gt":["$size", 1]},
{"$slice":["$rides", 0, {"$subtract":["$size", 1]}]}, []]},
[{"status_history":
{"$concatArrays":
[{"$cond":
[{"$gt":["$size", 0]},
{"$arrayElemAt":
["$rides.status_history", {"$subtract":["$size", 1]}]},
[]]}, ["new-status"]]}}]]}}},
{"$unset":["size"]}])