需求:
写一个版本升级脚本,脚本中要修改数据库结构。
上一个版本,数据库所有记录用一个叫“key”的字段作为key
升级后的版本,把key 写到_id 里面去。这样就可以作为唯一主键和 索引,从而提高性能。
困难点:
在网上找到的mgo,到数据库里面查到数据后,都是用struct来接受。这样我就要定义无数个struct,很明显不实际。
由于我现在要修改的东西是修改的是_id,所以无法用 update 来实现。要有这几步:
1、查记录
2、把key赋值给_id,同时删除key
3、把这个新记录插入表中
4、把原来的记录删除
经过阅读源码,发现struct并非唯一可以接受查询结果的方法,map也可以。如下是核心代码:
func handleSingleCollection(db *mgo.Database, collectionName string) {
fmt.Println("Handling collecitonName: " + collectionName)
var docs []map[string]interface{}
coll := db.C(collectionName)
count, _ := coll.Count()
fmt.Println("collecitonName total records count: ", count)
coll.Find(bson.M{"key": bson.M{"$exists": true}}).Select(bson.M{}).All(&docs)
fmt.Println("collecitonName handling records count: ", len(docs))
for _, doc := range docs {
doc["_id"] = doc["key"]
delete(doc, "key")
//
coll.Insert(doc)
}
coll.RemoveAll(bson.M{"key": bson.M{"$exists": true}})
}
modified 20200929
又遇到一个场景,要修改一个非key字段,可以直接用update搞定。困难点在于,我需要访问里面一层的数据:
{
_id: ***,
object: {
data: ***
}
}
用上面写的map切片取出来,之后是都是相当于指针,object的类型其实也是map[string]interface {}
我就写了一个小函数,获取嵌套在里面的数据:
// 用map切片获取ducument的时候,doc的类型是map[string]interface {}
// doc[object]的类型也是map[string]interface {}
// 但doc[object] 是一个interface {} 指向了map[string]interface {}
// 所以这里用反射的方法,获取了doc[object] 里面的内容
// 用interface{}的方式返回,在外层再转化成具体的类型
func getValueFromObject(obj interface{}, key string) interface{} {
v := reflect.ValueOf(obj)
return v.MapIndex(reflect.ValueOf(key)).Interface()
}
外层取用的时候是这样的:
domainId := getValueFromObject(doc["obj"], "domainId").(int)