[GODRIVER-686] How can I decode lookup result to nested structure? Created: 11/Dec/18  Updated: 27/Oct/23  Resolved: 12/Dec/18

Status: Closed
Project: Go Driver
Component/s: BSON
Affects Version/s: 0.1.0
Fix Version/s: None

Type: Task Priority: Major - P3
Reporter: 성욱 조 Assignee: Kristofer Brandow (Inactive)
Resolution: Works as Designed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified


 Description   

How can I decode lookup(aggregation) result to nested structure?

This is my test code and I expected all inner structures are decoded.

package main
 
import (
	"context"
	"fmt"
	"github.com/mongodb/mongo-go-driver/bson"
	"github.com/mongodb/mongo-go-driver/mongo"
	"github.com/mongodb/mongo-go-driver/bson/primitive"
	"github.com/mongodb/mongo-go-driver/mongo/options"
)
 
func main() {
	type item struct {
		ID 		primitive.ObjectID `bson:"_id"`
		Name 	string
		Price 	int
	}
 
	type order struct {
		ID 		primitive.ObjectID `bson:"_id"`
		Name	string
		Item 	[]primitive.ObjectID
		Qty 	int
	}
 
	type user struct{
		ID 		primitive.ObjectID `bson:"_id"`
		Name 	string
		Orders []primitive.ObjectID
	}
 
	type fOrder struct {
		ID 		primitive.ObjectID `bson:"_id"`
		Name	string
		Item 	[]item
		Qty 	int
	}
 
	type fUser struct {
		ID 		primitive.ObjectID `bson:"_id"`
		Name 	string
		Orders 	[]fOrder
	}
 
	ctx := context.Background()
 
 
	client, err := mongo.NewClient("mongodb://localhost:27027")
	if err != nil {
		panic(err)
	}
 
	err = client.Connect(ctx)
	if err != nil {
		panic(err)
	}
 
	db := client.Database("testDb")
	err = db.Drop(ctx)
	if err != nil {
		panic(err)
	}
 
	items := client.Database("testDb").Collection("items")
	orders := client.Database("testDb").Collection("orders")
	users := client.Database("testDb").Collection("users")
 
	item1 := item{ID:primitive.NewObjectID(), Name: "a", Price: 12}
	item2 := item{ID:primitive.NewObjectID(), Name: "b", Price: 20}
	item3 := item{ID:primitive.NewObjectID(), Name: "c", Price: 5}
	item4 := item{ID:primitive.NewObjectID(), Name: "d", Price: 4}
	item5 := item{ID:primitive.NewObjectID(), Name: "e", Price: 50}
 
 
	order1 := order{ID: primitive.NewObjectID(), Name: "o1", Item: []primitive.ObjectID{item1.ID, item2.ID}, Qty: 1}
	order2 := order{ID: primitive.NewObjectID(), Name: "o2", Item: []primitive.ObjectID{item2.ID, item3.ID, item4.ID}, Qty: 3}
 
	order3 := order{ID: primitive.NewObjectID(), Name: "o3", Item: []primitive.ObjectID{item5.ID, item3.ID}, Qty: 2}
	order4 := order{ID: primitive.NewObjectID(), Name: "o4", Item: []primitive.ObjectID{item5.ID}, Qty: 1}
	order5 := order{ID: primitive.NewObjectID(), Name: "o5", Item: []primitive.ObjectID{item1.ID, item2.ID, item3.ID, item4.ID}, Qty: 5}
 
	user1 := user{ID:primitive.NewObjectID(), Name: "John", Orders:[]primitive.ObjectID{order1.ID, order2.ID}}
	user2 := user{ID:primitive.NewObjectID(), Name: "Brown", Orders:[]primitive.ObjectID{order3.ID, order4.ID, order5.ID}}
 
 
	err = client.UseSession(ctx, func(sessionContext mongo.SessionContext) error {
		if err != nil {
			return err
		}
 
		_, err = items.InsertMany(sessionContext, []interface{}{item1, item2, item3, item4, item5})
		if err != nil {
			return err
		}
 
		_, err = orders.InsertMany(sessionContext, []interface{}{order1, order2, order3, order4, order5})
		if err != nil {
			return err
		}
 
		_, err = users.DeleteOne(sessionContext, bson.D{{"_id", "a"}})
		if err != nil {
			return err
		}
 
		_, err = users.InsertMany(sessionContext, []interface{}{user1, user2})
		if err != nil {
			return err
		}
 
		return nil
	})
	if err != nil {
		panic(err)
	}
 
	cur, err := users.Aggregate(nil, mongo.Pipeline{
		{{"$lookup", bson.D{{"from", "orders"}, {"localField","orders"}, {"foreignField", "_id"}, {"as", "orders"}}}},
		{{"$unwind", "$orders"}},
		{{"$lookup", bson.D{{"from", "items"}, {"localField","orders.item"}, {"foreignField", "_id"}, {"as", "item"}}}},
		{{"$group", bson.D{{"_id", "$_id"}, {"name", bson.D{{"$first","$name"}}}, {"orders", bson.D{{"$push", "$orders"}}}}},
	}}, options.Aggregate())
	if err != nil{
		panic(err)
	}
 
	for cur.Next(nil) {
		u := new(fUser)
		cur.Decode(u)
		fmt.Println(u)
	}
 
}

But the result is:

&{ObjectID("5c0f4181ae3cac264bc9e1ec") Brown []}
&{ObjectID("5c0f4181ae3cac264bc9e1eb") John []}



 Comments   
Comment by Kristofer Brandow (Inactive) [ 12/Dec/18 ]

No problem!

--Kris

Comment by 성욱 조 [ 12/Dec/18 ]

It works!

It was just my silly mistake.

Really appreciate for your help.

Comment by Kristofer Brandow (Inactive) [ 11/Dec/18 ]

Hi ghatdev,

cursor.Decode is returning this error: "cannot decode objectID into a main.item".

It seems like the second $lookup stage is slightly wrong, using "item" instead of "orders.item" which is causing the returned documents to have an array of objectIDs instead of an array of embedded documents. Change the as property of the second $lookup return no errors from cursor.Decode and properly fills in the struct.

Let me know if this fixes your problem.

--Kris

Generated at Thu Feb 08 08:34:44 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.