英文:
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)
英文:
@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)
答案2
得分: 1
我们也曾经遇到过同样的问题,我们创建了两个结构体,一个使用接口,另一个使用结构体。
- 当你想要更新或插入数据时,可以使用接口。
- 当你想要从数据库中获取数据时,你需要创建另一个结构体,其中字段的类型使用结构体。
英文:
We also faced the same issue once, where we made two structs one using the interface and another using a struct.
- When you want to update or Insert you can use the interface.
- 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)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论