Updating graph using struct with uid makes edges disappear (dgo)

Hi everyone, I have opened issue to this on dgo github, but I feel it will get more exposure here.

The problem: updating a node causes loss of deeper nested edges using dgo.

Graph before editing quote

{
	"uid": "0x2a85",
	"quote": "Moon",
	"car": "Lambo",
	"user.contact_details": {
	  "contact_details.mobile": {
		"phone.country_code": "+33",
		"phone.national_number": "98766543"
	  }
	}
  }

Graph after editing quote

{
	"uid": "0x2a85",
	"quote": "Jupiter",
	"car": "Lambo"
  }

Update worked, but user.contact_details edge was disconnected :man_shrugging:


Dgo go documentation says While setting an object if a struct has a Uid then its properties in the graph are updated else a new node is created.

I am providing code which:

  1. Creates User with quote , car and Contact details. (All good)
  2. Knowing UID of User I update quote . ( quote updated, car remains, however edge to Contact details disappeared)
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

	"github.com/dgraph-io/dgo/v200"
	"github.com/dgraph-io/dgo/v200/protos/api"
	"google.golang.org/grpc"
)

// User -
type User struct {
	UID            string             `json:"uid,omitempty"`
	DType          []string           `json:"dgraph.type,omitempty"`
	FavQuote       string             `json:"quote,omitempty"`
	FavCar         string             `json:"car,omitempty"`
	ContactDetails UserContactDetails `json:"user.contact_details,omitempty"`
}

// UserContactDetails -
type UserContactDetails struct {
	UID    string      `json:"uid,omitempty"`
	DType  []string    `json:"dgraph.type,omitempty"`
	Mobile PhoneNumber `json:"contact_details.mobile,omitempty"`
}

// PhoneNumber -
type PhoneNumber struct {
	UID            string   `json:"uid,omitempty"`
	DType          []string `json:"dgraph.type,omitempty"`
	CountryCode    string   `json:"phone.country_code,omitempty"`
	NationalNumber string   `json:"phone.national_number,omitempty"`
}

func main() {
	conn, err := grpc.Dial("localhost:9080", grpc.WithInsecure())
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	dg := dgo.NewDgraphClient(api.NewDgraphClient(conn))

	u := User{
		UID:      "_:newUser",
		DType:    []string{"User"},
		FavQuote: "Moon",
		FavCar:   "Lambo",
		ContactDetails: UserContactDetails{
			DType: []string{"ContactDetails"},
			Mobile: PhoneNumber{
				DType:          []string{"PhoneNumber"},
				CountryCode:    "+33",
				NationalNumber: "98766543",
			},
		},
	}

	op := &api.Operation{}
	op.Schema = `
		<phone.country_code>: string @index(exact) .
		<phone.national_number>: string @index(exact) .
		<quote>: string .
		<car>: string .

		type User {
			quote
			car
			user.contact_details: ContactDetails
		}

		type ContactDetails {
			contact_details.mobile: PhoneNumber
		}

		type PhoneNumber {
			phone.country_code
			phone.national_number
		}
	`

	ctx := context.Background()
	if err := dg.Alter(ctx, op); err != nil {
		log.Fatal(err)
	}

	// CREATING USER
	mu := &api.Mutation{
		CommitNow: true,
	}

	ub, err := json.Marshal(u)
	if err != nil {
		log.Fatal(err)
	}

	mu.SetJson = ub
	response, err := dg.NewTxn().Mutate(ctx, mu)
	if err != nil {
		log.Fatal(err)
	}

	uid := response.Uids["newUser"]
	fmt.Println("UID: " + uid)

	// User correctly created with all details and can be verified in Ratel

	// MODIFYING USER
	u = User{
		UID:      uid,
		FavQuote: "Jupiter",
	}

	mu = &api.Mutation{
		CommitNow: true,
	}

	ub, err = json.Marshal(u)
	if err != nil {
		log.Fatal(err)
	}

	mu.SetJson = ub
	_, err = dg.NewTxn().Mutate(ctx, mu)
	if err != nil {
		log.Fatal(err)
	}

	// User updated `FavQuote`. "FavCar" remains however contact details edge from user have dissapeared !?
}

For querying data in Ratel I use

{
  u(func: uid(0x2a85)) {
    uid
    quote
    car
    user.contact_details {
      contact_details.mobile {
        phone.country_code
        phone.national_number
      }
    }
  }
}

Any ideas much appreciated!

1 Like

Hi @AugustDev, Welcome to Dgraph community.

Yes, most of our community is active here and you may expect a quicker response here.

This seems like a bug. The updates are failing for deeper nested mutations. Thanks for letting us know this. I have added the tag to the issue and our team will try to pick it up soon.

EDIT: As mentioned below by @AugustDev, it was a programming bug. :slight_smile:

Thanks for reply. However as someone pointed out on Slacks. The problem is because of the way go serializes structs. Every works as expected if I change UserContactDetails to * UserContactDetails, because empty elements are no longer initialized to default values and are simply omitted.

2 Likes