Reputation: 27
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
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
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