Skip to content

Commit

Permalink
Add @noconflict directive to prevent conflict detection of predicate.
Browse files Browse the repository at this point in the history
  • Loading branch information
Arijit Das committed Dec 20, 2019
1 parent 6357d2e commit 9c0f3f4
Show file tree
Hide file tree
Showing 8 changed files with 457 additions and 246 deletions.
2 changes: 2 additions & 0 deletions posting/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,8 @@ func (l *List) addMutationInternal(ctx context.Context, txn *Txn, t *pb.Directed
return err
}
switch {
case schema.State().HasNoConflict(t.Attr):
break
case schema.State().HasUpsert(t.Attr):
// Consider checking to see if a email id is unique. A user adds:
// <uid> <email> "[email protected]", and there's a string equal tokenizer
Expand Down
3 changes: 3 additions & 0 deletions protos/pb.proto
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ message SchemaNode {
bool list = 7;
bool upsert = 8;
bool lang = 9;
bool no_conflict = 10;
}

message SchemaResult {
Expand Down Expand Up @@ -408,6 +409,8 @@ message SchemaUpdate {
// custom name. This field stores said name.
string object_type_name = 12;

bool no_conflict = 13;

// Deleted field:
reserved 7;
reserved "explicit";
Expand Down
573 changes: 328 additions & 245 deletions protos/pb/pb.pb.go

Large diffs are not rendered by default.

103 changes: 103 additions & 0 deletions query/query4_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ package query

import (
"context"
"encoding/json"
"fmt"
"testing"

"github.com/dgraph-io/dgo/v2"
"github.com/dgraph-io/dgo/v2/protos/api"
"github.com/dgraph-io/dgraph/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
)

func TestBigMathValue(t *testing.T) {
Expand Down Expand Up @@ -269,6 +276,102 @@ func TestDeleteAndReaddReverse(t *testing.T) {
setSchema(testSchema)
}

func TestSchemaUpdateNoConflict(t *testing.T) {
// Add new predicate with several indices.
s1 := testSchema + "\n noconflict_pred: string @noconflict .\n"
setSchema(s1)

// Verify queries work as expected.
q1 := `schema(pred: [noconflict_pred]) { }`
js := processQueryNoErr(t, q1)
require.JSONEq(t, `{
"data": {
"schema": [{
"predicate": "noconflict_pred",
"type": "string",
"no_conflict": true
}]
}
}`, js)

// Verify queries work as expected.
q1 = `schema(pred: [name]) { }`
js = processQueryNoErr(t, q1)
require.JSONEq(t, `{
"data": {
"schema": [{
"predicate": "name",
"type": "string",
"index": true,
"tokenizer": ["term", "exact", "trigram"],
"count": true,
"lang": true
}]
}
}`, js)
}

func TestNoConflictQuery(t *testing.T) {
conn, err := grpc.Dial(testutil.SockAddr, grpc.WithInsecure())
require.NoError(t, err)

dg := dgo.NewDgraphClient(api.NewDgraphClient(conn))
defer dg.Alter(context.Background(), &api.Operation{DropAll: true})

schema := `
type node {
name: string
child: uid
}
name: string @noconflict .
child: uid .
`
err = dg.Alter(context.Background(), &api.Operation{Schema: schema})
require.NoError(t, err)

type node struct {
ID string `json:"uid"`
Name string `json:"name"`
Child *node `json:"child"`
}

child := node{ID: "_:blank-0", Name: "child"}
js, err := json.Marshal(child)
require.NoError(t, err)

res, err := dg.NewTxn().Mutate(context.Background(),
&api.Mutation{SetJson: js, CommitNow: true})
require.NoError(t, err)

in := []node{}
for i := 0; i < 2; i++ {
in = append(in, node{ID: "_:blank-0", Name: fmt.Sprintf("%d", i+1),
Child: &node{ID: res.GetUids()["blank-0"]}})
}

errChan := make(chan error)
for i := range in {
go func(n node) {
js, err := json.Marshal(n)
require.NoError(t, err)

_, err = dg.NewTxn().Mutate(context.Background(),
&api.Mutation{SetJson: js, CommitNow: true})
errChan <- err
}(in[i])
}

errs := []error{}
for i := 0; i < len(in); i++ {
errs = append(errs, <-errChan)
}

for _, e := range errs {
assert.NoError(t, e)
}
}

func TestDropPredicate(t *testing.T) {
// Add new predicate with several indices.
s1 := testSchema + "\n numerology: string @index(term) .\n"
Expand Down
2 changes: 2 additions & 0 deletions schema/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ func parseDirective(it *lex.ItemIterator, schema *pb.SchemaUpdate, t types.TypeI
schema.Count = true
case "upsert":
schema.Upsert = true
case "noconflict":
schema.NoConflict = true
case "lang":
if t != types.StringID || schema.List {
return next.Errorf("@lang directive can only be specified for string type."+
Expand Down
7 changes: 7 additions & 0 deletions schema/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,13 @@ func TestParse8_Error(t *testing.T) {
require.Nil(t, result)
}

func TestParse9_Error(t *testing.T) {
reset()
result, err := Parse("age:uid @noconflict .")
require.NotNil(t, result)
require.Nil(t, err)
}

func TestParseScalarList(t *testing.T) {
reset()
result, err := Parse(`
Expand Down
9 changes: 9 additions & 0 deletions schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,15 @@ func (s *state) HasLang(pred string) bool {
return false
}

func (s *state) HasNoConflict(pred string) bool {
s.RLock()
defer s.RUnlock()
if schema, ok := s.predicate[pred]; ok {
return schema.NoConflict
}
return false
}

// Init resets the schema state, setting the underlying DB to the given pointer.
func Init(ps *badger.DB) {
pstore = ps
Expand Down
4 changes: 3 additions & 1 deletion worker/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func getSchema(ctx context.Context, s *pb.SchemaRequest) (*pb.SchemaResult, erro
fields = s.Fields
} else {
fields = []string{"type", "index", "tokenizer", "reverse", "count", "list", "upsert",
"lang"}
"lang", "noconflict"}
}

for _, attr := range predicates {
Expand Down Expand Up @@ -105,6 +105,8 @@ func populateSchema(attr string, fields []string) *pb.SchemaNode {
schemaNode.Upsert = schema.State().HasUpsert(attr)
case "lang":
schemaNode.Lang = schema.State().HasLang(attr)
case "noconflict":
schemaNode.NoConflict = schema.State().HasNoConflict(attr)
default:
//pass
}
Expand Down

0 comments on commit 9c0f3f4

Please sign in to comment.