Unstructured MongoDB collections with mgo

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

Unstructured MongoDB collections with mgo

问题

我非常新手Go语言。从我在mGo的示例中看到的,为了查询一个集合并从中读取数据,你需要预定义一个将返回的数据的结构体。

type Person struct {
    ID        bson.ObjectId `bson:"_id,omitempty"`
    Name      string
    Phone     string
    Timestamp time.Time
}

在PHP中,文档被分配给一个数组。这非常完美,因为一个记录可能有完全不同的键集(可能不包含Name或Phone,但包含Email),我可以直接访问它,而不需要设置预定义的类/结构体/变量。

在Go/mGo中是否有类似的方法呢?

英文:

I'm VERY new to Go. From what I've seen in the examples of mGo, in order to query a collection and then read from it, you have to predefine the data that will be coming back in a struct.

type Person struct {
	ID        bson.ObjectId `bson:"_id,omitempty"`
	Name      string
	Phone     string
	Timestamp time.Time
}

In PHP, the document was assigned to an array. This was perfect as one record may have completely different set of keys (may not contain Name or Phone but contain Email) and I could access it directly without setting up a predefined class / struct / variable.

Is there a way to do the same in Go / mGo?

答案1

得分: 48

有多种方法可以处理这个问题。

使用map:

var m bson.M
err := collection.Find(nil).One(&m)
check(err)
for key, value := range m {
    fmt.Println(key, value)
}

需要注意的是,对于mgo来说,bson.M并没有什么特殊之处。它只是一个map[string]interface{}类型,你可以定义自己的map类型,并在mgo中使用它们,即使它们具有不同的值类型。

使用文档切片:

bson.D是一个在mgo内部已知的切片,它既提供了更高效的机制,又提供了一种保留键顺序的方式,这在某些情况下(例如定义索引时)MongoDB会使用到。

例如:

var d bson.D
err := collection.Find(nil).One(&d)
check(err)
for i, elem := range d {
    fmt.Println(elem.Name, elem.Value)
}

使用内联map字段

,inline bson 标记也可以用于map字段,这样你既可以方便地操作已知字段,又可以通过内联map处理未知字段。

例如:

type Person struct {
    ID        bson.ObjectId `bson:"_id,omitempty"`
    Name      string
    Phone     string
    Extra     bson.M `bson:",inline"`
}
英文:

There are multiple ways you can handle this.

Using a map:

var m bson.M
err := collection.Find(nil).One(&m)
check(err)
for key, value := range m {
    fmt.Println(key, value)
}

Note that there's nothing special about bson.M as far as mgo is concerned. It's just a map[string]interface{} type, and you can define your own map types and use them with mgo, even if they have a different value type.

Using a document slice:

The bson.D is a slice that is internally known to mgo, and it exists both to offer a more efficient mechanism and to offer a way to preserve the ordering of keys, which is used by MongoDB in some circumstances (for example, when defining indexes).

For example:

var d bson.D
err := collection.Find(nil).One(&d)
check(err)
for i, elem := range d {
    fmt.Println(elem.Name, elem.Value)
}

Using an ,inline map field

The ,inline bson flag can also be used in a map field, so that you can have your cake and eat it too. In other words, it enables using a struct so that manipulating known fields is convenient, and at the same time allows dealing with unknown fields via the inline map.

For example:

type Person struct {
    ID        bson.ObjectId `bson:"_id,omitempty"`
    Name      string
    Phone     string
    Extra     bson.M `bson:",inline"`
}

答案2

得分: 3

你可以将所有内容存储在一个映射(map)中。mgo/bson 包提供了 bson.M 数据类型,可以用于存储任意数据。由于 MongoDB 不强制使用严格的模式,mgo 在内部使用 bson.M 类型来处理所有数据。

如果你只是想显示数据,使用 bson.M 应该是可以的。但是一旦你想要开始处理数据,最好考虑使用结构体(struct)来代替。否则,在程序中你将需要进行很多类型断言。例如,假设你想要将文档的标题(result["title"])以大写形式打印出来。如果仅使用 bson.M,你的代码将如下所示:

// 是否存在 title 属性?
if title, ok := result["title"]; ok {
    // 它是一个字符串吗?(而不是另一个映射、整数或其他类型)
    if titleStr, ok := title.(string); ok {
        // 是的,它是一个字符串
        fmt.Println("Title: ", strings.ToUpper(titleStr))
    }
}

当你让 mgo 将数据转换为结构体后,你的程序将变得更易读、更易于维护。然后,相同的代码可能会变成这样:

fmt.Println(strings.ToUpper(result.Title))

通常,你为每种类型的文档定义一个结构体类型(例如一个用于 "users",另一个用于 "blog posts" 等),其中包含你可能想要访问的每个属性。如果你的用户文档没有分配电子邮件地址,解码后你将得到一个空字符串(或更一般地说,零值)。

英文:

You could store everything in a map. The mgo/bson package provides a bson.M data type that can be used to store arbitrary data and since MongoDB doesn't enforce a strong schema, mgo uses the bson.M type internally for everything.

If you just want to display the data, using a bson.M should be fine, but once you want to start working with it, you should consider using a struct instead. Otherwise, you would need a lot of type assertions in your program. For example, consider you want to print the title (result["title"]) of your document in upper case. By using just bson.M, your code would look like:

// is there a title attribute?
if title, ok := result["title"]; ok {
    // is it a string? (and not another map or integer or something else)
    if titleStr, ok := title.(string); ok {
        // ok, it is a string
        fmt.Println("Title: ", strings.ToUpper(titleStr))
    }
}

Your program would become much more readable and easier to maintain when you let mgo convert the data to a struct for you. Then, the same code might read as:

fmt.Println(strings.ToUpper(result.Title))

Normally you define one struct type for each type of document you want to deal with (i.e. one type for "users", another for "blog posts", etc.) that contains every attribute that you might want to access. If your user document does not happen to have an email address assigned, you will just get an empty string (or more generally, the zero value) back when you decode it.

huangapple
  • 本文由 发表于 2013年8月21日 00:13:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/18340031.html
匿名

发表评论

匿名网友

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

确定