dgraph icon indicating copy to clipboard operation
dgraph copied to clipboard

Multiple Mutations Create Invalid JSON with Duplicate Fields

Open escquery opened this issue 8 months ago • 2 comments

Environment

  • Dgraph version: 24.1.2
  • dgo version: v240

Issue Summary

When using multiple mutations in a single transaction where the first mutation deletes an edge and the second mutation creates a new edge, the resulting JSON contains duplicate fields for the same edge predicate, even though the edge is defined as uid (not [uid]). This produces invalid JSON with two identical field names in the same object.

Test Schema

<name>: string @index(exact) .
<like>: uid @reverse .
<fruit>: string @index(exact) .

type Person {
	name
	like
}
type Fruit {
	fruit
}

Test Data

{
    set {
        _:person <name> "Tom" .
        _:person <dgraph.type> "Person" .
        _:apple <fruit> "apple" .
        _:apple <dgraph.type> "Fruit" .
        _:banana <fruit> "banana" .
        _:banana <dgraph.type> "Fruit" .
        _:person <like> _:apple .  
    }
}

Test Code

package main

import (
	"context"
	"fmt"
	"log"

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

func query() {
	conn, err := grpc.NewClient("localhost:9080", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	dg := dgo.NewDgraphClient(api.NewDgraphClient(conn))
	dql := `{
  q(func: eq(name, "Tom")) {
	uid
	like { uid fruit }
  }
}`
	req := &api.Request{
		Query: dql,
	}

	response, err := dg.NewTxn().Do(context.Background(), req)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%v\n", string(response.Json))
}

func mutation() {
	conn, err := grpc.NewClient("localhost:9080", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	dg := dgo.NewDgraphClient(api.NewDgraphClient(conn))
	dql := `{
	  person as var(func: eq(name, "Tom"))
	  banana as var(func: eq(fruit, "banana"))
	  }`
	mu1 := &api.Mutation{
		DelNquads: []byte(`uid(person) <like> * .`),
		// CommitNow: true,
	}
	mu2 := &api.Mutation{
		SetNquads: []byte(`uid(person) <like> uid(banana) .`),
		// CommitNow: true,
	}
	req := &api.Request{
		Query:     dql,
		Mutations: []*api.Mutation{mu1, mu2},
		CommitNow: true,
	}
	txn := dg.NewTxn()
	response, err := txn.Do(context.Background(), req)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%v\n", response)
}

func main() {
	mutation()
	query()
}

Actual Result

json:"{}"  txn:{start_ts:1071  commit_ts:1072  preds:"1-0-like"}  latency:{parsing_ns:48115  processing_ns:873283  encoding_ns:13938  assign_timestamp_ns:382101  total_ns:1412244}  metrics:{num_uids:{key:"_total"  value:2}  num_uids:{key:"fruit"  value:0}  num_uids:{key:"mutation_cost"  value:2}  num_uids:{key:"name"  value:0}}  hdrs:{key:"content-type"  value:{value:"application/grpc"}}  hdrs:{key:"dgraph-toucheduids"  value:{value:"2"}}
{"q":[{"uid":"0x1","like":{"uid":"0x2","fruit":"apple","uid":"0x3","fruit":"banana"}}]}

Problem Description

The query returns invalid JSON where the like object contains duplicate uid fields:

"like":{"uid":"0x2","fruit":"apple","uid":"0x3","fruit":"banana"}

This is invalid JSON since objects cannot have duplicate keys. The edge should only point to one UID (banana) after the mutation, not both the old (apple) and new (banana) values.

Expected Result

After the mutations, the query should return:

{"q":[{"uid":"0x1","like":{"uid":"0x3","fruit":"banana"}}]}

Additional Notes

  • When using only mu2 (set mutation) without mu1 (delete mutation), the behavior is correct
  • The issue only occurs when combining delete and set mutations in the same transaction
  • The like predicate is defined as uid (single value), not [uid] (list), so it should not contain multiple values

escquery avatar May 22 '25 01:05 escquery

This is similar to the issue described in https://discuss.hypermode.com/t/inconsistent-query-result/19844/8. A fix (https://github.com/hypermodeinc/dgraph/pull/9378) for this has been merged but has not been released yet. You can check if this fixes your problem by trying the nightly docker image or building from source.

xqqp avatar May 22 '25 14:05 xqqp