Multiple Mutations Create Invalid JSON with Duplicate Fields
Nobody has claimed this yet.
- 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) withoutmu1(delete mutation), the behavior is correct - The issue only occurs when combining delete and set mutations in the same transaction
- The
likepredicate is defined asuid(single value), not[uid](list), so it should not contain multiple values
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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