Get all model tree child node in MongoDB with Go

I have data collection in MongoDB.

My data was built in model tree and relationship between child node and parent node is property parentid .

Here is my data architecture


And here is my sample data

{"_id":{"$oid":"5ebd05b52f3700008500220b"},"username":"DHBK","password":"123456","lastname":"DHBK","useremail":"dhbk@edu.com.vn","usertel":"0907111001","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":null,"comid":"DHBK","comdepartment":"DHBK","usercode":"DHBK_0001","usertype":"ADMIN_COM"}
{"_id":{"$oid":"5ebd05b52f3700008500220c"},"username":"KHOA_DIEN","password":"123456","lastname":"KHOA_DIEN","useremail":"KHOA_DIEN@edu.com.vn","usertel":"0907111002","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"DHBK","comid":"DHBK","comdepartment":"KHOA_DIEN","usercode":"DHBK_0002","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f3700008500220d"},"username":"KHOA_XD","password":"123456","lastname":"KHOA_XD","useremail":"KHOA_XD@edu.com.vn","usertel":"0907111003","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"DHBK","comid":"DHBK","comdepartment":"KHOA_XD","usercode":"DHBK_0003","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f3700008500220e"},"username":"KHOA_CNTT","password":"123456","lastname":"KHOA_CNTT","useremail":"KHOA_CNTT@edu.com.vn","usertel":"0907111004","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"DHBK","comid":"DHBK","comdepartment":"KHOA_CNTT","usercode":"DHBK_0004","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f3700008500220f"},"username":"BOMON_TUDONG","password":"123456","lastname":"BOMON_TUDONG","useremail":"BOMON_TUDONG@edu.com.vn","usertel":"0907111005","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"KHOA_DIEN","comid":"DHBK","comdepartment":"KHOA_DIEN","usercode":"DHBK_0005","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f37000085002210"},"username":"BOMON_VIENTHONG","password":"123456","lastname":"BOMON_VIENTHONG","useremail":"BOMON_VIENTHONG@edu.com.vn","usertel":"0907111006","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"KHOA_DIEN","comid":"DHBK","comdepartment":"KHOA_DIEN","usercode":"DHBK_0006","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f37000085002211"},"username":"BOMON_HETHONG","password":"123456","lastname":"BOMON_HETHONG","useremail":"BOMON_HETHONG@edu.com.vn","usertel":"0907111007","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"KHOA_DIEN","comid":"DHBK","comdepartment":"KHOA_DIEN","usercode":"DHBK_0007","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f37000085002212"},"username":"BOMON1_XD","password":"123456","lastname":"BOMON1_XD","useremail":"BOMON1_XD@edu.com.vn","usertel":"0907111008","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"KHOA_XD","comid":"DHBK","comdepartment":"KHOA_XD","usercode":"DHBK_0008","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f37000085002213"},"username":"BOMON2_XD","password":"123456","lastname":"BOMON2_XD","useremail":"BOMON2_XD@edu.com.vn","usertel":"0907111009","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"KHOA_XD","comid":"DHBK","comdepartment":"KHOA_XD","usercode":"DHBK_0009","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f37000085002214"},"username":"BOMON3_XD","password":"123456","lastname":"BOMON3_XD","useremail":"BOMON3_XD@edu.com.vn","usertel":"0907111010","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"KHOA_XD","comid":"DHBK","comdepartment":"KHOA_XD","usercode":"DHBK_0010","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f37000085002215"},"username":"TRUONGKHOA_BMVT","password":"123456","lastname":"TRUONGKHOA_BMVT","useremail":"TRUONGKHOA_BMVT@edu.com.vn","usertel":"0907111011","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"BOMON_VIENTHONG","comid":"DHBK","comdepartment":"BOMON_VIENTHONG","usercode":"DHBK_0011","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f37000085002216"},"username":"PHOKHOA_BMVT","password":"123456","lastname":"PHOKHOA_BMVT","useremail":"PHOKHOA_BMVT@edu.com.vn","usertel":"0907111012","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"BOMON_VIENTHONG","comid":"DHBK","comdepartment":"BOMON_VIENTHONG","usercode":"DHBK_0012","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f37000085002217"},"username":"THUKY_BMVT","password":"123456","lastname":"THUKY_BMVT","useremail":"THUKY_BMVT@edu.com.vn","usertel":"0907111013","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"BOMON_VIENTHONG","comid":"DHBK","comdepartment":"BOMON_VIENTHONG","usercode":"DHBK_0013","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f37000085002218"},"username":"GV_BMVT","password":"123456","lastname":"GV_BMVT","useremail":"GV_BMVT@edu.com.vn","usertel":"0907111014","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"BOMON_VIENTHONG","comid":"DHBK","comdepartment":"BOMON_VIENTHONG","usercode":"DHBK_0014","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f37000085002219"},"username":"SV1_BMVT","password":"123456","lastname":"SV1_BMVT","useremail":"SV1_BMVT@edu.com.vn","usertel":"0907111015","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"GV_BMVT","comid":"DHBK","comdepartment":"BOMON_VIENTHONG","usercode":"DHBK_0015","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f3700008500221a"},"username":"SV2_BMVT","password":"123456","lastname":"SV2_BMVT","useremail":"SV2_BMVT@edu.com.vn","usertel":"0907111016","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"GV_BMVT","comid":"DHBK","comdepartment":"BOMON_VIENTHONG","usercode":"DHBK_0016","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f3700008500221b"},"username":"SV3_BMVT","password":"123456","lastname":"SV3_BMVT","useremail":"SV3_BMVT@edu.com.vn","usertel":"0907111017","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"GV_BMVT","comid":"DHBK","comdepartment":"BOMON_VIENTHONG","usercode":"DHBK_0017","usertype":"USER_COM"}
{"_id":{"$oid":"5ebd05b52f3700008500221c"},"username":"SV4_BMVT","password":"123456","lastname":"SV4_BMVT","useremail":"SV4_BMVT@edu.com.vn","usertel":"0907111018","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"GV_BMVT","comid":"DHBK","comdepartment":"BOMON_VIENTHONG","usercode":"DHBK_0018","usertype":"USER_COM"}
{"_id":{"$oid":"5ec642b2412b0000e70021a5"},"username":"KHOA_KT","password":"123456","lastname":"KHOA_KT","useremail":"KHOA_KT@edu.com.vn","usertel":"0907111002","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"DHKT","comid":"DHBK","comdepartment":"KHOA_KT","usercode":"DHKT_0002","usertype":"USER_COM"}
{"_id":{"$oid":"5ec642b2412b0000e70021a8"},"username":"BOMON_KTDOANHNGHIEP","password":"123456","lastname":"BOMON_KTDOANHNGHIEP","useremail":"BOMON_KTDOANHNGHIEP@edu.com.vn","usertel":"0907111005","userdate":"2020-05-05","userstatus":"ACTIVE","userparentid":"KHOA_KT","comid":"DHBK","comdepartment":"KHOA_KT","usercode":"DHKT_0005","usertype":"USER_COM"}
{"_id":{"$oid":"5ece3517b8d5570916d013f6"},"username":"SV5_BMVT","password":"123","lastname":"SV5_BMVT","useremail":"SV5_BMVT@edu.com.vn","usertel":"0907111019","userdate":"2020-05-14","userstatus":"ACTIVE","userparentid":"GV_BMVT","comid":"DHBK","comdepartment":"BOMON_VIENTHONG","usercode":"DHBK_0019","usertype":"USER_COM"}
{"_id":{"$oid":"5eddf0a9b8d5570916dae6ff"},"username":"SV6_BMVT","password":"123456","lastname":"SV6_BMVT","useremail":"SV6_BMVT@edu.com.vn","usertel":"0907111020","userdate":"2020-06-08","userstatus":"ACTIVE","userparentid":"GV_BMVT","comid":"DHBK","comdepartment":"BOMON_VIENTHONG","usercode":"DHBK_0019","usertype":"USER_COM"}

Now I want to get all child node of specific parent node. For example, I want to get all child node of DHBK node .

I have completed MongoDB shell to query this requirement.

Here is my MongoDB shell

var descendants=[]
var stack=[];
var item = db.users.findOne({username:"DHBK"});
stack.push(item);
while (stack.length>0){
    var currentnode = stack.pop();
    var children = db.users.find({userparentid:currentnode.username});
    while(true === children.hasNext()) {
        var child = children.next();
        descendants.push(child.username);
        stack.push(child);
    }
}
descendants.join(",")

It worked and showed me correct result. Here is my output result

KHOA_DIEN,KHOA_XD,KHOA_CNTT,BOMON1_XD,BOMON2_XD,BOMON3_XD,BOMON_TUDONG,BOMON_VIENTHONG,BOMON_HETHONG,TRUONGKHOA_BMVT,PHOKHOA_BMVT,THUKY_BMVT,GV_BMVT,SV1_BMVT,SV2_BMVT,SV3_BMVT,SV4_BMVT,SV5_BMVT,SV6_BMVT

Then I write Go code to implement this MongoDB shell.

Here is my code

package main
 import (
     "context"
     "fmt"
     "strings"
     "time"
     "go.mongodb.org/mongo-driver/bson"
     "go.mongodb.org/mongo-driver/mongo"
     "go.mongodb.org/mongo-driver/mongo/options"
 )
 func main() {
      GetAllChildOfNode("DHBK")
 }
func GetAllChildOfNode(node string) error {
   ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
   client, err := mongo.Connect(ctx, options.Client().ApplyURI("URI string"))
   if err != nil {
       return err
   }
   defer client.Disconnect(ctx)
   database := client.Database("users")
   users := database.Collection("users")
   var descendants []string
   var stack []string
   err = users.FindOne(ctx, bson.M{"username": "DHBK"}).Decode(&stack)
   leng := len(stack)
   for leng > 0 {
       //I HaVE TROUBLE HERE
       currentnode := stack.
   }
   return nil
 }

But I have trouble in implement push , pop method and while loop as MongoDB shell by use Go.
Thank you in advance.

Hi @Napoleon_Ponaparte,

You can optimise this by performing the operation as one database operation via Aggregation Pipeline. Especially with the use of $graphLookup stage to perform a recursive search on a collection. This would also reduce the network round trips between the application and the database server.

Using the document examples posted, you can get the children of username DHBK as below:

db.users.aggregate([
 {"$match":{"username":"DHBK"}},
 {"$graphLookup":{
    "from":"users",
    "startWith":"$username", 
    "connectFromField":"username", 
    "connectToField":"userparentid", 
    "as":"children"}}, 
 {"$project":{"_id":0 ,"result":"$children.username"}}
])

You can write this in Go using mongo-go-driver with an example as below:

collection := client.Database("users").Collection("users")

// Filter only for the highest parent node
matchStage := bson.D{
	{"$match", bson.D{
		{"username", "DHBK"},
	}},
}

// Traverse through the graph
graphLookupStage := bson.D{
	{"$graphLookup", bson.D{
		{"from", "users"},
		{"startWith", "$username"},
		{"connectFromField", "username"},
		{"connectToField", "userparentid"},
		{"as", "children"},
	}},
}

// Project only the required field
projectStage := bson.D{
	{"$project", bson.D{
		{"_id", 0},
		{"result", "$children.username"},
	}},
}

pipeline := mongo.Pipeline{matchStage, graphLookupStage, projectStage}
cursor, err := collection.Aggregate(context.TODO(), pipeline)
defer cursor.Close(context.TODO())
if err != nil {
     panic(err)
}
for cursor.Next(context.TODO()) {
        var doc bson.M
	err := cursor.Decode(&doc)
	if err != nil {
	    panic(err)
	}
	fmt.Println(doc["result"])
}

You may also find Model Tree Structures a useful reference to design various ways of tree data structures in MongoDB.

Regards,
Wan.

1 Like

A couple of small edits to @wan’s great answer:

  1. You should check the error from collection.Aggregate
  2. If you want to iterate the cursor using cursor.Next, you should redeclare the doc variable inside the loop at every iteration rather than reusing the same one.
  3. If you just want to decode each element from the cursor iteration into a bson.M, you can use cursor.All instead:
var results []bson.M
if err = cursor.All(ctx, &results); err != nil {
    panic(err)
}

Thank you for your help Mr @wan and @Divjot_Arora

Thanks @Divjot_Arora, edited.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.