Reputation: 1
I try to run a MongoDB query in Golang and getting some errors. I would like to update the field "expiresAt" with a new timestamp if the field "section.gateBack" is equal to 0.
The data model looks like this:
{
"_id": "78b0e7de-c24c-11eb-8529-0242ac130003",
"expiresAt": "2021-04-22T14:06:55.069Z",
"section": {
"gateFront": 1,
"gateMiddle": 1,
"gateBack": 0
}
}
In the first try I wrote the query like I do other (simple) $set-operations but this time with $cond to work with if-else:
filter := bson.M{
"_id": iD,
}
update := bson.M{
"$set": bson.M{
"expiresAt": bson.M{
"$cond": bson.M{
"if": bson.M{
"$and": []bson.M{
{"section.gateBack": bson.M{"$eq": 0}},
{"section.gateBack": bson.M{"$exists": true}},
},
},
"then": time.Now().AddDate(0, 1, 0),
"else": time.Now().AddDate(0, 0, 1),
},
},
},
}
res, err := s.coll.UpdateOne(
ctx,
filter,
update,
)
But with this update-operation I get the following error:
multiple write errors: [{write errors: [{The dollar ($) prefixed field '$cond' in 'expiresAt.$cond' is not valid for storage.}]}, {<nil>}]
I found a question on MongoDB developers side which says that the update-value has to be an array: https://developer.mongodb.com/community/forums/t/mongoerror-the-dollar-prefixed-field-cond-in-energy-cond-is-not-valid-for-storage/16448
This makes sense when I read the official documentation of MongoDB for the operation UpdateOne() with Aggregation pipeline: https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/#example-2
So I have changed my update value and append it to an array of bson.M:
filter := bson.M{
"_id": iD,
}
var pipeline []bson.M
update := bson.M{
"$set": bson.M{
"expiresAt": bson.M{
"$cond": bson.M{
"if": bson.M{
"$and": []bson.M{
{"section.gateBack": bson.M{"$eq": 0}},
{"section.gateBack": bson.M{"$exists": true}},
},
},
"then": time.Now().AddDate(0, 1, 0),
"else": time.Now().AddDate(0, 0, 1),
},
},
},
}
pipeline = append(pipeline, update)
res, err := s.coll.UpdateOne(
ctx,
filter,
pipeline,
)
But now I get the error like this:
cannot transform type []primitive.M to a BSON Document: WriteArray can only write a Array while positioned on a Element or Value but is positioned on a TopLevel
I don't know how to build the operation to make it runnable.
My aim is to update the field expiresAt with different timestamps depending on the values of 'section'. I also want to make it more complex with nested if-else but should first work with simple conditions.
Something like this:
update := bson.M{
"$set": bson.M{
"expiresAt": bson.M{
"$cond": bson.M{
"if": bson.M{
"$and": []bson.M{
{"section.gateBack": bson.M{"$eq": 0}},
{"section.gateBack": bson.M{"$exists": true}},
},
},
"then": time.Now().AddDate(0, 0, 1),
"else": bson.M{
"$cond": bson.M{
"if": bson.M{
"$and": []bson.M{
{"section.gateMiddle": bson.M{"$eq": 0}},
{"section.gateMiddle": bson.M{"$exists": true}},
},
},
"then": time.Now().AddDate(0, 1, 0),
"else": ...,
},
},
},
},
},
}
Does someone know how to write an updateOne-operation in Golang which is working with if-else as $cond?
Upvotes: 0
Views: 2317
Reputation: 1
Sorry for the late reply. I totally forgot to post the solution.
Like @prasad_ was saying, I tried to use the switch-case operator, but with UpdateOne()
.
ctx := context.Background()
filter := bson.M{
"_id": ID,
}
back := bson.M{
"case": bson.M{
"$and": []bson.M{
{"$gt": []interface{}{"$section.gateBack", 0}},
{"$exists": []interface{}{"$section.gateBack", true}},
},
},
"then": time.Now().AddDate(0, 0, 1),
}
middle := bson.M{
"case": bson.M{
"$and": []bson.M{
{"$gt": []interface{}{"$section.gateMiddle", 0}},
{"$exists": []interface{}{"$section.gateMiddle", true}},
},
},
"then": time.Now().AddDate(0, 1, 0),
}
front := bson.M{
"case": bson.M{
"$and": []bson.M{
{"$gt": []interface{}{"$section.gateFront", 0}},
{"$exists": []interface{}{"$section.gateFront", true}},
{"$in": []interface{}{"$section.access", []string{"gate", "door"}}},
},
},
"then": time.Now().AddDate(1, 0, 0),
}
cases := bson.A{back, middle, front}
update := []bson.M{
{
"$set": bson.M{
"expiresAt": bson.M{
"$switch": bson.M{
"branches": cases,
"default": time.Now().AddDate(0, 0, 0),
},
},
},
},
}
res, err := coll.UpdateOne(
ctx,
filter,
update,
)
if err != nil {
return 0, err
}
Upvotes: 0
Reputation: 977
Use bson.A{bson.M{...}, bson.M{...}}
instead of []bson.M{...}
the mongo driver aliases map and slice types and looks for bson.M{}
bson.A{}
types when converting your query.
Upvotes: 0