MongoDB在Go中将对象数据返回为键值对数组。

huangapple go评论85阅读模式
英文:

MongoDB returns object data as array of key-value pair in Go

问题

我已经编写了以下查询,它使用Golang中的mongodb库返回更新日期大于同步日期的记录。

pipeline := []bson.M{}
filter := []string{"$updated_at", "$synced_at"}
pipeline = append(pipeline, bson.M{"$match": bson.M{"$expr": bson.M{"$gte": filter}}})
opts := options.Aggregate().SetMaxTime(2 * time.Second)
cursor, err := collection.Aggregate(ctx, pipeline, opts)
for cursor.Next(context.Background()) {
    records := model.DData{}
    err = cursor.Decode(&records)
}

Data的结构如下:

type DData struct {
    Name      string      `json:"name" bson:"name"`
    Data      interface{} `json:"data" bson:"data"`
    UpdatedAt time.Time   `json:"updated_at" bson:"updated_at"`
    SyncedAt  time.Time   `json:"synced_at" bson:"synced_at"`
}

集合中的数据形式如下:

{
    "name": "Vallabh",
    "data": {
        "field1": "value1",
        "field2": "value2",
        "field3": "value3"
    },
    "updated_at": "2021-08-17T09:43:27Z",
    "synced_at": "2021-08-07T09:43:27Z"
}

但是,使用上述查询时,我得到的数据形式如下:

{
    "name": "Vallabh",
    "data": [
        {
            "key": "field1",
            "value": "value1"
        },
        {
            "key": "field2",
            "value": "value2"
        },
        {
            "key": "field3",
            "value": "value3"
        }
    ],
    "updated_at": "2021-08-17T09:43:27Z",
    "synced_at": "2021-08-07T09:43:27Z"
}

我做错了什么?只有在结构体中字段类型为接口时才会发生这种情况。

英文:

I have written following query which returns me records where updated_at date is greater than synced_at date from all records using mongodb library in Golang.

	pipeline := []bson.M{}
	filter := []string{"$updated_at", "$synced_at"}
	pipeline = append(pipeline, bson.M{"$match": bson.M{"$expr": bson.M{"$gte": filter}}})
    opts := options.Aggregate().SetMaxTime(2 * time.Second)
	cursor, err := collection.Aggregate(ctx, pipeline, opts)
    for cursor.Next(context.Background()) {
		records := model.DData{}
		err = cursor.Decode(&records)
    }

The structure of Data is:

type DData struct {
	Name               string   	`json:"name" bson:"name"` 
	Data               interface{}  `json:"data" bson:"data"`
	UpdatedAt          time.Time    `json:"updated_at" bson:"updated_at"`
	SyncedAt           time.Time     `json:"synced_at" bson:"synced_at"`
}

Data in collection is of the form:

{
	"name":"Vallabh",
	"data":{
		"field1":"value1",
		"field2":"value2",
		"field3":"value3",
	},
	"updated_at":2021-08-17T09:43:27Z,
	"synced_at":2021-08-07T09:43:27Z
}

But with above query I am getting data in the form:

{
	"name":"Vallabh",
	"data":[
		{
			"key":"field1",
			"value":"value1"
		},
		{
			"key":"field2",
			"value":"value2"
		},
		{
			"key":"field3",
			"value":"value3"
		}
	}],
	"updated_at":"2021-08-17T09:43:27Z",
	"synced_at":"2021-08-07T09:43:27Z"
}

What am I doing wrong? Its happening only when field type is an interface in struct.

答案1

得分: 2

@VallabhLakade 我有类似的担忧,并尝试了以下方法,帮助解决了问题。

问题的本质是mongo-driver默认将类型为interface{}的结构体反序列化为bson.D,而mgo mgo-driver默认为bson.M

因此,在尝试与mongo-db建立连接时,我们需要添加以下代码,将SetRegistry()选项作为clientOpts,以映射旧的mgo行为,这样mongo-driver在反序列化类型为interface{}的结构体时将默认为bson.M,并且不会将值显示为键值对

	tM := reflect.TypeOf(bson.M{})
	reg := bson.NewRegistryBuilder().RegisterTypeMapEntry(bsontype.EmbeddedDocument, tM).Build()
	clientOpts := options.Client().ApplyURI(SOMEURI).SetAuth(authVal).SetRegistry(reg)
	client, err := mongo.Connect(ctx, clientOpts)

参考链接 -> https://stackoverflow.com/questions/74418871/mongodb-document-returns-array-of-key-value-pair-in-go-mongo-driver/74423240#74423240

