为什么json.RawMessage扩大mongoDb文档的大小?

zan*_*ngw -3 go mongodb bson

以下代码尝试通过将新文档插入到mongoDB中 go.mongodb.org/mongo-driver

    data := "this is test string blablablablablablabla"
    type Doc struct {
        Version int "json:version, bson:version"
        Data   string   "json:data, bson:data"
    }
    dd := Doc{Version: 21, Data: data}
    dObj, _ := json.Marshal(dd)

    queryFilter := bson.M{"version": 1}
    update1 := bson.M{"$set": bson.M{"version": 1, "data": json.RawMessage(dObj)}}

    // insert data with json.RawMessage
    _, err := db.Mongo("test").Collection("test_doc1").UpdateOne(context.Background(), queryFilter, update1, options.Update().SetUpsert(true))
    if err != nil {
        fmt.Println("failed to insert doc1")
    }

    update2 := bson.M{"$set": bson.M{"version": 1, "data": (dObj)}}

    // insert data without json.RawMessage
    _, err = db.Mongo("test").Collection("test_doc2").UpdateOne(context.Background(), queryFilter, update2, options.Update().SetUpsert(true))
    if err != nil {
        fmt.Println("failed to insert doc2")
    }
Run Code Online (Sandbox Code Playgroud)

的内容test_doc1就是"data": json.RawMessage(dObj),而内容test_doc2就是"data": (dObj)

文件内容如下

db.test_doc1.find()
{ "_id" : ObjectId("5da164a950d625a5b2e5d23e"), "version" : 1, "data" : [ 123, 34, 86, 101, 114, 115, 105, 111, 110, 34, 58, 50, 49, 44, 34, 68, 97, 116, 97, 34, 58, 34, 116, 104, 105, 115, 32, 105, 115, 32, 116, 101, 115, 116, 32, 115, 116, 114, 105, 110, 103, 32, 98, 108, 97, 98, 108, 97, 98, 108, 97, 98, 108, 97, 98, 108, 97, 98, 108, 97, 98, 108, 97, 34, 125 ] }

db.test_doc2.find()
{ "_id" : ObjectId("5da164a950d625a5b2e5d249"), "version" : 1, "data" : BinData(0,"eyJWZXJzaW9uIjoyMSwiRGF0YSI6InRoaXMgaXMgdGVzdCBzdHJpbmcgYmxhYmxhYmxhYmxhYmxhYmxhYmxhIn0=") }
Run Code Online (Sandbox Code Playgroud)

经过检查以上两个文件的大小

Object.bsonsize(db.test_doc2.findOne())
111

Object.bsonsize(db.test_doc1.findOne())
556
Run Code Online (Sandbox Code Playgroud)

的大小test_doc1大于test_doc2。为什么?

Per bson文档

数组-数组的文档是普通的BSON文档,其中包含键的整数值,从0开始,然后依次继续。例如,数组['red','blue']将被编码为文档{'0':'red','1':'blue'}。键必须按升序排列。

Bson阵列可以占用更多磁盘空间吗?我对吗?

MongoDB版本:4.0

mas*_*nun 6

test_doc1本质上使用了json.RawMessage它,[]byte因此它被存储为代表字符串(文档的原始表示)的整数数组。

test_doc2将数据存储为二进制数据,这是一种更紧凑的形式。

Go Mongo驱动程序将WriteBinaryWithSubtype方法用于json编码的数据,但将方法WriteArray用于RawMessage

区别在于mongo端用于存储这些数据的数据类型。一种是将字节片存储为整数数组,另一种是将数据存储为具有子类型的二进制。与整数相比,二进制形式占用的空间更少。

深入研究,我发现Go驱动程序使用注册表来确定应如何将值编码为BSON。有一种专用于字节片的方法。

// ByteSliceEncodeValue is the ValueEncoderFunc for []byte.
func (dve DefaultValueEncoders) ByteSliceEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
Run Code Online (Sandbox Code Playgroud)

此方法使用将WriteBinary()字节片编码为二进制数据的方法。

如果有自定义类型(即使它在[]byte下面),则将其视为切片类型并触发切片的“默认编码器”。

// SliceEncodeValue is the ValueEncoderFunc for slice types.
func (dve DefaultValueEncoders) SliceEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
Run Code Online (Sandbox Code Playgroud)

此方法WriteArray()依次使用该方法。

摘要:json.Marshal调用[]byte直接获取使用类型,因此将它们视为bson二进制类型并以紧凑的二进制形式存储。json.RawMessage即使将数据[]byte内部存储为片,也就是整数片,因此在mongo中作为整数数组存储。

  • @AyushGupta完全不同意您的意见,我建议您尝试理解答案的实际含义。作为“再见值数组”,但表示为** integers **,它实际上在MongoDB中存储了许多** integers **,它们是[** 4字节值***](http:// bsonspec。 org / spec.html)和“不做一个**”。因此,随后的BSON大小差确实大于大小的** 4倍,加上每个整数值的额外填充和BSON数组的定义。BSON Binary字段仅按原样存储数据。 (5认同)