英文:
$pull doesn't work the same in Go with mongo-driver and in Mongosh
问题
什么?
尝试从Mongo文档中的对象数组中删除一个元素。
我使用的是:
go version go1.17.1 darwin/amd64;
go.mongodb.org/mongo-driver v1.8.4;
MongoDB 5.0.6 Enterprise
以下是函数的简化版本,尽可能简单:
func (r *Repo) UpdateModelByID(ctx context.Context, id string) error {
objID, err := primitive.ObjectIDFromHex(id)
// 实际上我在这里处理了 err,它是 nil
filter := bson.D{{"id", objID}}
update := bson.D{
{
Key: "$pull",
Value: bson.E{
Key: "data",
Value: bson.E{
Key: "$in",
Value: []string{"field_type_1", "field_type_2"},
},
},
},
}
log.Debugln("UPDATE", update)
result, err := cdm.getTemplatesCollection().UpdateOne(ctx, filter, update)
// 实际上我在这里处理了 err,它是 nil
log.Debugf("RESULT_OF_UPDATE: %+v", result)
}
期望的结果是:
我提供的ID对应的文档中,数组"data"中的元素不再具有"field_type"字段等于"field_type_1"或"field_type_2"。
实际结果是:
DEBU[0023] UPDATE [{$pull {data {field_type {$in [field_type_1, field_type_2]}}}}]
DEBU[0023] RESULT_OF_UPDATE: &{MatchedCount:1 ModifiedCount:0 UpsertedCount:0 UpsertedID:<nil>}
通过mongosh执行相同的命令:
db.templates.updateOne(
{ _id: ObjectId("6228a89d621d19a2f7977d2f") },
{ $pull: { data: {field_type: {$in: ["field_type_1", "field_type_2"]}}}
}
)
{ acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0 }
然后数组中的元素被删除了。
为什么会这样呢?
我注意到原生查询使用的是单花括号(只是一个对象),而Go代码使用的是bson.D,它在技术上是这些相同对象(bson.E)的数组([{}])。
我尝试更改mongosh命令进行测试:
db.templates.updateOne(
{ _id: ObjectId("6228a89d621d19a2f7977d2f") },
[{ $pull: { data: {field_type: {$in: ["field_type_1", "field_type_2"]}}}
}]
)
MongoServerError: Unrecognized pipeline stage name: '$pull'
在Go中,我尝试使用bson.E而不是bson.D(尽管根据文档,后者对于命令是推荐的),结果得到了这个错误:
DEBU[0030] UPDATE {$pull {data {field_type {$in [field_type_1, field_type_2]}}}}
ERRO[0030] cannot update model by id: update document must contain key beginning with '$' method=UpdateModelByID templateId=6228a89d621d19a2f7977d2f
英文:
What?
Trying to remove an element from an array of objects in a mongo document.
I use:
go version go1.17.1 darwin/amd64;
go.mongodb.org/mongo-driver v 1.8.4;
MongoDB 5.0.6 Enterprise
Shortened version of the function with everything as plain as possible:
func (r *Repo) UpdateModelByID(ctx context.Context, id string) error {
objID, err := primitive.ObjectIDFromHex(id)
// I actually handle err here, it's nil
filter := bson.D{{"_id", objID}}
update = bson.D{{
Key: "$pull",
Value: bson.E{
Key: "data",
Value: bson.E{
Key: "field_type",
Value: bson.E{
Key: "$in",
Value: []string{"field_type_1", "field_type_2"},
},
},
},
}}
log.Debugln("UPDATE", update)
result, err = cdm.getTemplatesCollection().UpdateOne(ctx, filter, update)
// I actually handle err here, it's nil
log.Debugf("RESULT_OF_UPDATE: %+v", result)
}
Expected:
Document with id I provided will no longer have elements in array "data" that have a field "field_type" equal to "field_type_1" or "field_type_2"
Got:
DEBU[0023] UPDATE [{$pull {data {field_type {$in [field_type_1, field_type_2]}}}}]
DEBU[0023] RESULT_OF_UPDATE: &{MatchedCount:1 ModifiedCount:0 UpsertedCount:0 UpsertedID:<nil>}
Same command through mongosh:
db.templates.updateOne(
{ _id: ObjectId("6228a89d621d19a2f7977d2f") },
{ $pull: { data: {field_type: {$in: ["field_type_1", "field_type_2"]}}}
}
)
{ acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0 }
And the elem-s are gone from the array.
Why could this be?
I did notice that native query has single curly brackets (just an object), while Go code uses bson.D which technically is an array of those same objects (bson.E) -> [{}].
I tried changing mongosh command to test:
db.templates.updateOne(
{ _id: ObjectId("6228a89d621d19a2f7977d2f") },
[{ $pull: { data: {field_type: {$in: ["field_type_1", "field_type_2"]}}}
}]
)
MongoServerError: Unrecognized pipeline stage name: '$pull'
In Go I tried using bson.E instead of bson.D (though the latter is recommended for commands per documentation) and got this:
DEBU[0030] UPDATE {$pull {data {field_type {$in [field_type_1, field_type_2]}}}}
ERRO[0030] cannot update model by id: update document must contain key beginning with '$' method=UpdateModelByID templateId=6228a89d621d19a2f7977d2f
答案1
得分: 1
bson.D
模型化了一个文档,它是一个有序的键值对列表,其中 key 是属性名,value 是属性的值。
因此,如果你打算使用 bson.D
来模型化文档,你必须始终使用 bson.D
的复合字面量来表示等效的 shell 命令中的文档。
所以你的 update
文档应该像这样:
update := bson.D{{
Key: "$pull", Value: bson.D{{
Key: "data", Value: bson.D{{
Key: "field_type", Value: bson.D{{
Key: "$in", Value: []string{"field_type_1", "field_type_2"},
}}}},
}},
}},
}
如果你省略了复合字面量中的命名字段,它会简化为:
update := bson.D{
{"$pull", bson.D{
"data", bson.D{
"field_type", bson.D{
"$in", []string{"field_type_1", "field_type_2"},
}}}},
},
}
是的,这看起来有点丑陋。请注意,如果元素的顺序不重要,使用 bson.M
值来定义文档(它是一个映射)会更容易。详情请参考 https://stackoverflow.com/questions/64281675/bson-d-vs-bson-m-for-find-queries/64281937#64281937
使用 bson.M
定义的 update
可以像这样:
update := bson.M{
"$pull": bson.M{
"data": bson.M{
"field_type": bson.M{
"$in": []string{"field_type_1", "field_type_2"},
},
},
},
}
英文:
bson.D
models a document, with an ordered list of key-value pairs, where the key is the property name, value is the property's value.
So if you intend to use bson.D
to model documents, you always have to write a bson.D
composite literal where there's a document in the equivalent shell command.
So your update
document must look like this:
update := bson.D{{
Key: "$pull", Value: bson.D{{
Key: "data", Value: bson.D{{
Key: "field_type", Value: bson.D{{
Key: "$in", Value: []string{"field_type_1", "field_type_2"},
}}},
}},
}},
}
If you omit the named fields from the composite literals, it's reduced to this:
update := bson.D{
{"$pull", bson.D{{
"data", bson.D{{
"field_type", bson.D{{
"$in", []string{"field_type_1", "field_type_2"},
}}},
}},
}},
}
Yes, it's kinda ugly. Please note that if order of elements does not matter, it's much easier to use bson.M
values to define documents (it's a map). For details, see https://stackoverflow.com/questions/64281675/bson-d-vs-bson-m-for-find-queries/64281937#64281937
Your update
could look like this defined with bson.M
:
update := bson.M{
"$pull": bson.M{
"data": bson.M{
"field_type": bson.M{
"$in": []string{"field_type_1", "field_type_2"},
},
},
},
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论