ROHITH P
ROHITH P

Reputation: 27

Get names of all keys in the collection using Go

I'd like to get the names of all the keys in a MongoDB collection.

For example, from this:

 "Id": ObjectId("5f5a010d431c4519dcda0e3d")
            "title": "App"
            "query": ""
            "db": ""
            "widgettype": ""
            "tablename": "active_instance"
            fields:Object
                user:"name",
                key:"passcode"
            "status": "active"
            "inlibrary": ""
            "createdts": 1599733804

Using "gopkg.in/mgo.v2" and "gopkg.in/mgo.v2/bson" packages.

err := mongodbSession.DB(dbName).C(collectionName).Find(bson.M{}).One(&result)
var keyset []string
    for index, _ := range result {
        fmt.Printf("%+v\n", index)
        keyset = append(keyset, index)
    }

    fmt.Println(keyset)

getting output as this

[_id title query db widgettype  status fields inlibrary createdts ]

child key is not being featched that is user and key.

Upvotes: 0

Views: 1183

Answers (2)

Phi Quang Phuoc
Phi Quang Phuoc

Reputation: 243

Package "gopkg.in/mgo.v2" and "gopkg.in/mgo.v2/bson" are outdated. Replace by "go.mongodb.org/mongo-driver/bson" and "go.mongodb.org/mongo-driver/mongo". Recursion must be used to retrieve nested fields. Assume the database "store" has collection "users" with this document

{"fields": {
  "user": "alex",
  "nickname": "ferguson",
}}

The code to retrieve keys from the collection is:

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
    defer cancel()
    client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))

    if err != nil {
        log.Panicln(err)
    }
    defer client.Disconnect(ctx)
    var docRaw bson.Raw
    res := client.Database("store").Collection("users").FindOne(context.Background(), bson.D{})

    err = res.Decode(&docRaw)
    if err != nil {
        log.Panicln(err)

    }
    elements, err := docRaw.Elements()
    if err != nil {
        log.Panicln(err)
    }
    var keys []string
    for _, e := range elements {
        keys = append(keys, getKey(e)...)
    }
    fmt.Println(keys)
}

func getKey(e bson.RawElement) []string {
    if e.Value().Type != bson.TypeEmbeddedDocument {
        return []string{e.Key()}
    }
    var result []string
    nested, _ := e.Value().Document().Elements()
    for _, n := range nested {
        nKeys := getKey(n)
        for _, k := range nKeys {
            result = append(result, fmt.Sprintf("%s.%s", e.Key(), k))
        }
    }
    return result
}

Output:

[_id fields.user fields.nickname]

Upvotes: 0

icza
icza

Reputation: 417412

Embedded documents will appear as another bson.M values inside your result, so you have to use a recursion to also traverse those.

Here's how you can do that:

func getKeys(m bson.M) (keys []string) {
    for k, v := range m {
        keys = append(keys, k)
        if m2, ok := v.(bson.M); ok {
            keys = append(keys, getKeys(m2)...)
        }
    }
    return
}

Example using it:

m := bson.M{"Id": bson.ObjectId("5f5a010d431c4519dcda0e3d"),
    "title":      "App",
    "query":      "",
    "db":         "",
    "widgettype": "",
    "tablename":  "active_instance",
    "fields": bson.M{
        "user": "name",
        "key":  "passcode",
    },
    "status":    "active",
    "inlibrary": "",
    "createdts": 1599733804,
}

keys := getKeys(m)
fmt.Println(keys)

Which will output (try it on the Go Playground):

[db widgettype createdts inlibrary _id title query tablename
  fields user key status]

If you look at the results, user and key are included, but it's not possible to tell if they were fields of the document or that of an embedded document.

You may choose to prefix fields of embedded documents with the field name of the embedded document field itself, e.g. to get fields.user and fields.key.

This is how you can do that:

func getKeys(m bson.M) (keys []string) {
    for k, v := range m {
        keys = append(keys, k)
        if m2, ok := v.(bson.M); ok {
            for _, k2 := range getKeys(m2) {
                keys = append(keys, k+"."+k2)
            }
        }
    }
    return
}

Which would output (try it on the Go Playground):

[createdts title query db status inlibrary _id widgettype tablename
    fields fields.user fields.key]

Also note that the above solutions do not handle arrays. If you have arrays, you should also iterate over them, and if they contain another array or object, you should do the same (recursively). It's an exercise for you to extend it to handle arrays too.

Upvotes: 1

Related Questions