-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Open
Labels
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
like
predicate is defined asuid
(single value), not[uid]
(list), so it should not contain multiple values