- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
(抱歉,这个问题比我想象的要长......)
我正在使用带有 mgo 驱动程序的 Go 和 MongoDB。我试图在同一个 MongoDB 集合中保留和检索不同的结构(实现通用接口(interface))。我来自 Java 世界(使用 Spring 很容易做到这一点,几乎没有配置),我很难用 Go 做类似的事情。我已经阅读了我能找到的所有最新相关文章或帖子或 StackExchange 问题,但我仍然没有找到完整的解决方案。这包括:
这是我用于测试的简化设置。假设两个结构S1
和S2
,实现了一个公共(public)接口(interface)I
。 S2
有一个类型为 S1
的隐式字段,这意味着结构明智的 S2
嵌入了一个 S1
值,并且按类型 S2
实现 I
。
type I interface {
f1()
}
type S1 struct {
X int
}
type S2 struct {
S1
Y int
}
func (*S1) f1() {
fmt.Println("f1")
}
现在我可以使用 mgo.Collection.Insert()
轻松保存 S1
或 S2
的实例,但要正确填充一个值例如,使用 mgo.Collection.Find().One()
,我需要传递一个指向 S1
或 S2
的现有值的指针,这意味着我已经知道我要读取的对象的类型!!我希望能够从 MongoDB 集合中检索文档,而不知道它是 S1
还是 S2
,或者实际上是实现了 I< 的任何对象
.
这是我到目前为止的位置:我没有直接保存我想要保留的对象,而是保存了一个 Wrapper
结构,它包含 MongoDB id、类型标识符和实际值。类型标识符是 packageName + "."的串联。 + typeName,它用于在类型注册表中查找类型,因为在 Go 中没有从类型名称映射到 Type 对象的本地机制。这意味着我需要注册我希望能够保留和检索的类型,但我可以接受。事情是这样的:
typeregistry.Register(reflect.TypeOf((*S1)(nil)).Elem())
typeregistry.Register(reflect.TypeOf((*S2)(nil)).Elem())
这是类型注册表的代码:
var types map[string]reflect.Type
func init() {
types = make(map[string]reflect.Type)
}
func Register(t reflect.Type) {
key := GetKey(t)
types[key] = t
}
func GetKey(t reflect.Type) string {
key := t.PkgPath() + "." + t.Name()
return key
}
func GetType(key string) reflect.Type {
t := types[key]
return t
}
保存对象的代码非常简单:
func save(coll *mgo.Collection, s I) (bson.ObjectId, error) {
t := reflect.TypeOf(s)
wrapper := Wrapper{
Id: bson.NewObjectId(),
TypeKey: typeregistry.GetKey(t),
Val: s,
}
return wrapper.Id, coll.Insert(wrapper)
}
检索对象的代码有点棘手:
func getById(coll *mgo.Collection, id interface{}) (*I, error) {
// read wrapper
wrapper := Wrapper{}
err := coll.Find(bson.M{"_id": id}).One(&wrapper)
if err != nil {
return nil, err
}
// obtain Type from registry
t := typeregistry.GetType(wrapper.TypeKey)
// get a pointer to a new value of this type
pt := reflect.New(t)
// FIXME populate value using wrapper.Val (type bson.M)
// HOW ???
// return the value as *I
i := pt.Elem().Interface().(I)
return &i, nil
}
这在返回的对象类型正确时部分起作用,但我不知道如何使用从存储在 wrapper 中的 MongoDB 检索的数据填充值
作为 pt
.Valbson.M
。
我已经尝试了以下但它不起作用:
m := wrapper.Val.(bson.M)
bsonBytes, _ := bson.Marshal(m)
bson.Unmarshal(bsonBytes, pt)
所以基本上剩下的问题是:如何从 bson.M
值填充未知结构?我确定必须有一个简单的解决方案......在此先感谢您的帮助。
这是包含所有代码的 Github 要点:https://gist.github.com/ogerardin/5aa272f69563475ba9d7b3194b12ae57
最佳答案
首先,您应该始终检查返回的错误。 bson.Marshal()
和 bson.Unmarshal()
返回您不检查的错误。这样做揭示了为什么它不起作用:
unmarshal can't deal with struct values. Use a pointer
pt
的类型是 reflect.Value
(这恰好是一个结构),而不是你应该传递给 bson.Unmarshal()
的东西。你应该通过例如指向要解码到的结构值的指针(它将包装在 interface{}
值中)。所以调用Value.Interface()
关于 reflect.New()
返回的值:
pt := reflect.New(t).Interface()
您可以将其传递给 bson.Unmarshal()
:
bsonBytes, err := bson.Marshal(m)
if err != nil {
panic(err)
}
if err = bson.Unmarshal(bsonBytes, pt); err != nil {
panic(err)
}
(在你的真实代码中你想要做一些除了 panic 之外的事情,这只是为了表明你应该总是检查错误!)
另请注意,可以将映射直接转换为结构(直接意味着无需编码和解码)。您可以手动实现或使用现成的第 3 方库。有关详细信息,请参阅 Converting map to struct
另请注意,还有更聪明的方法可以解决您想要做的事情。您可以将类型存储在 ID 本身中,因此如果您有 ID,则可以构造一个类型的值以解码到查询结果中,这样您就可以跳过整个过程。这会简单得多,效率也高得多。
例如,您可以使用以下 ID 结构:
<type>-<id>
例如:
my.package.S1-123
当获取/加载此文档时,您可以使用反射创建 my.package.S1
的值,并直接解码到该值(将其传递给 Query.One()
)。
关于mongodb - 去 + MongoDB : polymorphic queries,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54865657/
“多态”一词从何而来? 最佳答案 它来自希腊词根“poly”(许多)和“morphe”(形式)。多态对象可以采用多种形式(它可以由指向其任何祖先类的指针表示)。多态函数也可以有多种形式(它可以对实际上
我有一个名为 Frame 的基本结构这对一堆计算很有用:。 pub struct Frame { grid_val: Vec, grid_space: Vec, calcula
我有一个名为 Frame 的基本结构这对一堆计算很有用:。 pub struct Frame { grid_val: Vec, grid_space: Vec, calcula
“多态与方法重载或方法覆盖不同。......两者都不是......本身就是多态的实现”。 这是引自 wikipedia 然而,在“面向对象编程”一书中,Timothy Budd 指出有“四种不同形式的
我有一组我没有编写的类,它们是只读的。例如,假设这些类是以下类: public class Base { } public class B : Base { } public class C : B
什么是 Smalltalk 中的“无限动态多态性”?有人可以举个例子吗? 在这个 book 中提到了它:C++ 模板:完整指南,p. 238. 最佳答案 See在 C++ 中:通过继承实现的多态性是有
我在 Laravel 4 中有一个多态关系,它像我想要的那样工作。我有另一个,当我尝试插入相关模型时它不起作用,即使它与第一个相同并且我以相同的方式使用它。 我有 Event模型和 Article模型
我试图弄清楚如何在 Hack 中实现访客模式。它显然需要函数重载多态性,但正如我所测试的,这个例子: visitInt($m); } else if (is_string($m)) {
两者有什么区别? 具有 myMethod(int a) 的父类(super class)以及具有相同方法的继承类, 这是覆盖还是多态? 我很清楚黑白覆盖和重载的区别,但多态性和覆盖似乎是一样的。还是他
来自 Programming Languages: Principles and Paradigms, byMaurizio Gabbrielli, Simone Martini Definition
给定这段代码 locale A = fixes foo :: "'a" locale B = A + fixes bar :: "'a × 'a" locale C' = A + fixe
榆树市可能存在以下情况? func : a -> {a | id : Int} func x = { x | id = 123 } 由于a的多态性太强,因此无法编译。它认为可以是任何东西,包括非记录类
我只想有一个在 Hashtbls 上通用的简单函数,所以我写了这个: let iter_htbl (type a) (module H : Hashtbl.S with type key = a) h
我了解Polymorphic 和Metamorphic 代码的概念,但是最近我同时阅读了两者的Wikipedia页面(出于某种原因,我以前没有这样做过!)。现在,我真的很想为自己编写一些变形代码。 我
多态性是过载的另一个术语吗? 最佳答案 没有;重载是创建一个具有相同名称,带有不同数量参数或具有另一种类型参数的方法。 多态性是指在各种类型(都具有相同的“基本类型”)中更改特定方法的实现/功能。 重
我试图在 OCaml 中表示一组语法的产生式,存在类型对于建模语法规则的语义 Action 非常有用。我一直在研究 Menhir 源代码,存在类型也用于建模语义 Action 。考虑以下因素: typ
我在SML的学习上取得了一些进步,试图巩固我对一些最基本概念的理解。下面的练习证明还有很多东西要学。 我被要求实现一个名为 add 的函数,它接收两个数字并返回它们的总和。问题是,add 应该接收整数
首先,请看这些 Java 代码: Drawable.java package examples.simple.model; public interface Drawable { public
考虑以下代码: trait Animal { fn make_sound(&self) -> String; } struct Cat; impl Animal for Cat { f
考虑以下代码: trait Animal { fn make_sound(&self) -> String; } struct Cat; impl Animal for Cat { f
我是一名优秀的程序员,十分优秀!