英文:

@VallabhLakade I had similar concern like this and tried the below way that helped .

So basically what's the problem is that the mongo-driver defaults to unmarshalling as bson.D for structs of type interface{} where as mgo mgo-driver defaults to bson.M .

So we will have to add the below code while trying to establish connection with mongo-db , SetRegistry() options as clientOpts To map the old mgo behavior, so that mongo-driver defaults to bson.M while unmarshalling structs of type interface{} , and this should not display the values back as key-value pair

	tM := reflect.TypeOf(bson.M{})
	reg := bson.NewRegistryBuilder().RegisterTypeMapEntry(bsontype.EmbeddedDocument, tM).Build()
	clientOpts := options.Client().ApplyURI(SOMEURI).SetAuth(authVal).SetRegistry(reg)
	client, err := mongo.Connect(ctx, clientOpts)

Reference -> https://stackoverflow.com/questions/74418871/mongodb-document-returns-array-of-key-value-pair-in-go-mongo-driver/74423240#74423240

答案2

得分: 1

我们也曾经遇到过同样的问题,我们创建了两个结构体,一个使用接口,另一个使用结构体。

  1. 当你想要更新或插入数据时,可以使用接口。
  2. 当你想要从数据库中获取数据时,你需要创建另一个结构体,其中字段的类型使用结构体。
英文:

We also faced the same issue once, where we made two structs one using the interface and another using a struct.

  1. When you want to update or Insert you can use the interface.
  2. When you want to fetch from database you have to create another structure which using struct as the type for the field.

答案3

得分: 0

尝试使用以下代码:

type DData struct {
    Name      string            `json:"name" bson:"name"` 
    Data      map[string]string `json:"data" bson:"data"`
    UpdatedAt time.Time         `json:"updated_at" bson:"updated_at"`
    SyncedAt  time.Time         `json:"synced_at" bson:"synced_at"`
}

如果您事先知道类型,请始终使用该类型,使用interface{}会使库需要找出类型。如果您期望映射中有不同的值,可以使用map[string]interface{}

英文:

Try with this

type DData struct {
    Name               string             `json:"name" bson:"name"` 
    Data               map[string]string  `json:"data" bson:"data"`
    UpdatedAt          time.Time          `json:"updated_at" bson:"updated_at"`
    SyncedAt           time.Time          `json:"synced_at" bson:"synced_at"`
}

Always use the type if you know it beforehand, with interface{} the burden is on the library to find out what the type is. If you expect the varied values in the map you can use map[string]interface{}

答案4

得分: 0

我发现了一个非常奇怪的解决方案,我将其解码为bson.M{},然后将其编组和解组为我的结构体。我确定有更好的方法,但这个方法对我有效。

for cursor.Next(context.Background()) {
	tempResult := bson.M{}
	err = decode(cursor, &tempResult)
	if err != nil {
		logger.LogErrorf(err, "解码时发生错误")
		continue
	}
	obj, err := marshal(tempResult)
	if err != nil {
		logger.LogErrorf(err, "编组时发生错误")
		continue
	}
	var data model.DData
	err = json.Unmarshal(obj, &data)
	if err != nil {
		tenant.LogError(err, "解组时发生错误")
		continue
	}
}
英文:

Found a very weird solution to this problem where I decoded it to bson.M{}, then marshalled it and unmarshalled it to my struct. I am sure there are better ways but this one worked for me.

for cursor.Next(context.Background()) {
		tempResult := bson.M{}
		err = decode(cursor, &tempResult)
		if err != nil {
			logger.LogErrorf(err, "error while decoding")
			continue
		}
		obj, err := marshal(tempResult)
		if err != nil {
			logger.LogErrorf(err, "error while marshalling")
			continue
		}
		var data model.DData
		err = json.Unmarshal(obj, &data)
		if err != nil {
			tenant.LogError(err, "error while marshalling")
			continue
		}
}

答案5

得分: 0

@Vallabh,你应该使用mgocompat,类似下面的方式来模拟旧的mgo行为:

registry := mgocompat.NewRegistryBuilder().Build()
connectOpts := options.Client().ApplyURI(SOMEURI).SetAuth(info).SetRegistry(registry)
英文:

@Vallabh you shall use mgocompat too something like below this should mimic the old mgo behavior

registry := mgocompat.NewRegistryBuilder().Build()
connectOpts := options.Client().ApplyURI(SOMEURI).SetAuth(info).SetRegistry(registry)

huangapple
  • 本文由 发表于 2021年8月23日 21:42:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/68893765.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定