MongoDB报错Cannot apply $pull to a non-array value求助
问题分析与解决方案
报错根源
- "Cannot apply $pull to a non-array value":集合中部分文档的
liked/disliked字段并非数组类型(比如是单个ObjectID、字符串等非数组值),你原本用的$ifNull仅能处理字段为null的情况,无法覆盖其他非数组场景。 - "error decoding key disliked: cannot decode document into []primitive.ObjectID":查询解码时,文档中
disliked字段的实际类型与模型定义的[]primitive.ObjectID不匹配,导致解码失败。
修复步骤
1. 合并更新操作并完善类型校验
将两次UpdateOne合并为一次,用$cond判断字段是否为数组,确保无论当前字段是什么类型,都转为数组后再执行$addToSet和$pull:
func (f *ForumServiceImpl) LikedAPost(postId string, userId string) (*model.Forum, int64, error) { postObjectId, err := primitive.ObjectIDFromHex(postId) if err != nil { return nil, 0, err } userObjectId, err := primitive.ObjectIDFromHex(userId) if err != nil { return nil, 0, err } filter := bson.M{"_id": postObjectId} // 合并确保数组类型和更新操作 update := bson.M{ // 先确保liked是数组:如果是数组则保留,否则转为空数组 "$set": bson.M{ "liked": bson.M{ "$cond": []interface{}{ bson.M{"$isArray": "$liked"}, "$liked", primitive.A{}, }, }, "disliked": bson.M{ "$cond": []interface{}{ bson.M{"$isArray": "$disliked"}, "$disliked", primitive.A{}, }, }, }, // 执行点赞和取消踩的操作 "$addToSet": bson.M{"liked": userObjectId}, "$pull": bson.M{"disliked": userObjectId}, } result, err := f.forumCollection.UpdateOne(context.TODO(), filter, update) if err != nil { return nil, 0, err } // 查询更新后的文档 var forum model.Forum err = f.forumCollection.FindOne(context.TODO(), filter).Decode(&forum) if err != nil { return nil, 0, err } return &forum, result.ModifiedCount, nil }
2. 批量修正现有脏数据
执行MongoDB命令,批量将集合中所有文档的liked/disliked字段转为数组类型(如果不是数组的话):
db.forums.updateMany( { $or: [ { liked: { $not: { $type: "array" } } }, { disliked: { $not: { $type: "array" } } } ] }, [ { $set: { liked: { $cond: [{ $isArray: "$liked" }, "$liked", []] }, disliked: { $cond: [{ $isArray: "$disliked" }, "$disliked", []] } } } ] )
3. 确保创建文档时字段初始化正确
模型中的default:"[]"标签仅在使用部分ORM(如GORM)创建文档且未赋值时生效,直接用MongoDB驱动插入时需要手动初始化:
// 创建新Forum文档时,显式初始化liked和disliked为空数组 newForum := model.Forum{ // 其他字段赋值... Liked: []primitive.ObjectID{}, Disliked: []primitive.ObjectID{}, } // 执行插入操作 _, err := f.forumCollection.InsertOne(context.TODO(), newForum)
关键说明
- 用
$isArray替代$ifNull,能覆盖所有非数组类型的字段(包括单个值、字符串等),彻底解决$pull的非数组报错。 - 合并更新操作避免了两次数据库请求的并发问题,也提升了性能。
- 批量修正脏数据是一次性操作,确保现有所有文档的字段类型符合模型定义,消除解码报错。
内容的提问来源于stack exchange,提问作者Gbenga B Ayannuga
相关产品推荐
相关产品推荐

