dolan-in/dgman

Recursive type detection on mutate

Closed this issue · 5 comments

Hello,

First of all, thank you for this very helpful lib.
I've a question (maybe an issue) on mutation when the object to mutate enclose other object with different types, for exemple :

type T1 struct {
	UID  string `json:"uid,omitempty"`
	Name string `json:"name,omitempty"`
	T2s  []T2   `json:"t2s,omitempty"`
}

type T2 struct {
	UID string `json:"uid,omitempty"`
	Age int    `json:"age,omitempty"`
	T3s []T3   `json:"t3s,omitempty"`
}

type T3 struct {
	UID   string `json:"uid,omitempty"`
	Color string `json:"color,omitempty"`
}

The schema generation work well, but...

Lets say I would like to persist this kind of value :

	test := T1{
		Name: "testName",
		T2s: []T2{
			{
				Age: 30,
				T3s: []T3{
					{
						Color: "blue",
					},
					{
						Color: "Red",
					},
				},
			},
		},
	}

It seems dgman will generate a JSON GraphQL+ representation with only one "dgraph.type" predicate, in this case `"dgraph.type": "T1", omitting the type for T2 and T3 values.

This is problematics on last versions of dgraph (> v1.1.0) because :

  • expand(_all_) work now only on typed nodes
  • dgman Get use expand(_all_) to discover node values and fill target structure

Then, when I try to get an T1 node type in a T1 struct I will get only the first level of data despite the depthParam.

I've I missed something ? or is it a real issue ?

Yes this is a real issue that I have come across while developing the lib. I might or might not have a fix for this.

Currently, for each depth of the node structure, you will need to create a separate mutation. For example:

// Currently, you must use a slice of pointer structs, so UIDs can be injected
// might need to fix this too allowing slice of structs
t3s := []*T3{
	{
		Color: "blue",
	},
	{
		Color: "Red",
	},
}
if err := dgman.NewTxn(c).Mutate(&t3s, true); err != nil {
	log.Println(err)
}
t2s := []*T2{
	Age: 30,
	T3s: t3s, // uid should be injected on t3s, connecting the nodes
}
if err := dgman.NewTxn(c).Mutate(&t2s, true); err != nil {
	log.Println(err)
}
t1 := T1{
	Name: "testName",
	T2s: t2s,
}
if err := dgman.NewTxn(c).Mutate(&t1, true); err != nil {
	log.Println(err)
}
log.Println(t1) // uids should be populated on every depth

// try to query the data
var t1 T1
err := dgman.NewReadOnlyTxn(c).Get(&t1).
	Filter("eq(name, $1)", "testName").
	All(3).
	Node()

log.Println(t1) // should resolve nodes tree until T3

Thank you pointing out this issue, I will evaluate my priorities on the development of this lib, if this an urgent issue.

Thank you for your response.
Another solution is to overwrite MarshalSHON method for each sub-structure.
Then T1 (with sub-structures) can be mutated directly.

I'll try to generify that in marshalAndInjectType method.

func (p *T2) MarshalJSON() ([]byte, error) {
	type Copy T2
	v := *p
	return json.Marshal(&struct {
		DgraphType string `json:"dgraph.type"`
		*Copy
	}{
		DgraphType: reflect.TypeOf(v).Name(),
		Copy:       (*Copy)(p),
	})
}

Cool.. That would be great, contributions are highly welcome.

Though we can't make assumptions on the underlying type, without doing reflection, but we might go down that route.

Hi @protheusfr, this is fixed in #36 .

It allows recursive type detection, though it requires dgraph.type field to be present on the struct.

Great! thank you @wildan2711 .