$pull 在带有 mongo-driver 的 Go 和 Mongosh 中的工作方式不同

$pull doesn't work the same in Go with mongo-driver and in Mongosh

什么?

正在尝试从 mongo 文档中的对象数组中删除一个元素。

我使用: go 版本 go1.17.1 darwin/amd64; go.mongodb.org/mongo-driver v 1.8.4; MongoDB 5.0.6 企业版

函数的简化版本,一切都尽可能简单:

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)
}

预期:

带有我提供的 ID 的文档将不再包含数组“数据”中的元素,这些元素的字段“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

bson.D 为文档建模,其中包含 key-value 对的有序列表,其中 key 是 属性 名称,value 是 属性 的值。

因此,如果您打算使用 bson.D 对文档建模,您总是必须在等效的 shell 命令中有一个文档的地方编写一个 bson.D 复合文字。

因此您的 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 values to define documents (it's a map). For details, see

会容易得多

您的 update 可能看起来像这样定义 bson.M:

update := bson.M{
    "$pull": bson.M{
        "data": bson.M{
            "field_type": bson.M{
                "$in": []string{"field_type_1", "field_type_2"},
            },
        },
    },
}