Search code examples
mongodbgoslicemgo

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


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?


Solution

  • 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)