如何在Golang中通用避免向MongoDB插入零值ObjectId
如何防止Golang生成的零值ObjectId存入MongoDB?
我不太喜欢Golang的零值特性,它会自动生成零值ObjectId。目前我用以下JSON Schema做校验:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://vibe.com/wss/src/common/mongo/mongo-message-ack", "$ref": "#/$defs/MongoMessageAck", "$defs": { "MongoMessageAck": { "properties": { "_id": { "$ref": "#/$defs/ObjectID" }, "MessageId": { "$ref": "#/$defs/ObjectID" } }, "additionalProperties": false, "type": "object", "required": [ "_id", "MessageId" ] }, "ObjectID": { "items": { "type": "integer" }, "type": "array", "maxItems": 12, "minItems": 12 } } }
其中校验ObjectId的部分是:
"ObjectID": { "items": { "type": "integer" }, "type": "array", "maxItems": 12, "minItems": 12 }
我还看到过这种写法:
"$expr": { "$and": [ { "$ne": ["$_id", ObjectId("000000000000000000000000")] }, { "$ne": ["$MessageId", ObjectId("000000000000000000000000")] } ] }
但$expr的写法不是合法JSON,用起来不方便(虽然能生效)。
我知道可以给每个ObjectId字段建唯一索引并预插入零值,但希望再加一层保障,避免遗漏给某些ObjectId字段建索引,有没有更好的办法?
解决方案
1. 业务代码层前置校验
直接在数据入库前对所有ObjectId字段做非零检查,写个通用校验函数就能复用:
import ( "errors" "context" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" ) func IsZeroObjectID(id primitive.ObjectID) bool { return id == primitive.NilObjectID } // 保存前做校验 func SaveMessageAck(collection *mongo.Collection, ack MongoMessageAck) error { if IsZeroObjectID(ack.ID) || IsZeroObjectID(ack.MessageId) { return errors.New("ObjectId字段不能为零值") } _, err := collection.InsertOne(context.TODO(), ack) return err }
这种方式直接在代码层拦截无效请求,不依赖外部工具,简单直接。
2. 扩展JSON Schema校验规则
现有JSON Schema只校验了ObjectId的格式,没检查是否为零值。可以给ObjectID定义添加not规则,排除全零数组:
"ObjectID": { "items": { "type": "integer" }, "type": "array", "maxItems": 12, "minItems": 12, "not": { "const": [0,0,0,0,0,0,0,0,0,0,0,0] } }
完全符合JSON规范,能直接拦截全零的ObjectId数组。
3. MongoDB文档级验证器
给集合添加文档验证规则,在数据库层面拦截不符合要求的文档,用合法JSON格式定义即可:
{ "$jsonSchema": { "bsonType": "object", "required": ["_id", "MessageId"], "properties": { "_id": { "bsonType": "objectId", "not": { "$eq": ObjectId("000000000000000000000000") } }, "MessageId": { "bsonType": "objectId", "not": { "$eq": ObjectId("000000000000000000000000") } } } } }
就算代码层漏了校验,数据库也会拒绝插入零值ID的文档。
4. 自定义结构体校验标签
如果用结构体绑定数据,可借助第三方校验库(比如go-playground/validator)添加零值检查:
import ( "github.com/go-playground/validator/v10" "go.mongodb.org/mongo-driver/bson/primitive" ) type MongoMessageAck struct { ID primitive.ObjectID `bson:"_id" validate:"required,nonzeroobjectid"` MessageId primitive.ObjectID `bson:"MessageId" validate:"required,nonzeroobjectid"` } var validate *validator.Validate func init() { validate = validator.New() // 注册自定义校验规则 _ = validate.RegisterValidation("nonzeroobjectid", func(fl validator.FieldLevel) bool { id, ok := fl.Field().Interface().(primitive.ObjectID) if !ok { return false } return id != primitive.NilObjectID }) } // 使用时校验 func ValidateAck(ack MongoMessageAck) error { return validate.Struct(ack) }
把校验逻辑和结构体绑定,代码更整洁,复用性强。
内容的提问来源于stack exchange,提问作者Alexander Mills
相关产品推荐
相关产品推荐

