Is there a way to get slice as result of Find()?

你离开我真会死。 提交于 2019-12-25 17:37:45

问题


Now I'm doing:

sess := mongodb.DB("mybase").C("mycollection")
var users []struct {
    Username string `bson:"username"`
}

err = sess.Find(nil).Select(bson.M{"username": 1, "_id": 0}).All(&users)
if err != nil {
    fmt.Println(err)
}

var myUsers []string
for _, user := range users{
    myUsers = append(myUsers, user.Username)
}

Is there a more effective way to get slice with usernames from Find (or another search function) directly, without struct and range loop?


回答1:


The result of a MongoDB find() is always a list of documents. So if you want a list of values, you have to convert it manually just as you did.

Using a custom type (derived from string)

Also note that if you would create your own type (derived from string), you could override its unmarshaling logic, and "extract" just the username from the document.

This is how it could look like:

type Username string

func (u *Username) SetBSON(raw bson.Raw) (err error) {
    doc := bson.M{}
    if err = raw.Unmarshal(&doc); err != nil {
        return
    }
    *u = Username(doc["username"].(string))
    return
}

And then querying the usernames into a slice:

c := mongodb.DB("mybase").C("mycollection") // Obtain collection

var uns []Username
err = c.Find(nil).Select(bson.M{"username": 1, "_id": 0}).All(&uns)
if err != nil {
    fmt.Println(err)
}
fmt.Println(uns)

Note that []Username is not the same as []string, so this may or may not be sufficient to you. Should you need a user name as a value of string instead of Username when processing the result, you can simply convert a Username to string.

Using Query.Iter()

Another way to avoid the slice copying would be to call Query.Iter(), iterate over the results and extract and store the username manually, similarly how the above custom unmarshaling logic does.

This is how it could look like:

var uns []string
it := c.Find(nil).Select(bson.M{"username": 1, "_id": 0}).Iter()
defer it.Close()
for doc := (bson.M{}); it.Next(&doc); {
    uns = append(uns, doc["username"].(string))
}
if err := it.Err(); err != nil {
    fmt.Println(err)
}
fmt.Println(uns)



回答2:


I don't see what could be more effective than a simple range loop with appends. Without all the Mongo stuff your code basically is this and that's exactly how I would do this.

package main

import (
    "fmt"
)

type User struct {
    Username string
}

func main() {

    var users []User
    users = append(users, User{"John"}, User{"Jane"}, User{"Jim"}, User{"Jean"})
    fmt.Println(users)

    // Interesting part starts here.
    var myUsers []string
    for _, user := range users {
        myUsers = append(myUsers, user.Username)
    }
    // Interesting part ends here.

    fmt.Println(myUsers)
}

https://play.golang.com/p/qCwENmemn-R



来源:https://stackoverflow.com/questions/49317887/is-there-a-way-to-get-slice-as-result-of-find

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!