dgraph-io / dgraph-io/dgraph

Multiple Mutations Create Invalid JSON with Duplicate Fields

Open
#9,422 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug Stale
Dominant language
Go
Stars
21.8k
Forks
1.6k
Avg merge
2d 5h
Merged PRs (30d)
9

Description

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the provided Go reproduction against Dgraph 24.1.2, using the schema and sequential delete/set mutations shown. Trace how multiple mutations are applied and how the query response JSON is produced. Done means a regression test confirms that the single-valued like edge returns only banana, with valid JSON and no duplicate fields.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.