From 8f3614a0f6a1bfd35d6b47eae7fdd75316b9627c Mon Sep 17 00:00:00 2001 From: Aravind Rao Date: Tue, 18 Jul 2017 14:49:04 -0700 Subject: [PATCH 1/6] GRAMMAR section of DECONSTRUCT is complete. --- bql/grammar/grammar.go | 62 ++++++++++++++++++++++++++- bql/grammar/grammar_test.go | 85 ++++++++++++++++++++++++++++++++++++- bql/lexer/lexer.go | 9 ++++ bql/semantic/semantic.go | 4 ++ 4 files changed, 157 insertions(+), 3 deletions(-) diff --git a/bql/grammar/grammar.go b/bql/grammar/grammar.go index 3d6a3a69..48ddea4c 100644 --- a/bql/grammar/grammar.go +++ b/bql/grammar/grammar.go @@ -101,6 +101,19 @@ func BQL() *Grammar { NewTokenType(lexer.ItemSemicolon), }, }, + { + Elements: []Element{ + NewTokenType(lexer.ItemDeconstruct), + NewSymbol("DECONSTRUCT_FACTS"), + NewTokenType(lexer.ItemAt), + NewSymbol("OUTPUT_GRAPHS"), + NewTokenType(lexer.ItemFrom), + NewSymbol("INPUT_GRAPHS"), + NewSymbol("WHERE"), + NewSymbol("HAVING"), + NewTokenType(lexer.ItemSemicolon), + }, + }, }, "CREATE_GRAPHS": []*Clause{ { @@ -879,6 +892,50 @@ func BQL() *Grammar { }, {}, }, + "DECONSTRUCT_FACTS": []*Clause{ + { + Elements: []Element{ + NewTokenType(lexer.ItemLBracket), + NewSymbol("DECONSTRUCT_TRIPLES"), + NewTokenType(lexer.ItemRBracket), + }, + }, + }, + "DECONSTRUCT_TRIPLES": []*Clause{ + { + Elements: []Element{ + NewTokenType(lexer.ItemNode), + NewSymbol("CONSTRUCT_PREDICATE"), + NewSymbol("CONSTRUCT_OBJECT"), + NewSymbol("MORE_DECONSTRUCT_TRIPLES"), + }, + }, + { + Elements: []Element{ + NewTokenType(lexer.ItemBlankNode), + NewSymbol("CONSTRUCT_PREDICATE"), + NewSymbol("CONSTRUCT_OBJECT"), + NewSymbol("MORE_DECONSTRUCT_TRIPLES"), + }, + }, + { + Elements: []Element{ + NewTokenType(lexer.ItemBinding), + NewSymbol("CONSTRUCT_PREDICATE"), + NewSymbol("CONSTRUCT_OBJECT"), + NewSymbol("MORE_DECONSTRUCT_TRIPLES"), + }, + }, + }, + "MORE_DECONSTRUCT_TRIPLES": []*Clause{ + { + Elements: []Element{ + NewTokenType(lexer.ItemDot), + NewSymbol("DECONSTRUCT_TRIPLES"), + }, + }, + {}, + }, } } @@ -1003,7 +1060,7 @@ func SemanticBQL() *Grammar { // CONSTRUCT clause semantic hooks. setClauseHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_FACTS"}, semantic.InitWorkingConstructClauseHook(), semantic.TypeBindingClauseHook(semantic.Construct)) - constructTriplesSymbols := []semantic.Symbol{"CONSTRUCT_TRIPLES", "MORE_CONSTRUCT_TRIPLES"} + constructTriplesSymbols := []semantic.Symbol{"CONSTRUCT_TRIPLES", "MORE_CONSTRUCT_TRIPLES", "DECONSTRUCT_TRIPLES", "MORE_DECONSTRUCT_TRIPLES"} setClauseHook(semanticBQL, constructTriplesSymbols, semantic.NextWorkingConstructClauseHook(), semantic.NextWorkingConstructClauseHook()) setClauseHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_PREDICATE"}, semantic.NextWorkingConstructPredicateObjectPairClauseHook(), nil) setClauseHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_OBJECT"}, nil, semantic.NextWorkingConstructPredicateObjectPairClauseHook()) @@ -1012,5 +1069,8 @@ func SemanticBQL() *Grammar { setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_PREDICATE"}, semantic.ConstructPredicateHook(), nil) setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_OBJECT"}, semantic.ConstructObjectHook(), nil) + // DECONSTRUCT clause semantic hooks. + setClauseHook(semanticBQL, []semantic.Symbol{"DECONSTRUCT_FACTS"}, semantic.InitWorkingConstructClauseHook(), semantic.TypeBindingClauseHook(semantic.Deconstruct)) + return semanticBQL } diff --git a/bql/grammar/grammar_test.go b/bql/grammar/grammar_test.go index e6b9509a..6b637eaa 100644 --- a/bql/grammar/grammar_test.go +++ b/bql/grammar/grammar_test.go @@ -145,6 +145,18 @@ func TestAcceptByParse(t *testing.T) { from ?b where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2. ?s "old_predicate_3"@[,] ?o3};`, + // Test Deconstruct clause. + `deconstruct {?s "new_predicate"@[] ?o} at ?a from ?b where {?s "old_predicate"@[,] ?o} having ?s = ?o;`, + `deconstruct {?s "new_predicate"@[] ?o} at ?a from ?b where {?s "old_predicate"@[,] ?o};`, + `deconstruct {?s ?p ?o. + _:v "_subject"@[] ?s. + _:v "_predicate"@[] ?p. + _:v "_object"@[] ?o} + at ?a, ?b + from ?c, ?d + where {?n "_subject"@[] ?s. + ?n "_predicate"@[] ?p. + ?n "_object"@[] ?o};`, } p, err := NewParser(BQL()) if err != nil { @@ -262,6 +274,29 @@ func TestRejectByParse(t *testing.T) { from ?b where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2};`, + // Deconstruct clause without source. + `deconstruct {?s "foo"@[,] ?o} at ?a where{?s "foo"@[,] ?o} having ?s = ?o;`, + // Deconstruct clause without destination. + `deconstruct {?s "foo"@[,] ?o} from ?b where{?s "foo"@[,] ?o} having ?s = ?o;`, + // Deconstruct clause with badly formed blank node. + `deconstruct {?s ?p ?o. + _v "some_pred"@[] ?k} + at ?a + from ?b + where {?s "foo"@[,] ?o};`, + // Deconstruct clause with badly formed triple. + `deconstruct {?s ?p ?o. + _:v "some_pred"@[]} + at ?a + from ?b + where {?s "foo"@[,] ?o};`, + // Deconstruct clause with multiple predicate-object pairs. + `deconstruct {?s "predicate_1"@[] ?o1; + "predicate_1"@[] ?o1} + at ?a + from ?b + where {?s "old_predicate_1"@[,] ?o1. + ?s "old_predicate_2"@[,] ?o2};`, } p, err := NewParser(BQL()) if err != nil { @@ -319,7 +354,7 @@ func TestAcceptGraphOpsByParseAndSemantic(t *testing.T) { ?s "old_predicate_2"@[,] ?o2. ?s "old_predicate_3"@[,] ?o3};`, empty, []string{"?b"}, []string{"?a"}, 0}, - // construct data into multiple output graphs from multple input graphs. + // Construct data into multiple output graphs from multple input graphs. {`construct {?s "predicate_1"@[] ?o1; "predicate_2"@[] ?o2} into ?a, ?b @@ -327,6 +362,22 @@ func TestAcceptGraphOpsByParseAndSemantic(t *testing.T) { where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2. ?s "old_predicate_3"@[,] ?o3};`, empty, []string{"?c", "?d"}, []string{"?a", "?b"}, 0}, + + // Deconstruct data. Graphs can be input or output graphs. + {`deconstruct {?s "predicate_1"@[] ?o1} + at ?a + from ?b + where {?s "old_predicate_1"@[,] ?o1. + ?s "old_predicate_2"@[,] ?o2. + ?s "old_predicate_3"@[,] ?o3};`, empty, []string{"?b"}, []string{"?a"}, 0}, + + // Construct data into multiple output graphs from multple input graphs. + {`deconstruct {?s "predicate_1"@[] ?o1} + at ?a, ?b + from ?c, ?d + where {?s "old_predicate_1"@[,] ?o1. + ?s "old_predicate_2"@[,] ?o2. + ?s "old_predicate_3"@[,] ?o3};`, empty, []string{"?c", "?d"}, []string{"?a", "?b"}, 0}, } p, err := NewParser(SemanticBQL()) if err != nil { @@ -461,7 +512,7 @@ func TestSemanticStatementGraphClausesLengthCorrectness(t *testing.T) { } } -func TestSemanticStatementConstructClausesLengthCorrectness(t *testing.T) { +func TestSemanticStatementConstructDeconstructClausesLengthCorrectness(t *testing.T) { table := []struct { query string want int @@ -487,6 +538,25 @@ func TestSemanticStatementConstructClausesLengthCorrectness(t *testing.T) { ?s "old_predicate_3"@[,] ?o3};`, want: 2, }, + { + query: `deconstruct {?s "predicate_1"@[] ?o1} + at ?a + from ?b + where {?s "old_predicate_1"@[,] ?o1. + ?s "old_predicate_2"@[,] ?o2. + ?s "old_predicate_3"@[,] ?o3};`, + want: 1, + }, + { + query: `deconstruct {?s "predicate_1"@[] ?o1. + ?s "predicate_3"@[] ?o3} + at ?a + from ?b + where {?s "old_predicate_1"@[,] ?o1. + ?s "old_predicate_2"@[,] ?o2. + ?s "old_predicate_3"@[,] ?o3};`, + want: 2, + }, } p, err := NewParser(SemanticBQL()) if err != nil { @@ -578,6 +648,17 @@ func TestSemanticStatementPredicateObjectPairsLengthCorrectness(t *testing.T) { wantOne: 3, wantTwo: 3, }, + { + query: `deconstruct {?s "predicate_1"@[] ?o1. + ?s1 "predicate_1"@[] ?o1} + at ?a + from ?b + where {?s "old_predicate_1"@[,] ?o1. + ?s "old_predicate_2"@[,] ?o2. + ?s1 "old_predicate_3"@[,] AT ?t ?o3};`, + want_one: 1, + want_two: 1, + }, } p, err := NewParser(SemanticBQL()) if err != nil { diff --git a/bql/lexer/lexer.go b/bql/lexer/lexer.go index c0d1d3db..ca73ef43 100644 --- a/bql/lexer/lexer.go +++ b/bql/lexer/lexer.go @@ -43,6 +43,8 @@ const ( ItemCreate // ItemConstruct represents the construct keyword in BQL. ItemConstruct + // ItemDeconstruct represents the deconstruct keyword in BQL. + ItemDeconstruct // ItemDrop represent the destruction of a graph in BQL. ItemDrop // ItemGraph represent the graph to be created of destroyed in BQL. @@ -145,6 +147,8 @@ func (tt TokenType) String() string { return "CREATE" case ItemConstruct: return "CONSTRUCT" + case ItemDeconstruct: + return "DECONSTRUCT" case ItemDrop: return "DROP" case ItemGraph: @@ -262,6 +266,7 @@ const ( delete = "delete" create = "create" construct = "construct" + deconstruct = "deconstruct" drop = "drop" graph = "graph" data = "data" @@ -475,6 +480,10 @@ func lexKeyword(l *lexer) stateFn { consumeKeyword(l, ItemConstruct) return lexSpace } + if strings.EqualFold(input, deconstruct) { + consumeKeyword(l, ItemDeconstruct) + return lexSpace + } if strings.EqualFold(input, drop) { consumeKeyword(l, ItemDrop) return lexSpace diff --git a/bql/semantic/semantic.go b/bql/semantic/semantic.go index b42780f8..1263dcda 100644 --- a/bql/semantic/semantic.go +++ b/bql/semantic/semantic.go @@ -50,6 +50,8 @@ const ( Drop // Construct statement. Construct + // Deconstruct statement. + Deconstruct ) // String provides a readable version of the StatementType. @@ -67,6 +69,8 @@ func (t StatementType) String() string { return "DROP" case Construct: return "CONSTRUCT" + case Deconstruct: + return "DECONSTRUCT" default: return "UNKNOWN" } From e2301191bda8251887ea880b1dcbef0dc47e1735 Mon Sep 17 00:00:00 2001 From: Aravind Rao Date: Tue, 18 Jul 2017 18:19:21 -0700 Subject: [PATCH 2/6] Modified Planner to support DECONSTRUCT --- bql/planner/planner.go | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/bql/planner/planner.go b/bql/planner/planner.go index 4d5f80c9..c7bf563f 100644 --- a/bql/planner/planner.go +++ b/bql/planner/planner.go @@ -818,18 +818,23 @@ func (p *queryPlan) String() string { } // constructPlan encapsulates the sequence of instructions that need to be -// executed in order to satisfy the execution of a valid construct BQL statement. +// executed in order to satisfy the execution of a valid construct or deconstruct +// BQL statement. type constructPlan struct { stm *semantic.Statement store storage.Store tracer io.Writer bulkSize int queryPlan *queryPlan + construct bool } // Type returns the type of plan used by the executor. func (p *constructPlan) Type() string { - return "CONSTRUCT" + if p.construct { + return "CONSTRUCT" + } + return "DECONSTRUCT" } func (p *constructPlan) processPredicateObjectPair(pop *semantic.ConstructPredicateObjectPair, tbl *table.Table, r table.Row) (*predicate.Predicate, *triple.Object, error) { @@ -926,9 +931,17 @@ func (p *constructPlan) Execute(ctx context.Context) (*table.Table, error) { var ts []*triple.Triple updateFunc := func(g storage.Graph, d []*triple.Triple) error { trace(p.tracer, func() []string { - return []string{"Inserting triples to graph \"" + g.ID(ctx) + "\""} + return []string{"removing triples from graph \"" + g.ID(ctx) + "\""} }) - return g.AddTriples(ctx, d) + return g.RemoveTriples(ctx, d) + } + if p.construct { + updateFunc = func(g storage.Graph, d []*triple.Triple) error { + trace(p.tracer, func() []string { + return []string{"Inserting triples to graph \"" + g.ID(ctx) + "\""} + }) + return g.AddTriples(ctx, d) + } } for elem := range tripChan { ts = append(ts, elem) @@ -982,7 +995,10 @@ func (p *constructPlan) Execute(ctx context.Context) (*table.Table, error) { // String returns a readable description of the execution plan. func (p *constructPlan) String() string { - b := bytes.NewBufferString("CONSTRUCT plan:\n\n") + b := bytes.NewBufferString("DECONSTRUCT plan:\n\n") + if p.construct { + b = bytes.NewBufferString("CONSTRUCT plan:\n\n") + } b.WriteString("Input graphs:\n") for _, gn := range p.stm.InputGraphNames() { b.WriteString(fmt.Sprintf("\t%v\n", gn)) @@ -1036,6 +1052,17 @@ func New(ctx context.Context, store storage.Store, stm *semantic.Statement, chan tracer: w, bulkSize: bulkSize, queryPlan: qp, + construct: true, + }, nil + case semantic.Deconstruct: + qp, _ := newQueryPlan(ctx, store, stm, chanSize, w) + return &constructPlan{ + stm: stm, + store: store, + tracer: w, + bulkSize: bulkSize, + queryPlan: qp, + construct: false, }, nil default: return nil, fmt.Errorf("planner.New: unknown statement type in statement %v", stm) From 721cf7dadb0eb16556f778b6c5687611702bdc47 Mon Sep 17 00:00:00 2001 From: Aravind Rao Date: Wed, 19 Jul 2017 17:30:47 -0700 Subject: [PATCH 3/6] Planner tests + grammar tweak --- bql/grammar/grammar.go | 12 ++--- bql/grammar/grammar_test.go | 6 +-- bql/planner/planner.go | 2 +- bql/planner/planner_test.go | 100 ++++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 11 deletions(-) diff --git a/bql/grammar/grammar.go b/bql/grammar/grammar.go index 48ddea4c..bf4ac76c 100644 --- a/bql/grammar/grammar.go +++ b/bql/grammar/grammar.go @@ -1058,19 +1058,17 @@ func SemanticBQL() *Grammar { }) setClauseHook(semanticBQL, []semantic.Symbol{"START"}, nil, semantic.GroupByBindingsChecker()) - // CONSTRUCT clause semantic hooks. + // CONSTRUCT and DECONSTRUCT clauses semantic hooks. setClauseHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_FACTS"}, semantic.InitWorkingConstructClauseHook(), semantic.TypeBindingClauseHook(semantic.Construct)) - constructTriplesSymbols := []semantic.Symbol{"CONSTRUCT_TRIPLES", "MORE_CONSTRUCT_TRIPLES", "DECONSTRUCT_TRIPLES", "MORE_DECONSTRUCT_TRIPLES"} - setClauseHook(semanticBQL, constructTriplesSymbols, semantic.NextWorkingConstructClauseHook(), semantic.NextWorkingConstructClauseHook()) + setClauseHook(semanticBQL, []semantic.Symbol{"DECONSTRUCT_FACTS"}, semantic.InitWorkingConstructClauseHook(), semantic.TypeBindingClauseHook(semantic.Deconstruct)) + constructAndDeconstructTriplesSymbols := []semantic.Symbol{"CONSTRUCT_TRIPLES", "MORE_CONSTRUCT_TRIPLES", "DECONSTRUCT_TRIPLES", "MORE_DECONSTRUCT_TRIPLES"} + setClauseHook(semanticBQL, constructAndDeconstructTriplesSymbols, semantic.NextWorkingConstructClauseHook(), semantic.NextWorkingConstructClauseHook()) setClauseHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_PREDICATE"}, semantic.NextWorkingConstructPredicateObjectPairClauseHook(), nil) setClauseHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_OBJECT"}, nil, semantic.NextWorkingConstructPredicateObjectPairClauseHook()) - setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_TRIPLES"}, semantic.ConstructSubjectHook(), nil) + setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_TRIPLES", "DECONSTRUCT_TRIPLES"}, semantic.ConstructSubjectHook(), nil) setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_PREDICATE"}, semantic.ConstructPredicateHook(), nil) setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_OBJECT"}, semantic.ConstructObjectHook(), nil) - // DECONSTRUCT clause semantic hooks. - setClauseHook(semanticBQL, []semantic.Symbol{"DECONSTRUCT_FACTS"}, semantic.InitWorkingConstructClauseHook(), semantic.TypeBindingClauseHook(semantic.Deconstruct)) - return semanticBQL } diff --git a/bql/grammar/grammar_test.go b/bql/grammar/grammar_test.go index 6b637eaa..6c7e27f3 100644 --- a/bql/grammar/grammar_test.go +++ b/bql/grammar/grammar_test.go @@ -371,7 +371,7 @@ func TestAcceptGraphOpsByParseAndSemantic(t *testing.T) { ?s "old_predicate_2"@[,] ?o2. ?s "old_predicate_3"@[,] ?o3};`, empty, []string{"?b"}, []string{"?a"}, 0}, - // Construct data into multiple output graphs from multple input graphs. + // Deconstruct data at multiple output graphs from multple input graphs. {`deconstruct {?s "predicate_1"@[] ?o1} at ?a, ?b from ?c, ?d @@ -656,8 +656,8 @@ func TestSemanticStatementPredicateObjectPairsLengthCorrectness(t *testing.T) { where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2. ?s1 "old_predicate_3"@[,] AT ?t ?o3};`, - want_one: 1, - want_two: 1, + wantOne: 1, + wantTwo: 1, }, } p, err := NewParser(SemanticBQL()) diff --git a/bql/planner/planner.go b/bql/planner/planner.go index c7bf563f..103ba8c3 100644 --- a/bql/planner/planner.go +++ b/bql/planner/planner.go @@ -931,7 +931,7 @@ func (p *constructPlan) Execute(ctx context.Context) (*table.Table, error) { var ts []*triple.Triple updateFunc := func(g storage.Graph, d []*triple.Triple) error { trace(p.tracer, func() []string { - return []string{"removing triples from graph \"" + g.ID(ctx) + "\""} + return []string{"Removing triples from graph \"" + g.ID(ctx) + "\""} }) return g.RemoveTriples(ctx, d) } diff --git a/bql/planner/planner_test.go b/bql/planner/planner_test.go index c71f970e..c997730d 100644 --- a/bql/planner/planner_test.go +++ b/bql/planner/planner_test.go @@ -78,6 +78,20 @@ const ( constructTestDestTriples = `/person "met"@[] /person ` + deconstructTestSrcTriples = `/person "lives_in"@[] /city + /person "lives_in"@[] /city + /person "lives_in"@[] /city + /person "lives_in"@[] /city + ` + + deconstructTestDestTriples = `/person "met"@[] /person + /person "met"@[] /person + /person "met"@[] /person + /person "met"@[] /person + /person "met"@[] /person + /person "met"@[] /person + ` + testTriples = originalTriples + tripleFromIssue40 ) @@ -732,6 +746,92 @@ func TestPlannerConstructAddsCorrectTriples(t *testing.T) { } } +func TestPlannerDeconstructRemovesCorrectTriples(t *testing.T) { + testTable := []struct { + s string + trps []string + }{ + { + s: `deconstruct {?p1 "met"@[] ?p2} + at ?dest + from ?src + where {?p1 "lives_in"@[] /city. + ?p2 "lives_in"@[] /city};`, + trps: []string{fmt.Sprintf("%s\t%s\t%s", `/person`, `"met"@[]`, `/person`), + fmt.Sprintf("%s\t%s\t%s", `/person`, `"met"@[]`, `/person`), + fmt.Sprintf("%s\t%s\t%s", `/person`, `"met"@[]`, `/person`), + fmt.Sprintf("%s\t%s\t%s", `/person`, `"met"@[]`, `/person`)}, + }, + { + s: `deconstruct {?p1 "met"@[] ?p2. + ?p2 "met"@[] ?p1} + at ?dest + from ?src + where {?p1 "lives_in"@[] /city. + ?p2 "lives_in"@[] /city};`, + trps: []string{fmt.Sprintf("%s\t%s\t%s", `/person`, `"met"@[]`, `/person`), + fmt.Sprintf("%s\t%s\t%s", `/person`, `"met"@[]`, `/person`)}, + }, + } + p, err := grammar.NewParser(grammar.SemanticBQL()) + if err != nil { + t.Errorf("grammar.NewParser: should have produced a valid BQL parser, %v", err) + } + for _, entry := range testTable { + + s, ctx := memory.NewStore(), context.Background() + populateStoreWithTriples(ctx, s, "?src", deconstructTestSrcTriples, t) + populateStoreWithTriples(ctx, s, "?dest", deconstructTestDestTriples, t) + + st := &semantic.Statement{} + if err := p.Parse(grammar.NewLLk(entry.s, 1), st); err != nil { + t.Errorf("Parser.consume: failed to parse query %q with error %v", entry.s, err) + } + plnr, err := New(ctx, s, st, 0, 10, nil) + if err != nil { + t.Errorf("planner.New failed to create a valid query plan with error %v", err) + } + _, err = plnr.Execute(ctx) + if err != nil { + t.Errorf("planner.Execute failed for query %q with error %v", entry.s, err) + continue + } + + g, err := s.Graph(ctx, "?dest") + if err != nil { + t.Errorf("memory.DefaultStore.Graph(%q) should have not fail with error %v", "?test", err) + } + + ts := make(chan *triple.Triple) + go func() { + if err := g.Triples(ctx, storage.DefaultLookup, ts); err != nil { + t.Error(err) + } + }() + + dt := make(map[string]bool) + for _, trp := range entry.trps { + dt[trp] = false + } + + i := 0 + for trp := range ts { + if val, ok := dt[trp.String()]; ok { + if !val { + i++ + } + dt[trp.String()] = true + } else { + t.Errorf("unexpected triple: %v added to graph", t) + } + } + if i != len(entry.trps) { + t.Errorf("g.Triples did not return some of the triples.") + } + } + +} + func TestTreeTraversalToRoot(t *testing.T) { // Graph traversal data. traversalTriples := `/person "born in"@[] /city From b60eced959db9212870f03e05a79f095985873d6 Mon Sep 17 00:00:00 2001 From: Aravind Rao Date: Wed, 19 Jul 2017 18:01:56 -0700 Subject: [PATCH 4/6] Changed AT keyword to IN --- bql/grammar/grammar.go | 2 +- bql/grammar/grammar_test.go | 24 ++++++++++++------------ bql/lexer/lexer.go | 9 +++++++++ bql/planner/planner_test.go | 6 +++--- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/bql/grammar/grammar.go b/bql/grammar/grammar.go index bf4ac76c..75817775 100644 --- a/bql/grammar/grammar.go +++ b/bql/grammar/grammar.go @@ -105,7 +105,7 @@ func BQL() *Grammar { Elements: []Element{ NewTokenType(lexer.ItemDeconstruct), NewSymbol("DECONSTRUCT_FACTS"), - NewTokenType(lexer.ItemAt), + NewTokenType(lexer.ItemIn), NewSymbol("OUTPUT_GRAPHS"), NewTokenType(lexer.ItemFrom), NewSymbol("INPUT_GRAPHS"), diff --git a/bql/grammar/grammar_test.go b/bql/grammar/grammar_test.go index 6c7e27f3..1dbe118f 100644 --- a/bql/grammar/grammar_test.go +++ b/bql/grammar/grammar_test.go @@ -146,13 +146,13 @@ func TestAcceptByParse(t *testing.T) { ?s "old_predicate_2"@[,] ?o2. ?s "old_predicate_3"@[,] ?o3};`, // Test Deconstruct clause. - `deconstruct {?s "new_predicate"@[] ?o} at ?a from ?b where {?s "old_predicate"@[,] ?o} having ?s = ?o;`, - `deconstruct {?s "new_predicate"@[] ?o} at ?a from ?b where {?s "old_predicate"@[,] ?o};`, + `deconstruct {?s "new_predicate"@[] ?o} in ?a from ?b where {?s "old_predicate"@[,] ?o} having ?s = ?o;`, + `deconstruct {?s "new_predicate"@[] ?o} in ?a from ?b where {?s "old_predicate"@[,] ?o};`, `deconstruct {?s ?p ?o. _:v "_subject"@[] ?s. _:v "_predicate"@[] ?p. _:v "_object"@[] ?o} - at ?a, ?b + in ?a, ?b from ?c, ?d where {?n "_subject"@[] ?s. ?n "_predicate"@[] ?p. @@ -275,25 +275,25 @@ func TestRejectByParse(t *testing.T) { where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2};`, // Deconstruct clause without source. - `deconstruct {?s "foo"@[,] ?o} at ?a where{?s "foo"@[,] ?o} having ?s = ?o;`, + `deconstruct {?s "foo"@[,] ?o} in ?a where{?s "foo"@[,] ?o} having ?s = ?o;`, // Deconstruct clause without destination. `deconstruct {?s "foo"@[,] ?o} from ?b where{?s "foo"@[,] ?o} having ?s = ?o;`, // Deconstruct clause with badly formed blank node. `deconstruct {?s ?p ?o. _v "some_pred"@[] ?k} - at ?a + in ?a from ?b where {?s "foo"@[,] ?o};`, // Deconstruct clause with badly formed triple. `deconstruct {?s ?p ?o. _:v "some_pred"@[]} - at ?a + in ?a from ?b where {?s "foo"@[,] ?o};`, // Deconstruct clause with multiple predicate-object pairs. `deconstruct {?s "predicate_1"@[] ?o1; "predicate_1"@[] ?o1} - at ?a + in ?a from ?b where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2};`, @@ -365,7 +365,7 @@ func TestAcceptGraphOpsByParseAndSemantic(t *testing.T) { // Deconstruct data. Graphs can be input or output graphs. {`deconstruct {?s "predicate_1"@[] ?o1} - at ?a + in ?a from ?b where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2. @@ -373,7 +373,7 @@ func TestAcceptGraphOpsByParseAndSemantic(t *testing.T) { // Deconstruct data at multiple output graphs from multple input graphs. {`deconstruct {?s "predicate_1"@[] ?o1} - at ?a, ?b + in ?a, ?b from ?c, ?d where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2. @@ -540,7 +540,7 @@ func TestSemanticStatementConstructDeconstructClausesLengthCorrectness(t *testin }, { query: `deconstruct {?s "predicate_1"@[] ?o1} - at ?a + in ?a from ?b where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2. @@ -550,7 +550,7 @@ func TestSemanticStatementConstructDeconstructClausesLengthCorrectness(t *testin { query: `deconstruct {?s "predicate_1"@[] ?o1. ?s "predicate_3"@[] ?o3} - at ?a + in ?a from ?b where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2. @@ -651,7 +651,7 @@ func TestSemanticStatementPredicateObjectPairsLengthCorrectness(t *testing.T) { { query: `deconstruct {?s "predicate_1"@[] ?o1. ?s1 "predicate_1"@[] ?o1} - at ?a + in ?a from ?b where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2. diff --git a/bql/lexer/lexer.go b/bql/lexer/lexer.go index ca73ef43..df13f47b 100644 --- a/bql/lexer/lexer.go +++ b/bql/lexer/lexer.go @@ -65,6 +65,8 @@ const ( ItemID // ItemAt represents at keyword in BQL. ItemAt + // ItemIn represents in keyword in BQL. + ItemIn // ItemBefore represents the before keyword in BQL. ItemBefore // ItemAfter represents the after keyword in BQL. @@ -231,6 +233,8 @@ func (tt TokenType) String() string { return "TYPE" case ItemAt: return "AT" + case ItemIn: + return "IN" case ItemDistinct: return "DISTINCT" default: @@ -293,6 +297,7 @@ const ( id = "id" typeKeyword = "type" atKeyword = "at" + inKeyword = "in" anchor = "\"@[" literalType = "\"^^type:" literalBool = "bool" @@ -588,6 +593,10 @@ func lexKeyword(l *lexer) stateFn { consumeKeyword(l, ItemAt) return lexSpace } + if strings.EqualFold(input, inKeyword) { + consumeKeyword(l, ItemIn) + return lexSpace + } for { r := l.next() if unicode.IsSpace(r) || r == eof { diff --git a/bql/planner/planner_test.go b/bql/planner/planner_test.go index c997730d..d210b6c5 100644 --- a/bql/planner/planner_test.go +++ b/bql/planner/planner_test.go @@ -753,7 +753,7 @@ func TestPlannerDeconstructRemovesCorrectTriples(t *testing.T) { }{ { s: `deconstruct {?p1 "met"@[] ?p2} - at ?dest + in ?dest from ?src where {?p1 "lives_in"@[] /city. ?p2 "lives_in"@[] /city};`, @@ -765,7 +765,7 @@ func TestPlannerDeconstructRemovesCorrectTriples(t *testing.T) { { s: `deconstruct {?p1 "met"@[] ?p2. ?p2 "met"@[] ?p1} - at ?dest + in ?dest from ?src where {?p1 "lives_in"@[] /city. ?p2 "lives_in"@[] /city};`, @@ -822,7 +822,7 @@ func TestPlannerDeconstructRemovesCorrectTriples(t *testing.T) { } dt[trp.String()] = true } else { - t.Errorf("unexpected triple: %v added to graph", t) + t.Errorf("unexpected triple: %v added to graph", trp) } } if i != len(entry.trps) { From cff771d009ed8a6397fe242e109bc04680f757d7 Mon Sep 17 00:00:00 2001 From: Aravind Rao Date: Wed, 19 Jul 2017 18:08:32 -0700 Subject: [PATCH 5/6] Separated grammar test fields into multiple lines. --- bql/grammar/grammar_test.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/bql/grammar/grammar_test.go b/bql/grammar/grammar_test.go index 1dbe118f..a76975f6 100644 --- a/bql/grammar/grammar_test.go +++ b/bql/grammar/grammar_test.go @@ -352,7 +352,11 @@ func TestAcceptGraphOpsByParseAndSemantic(t *testing.T) { from ?b where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2. - ?s "old_predicate_3"@[,] ?o3};`, empty, []string{"?b"}, []string{"?a"}, 0}, + ?s "old_predicate_3"@[,] ?o3};`, + empty, + []string{"?b"}, + []string{"?a"}, + 0}, // Construct data into multiple output graphs from multple input graphs. {`construct {?s "predicate_1"@[] ?o1; @@ -361,7 +365,11 @@ func TestAcceptGraphOpsByParseAndSemantic(t *testing.T) { from ?c, ?d where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2. - ?s "old_predicate_3"@[,] ?o3};`, empty, []string{"?c", "?d"}, []string{"?a", "?b"}, 0}, + ?s "old_predicate_3"@[,] ?o3};`, + empty, + []string{"?c", "?d"}, + []string{"?a", "?b"}, + 0}, // Deconstruct data. Graphs can be input or output graphs. {`deconstruct {?s "predicate_1"@[] ?o1} @@ -369,7 +377,11 @@ func TestAcceptGraphOpsByParseAndSemantic(t *testing.T) { from ?b where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2. - ?s "old_predicate_3"@[,] ?o3};`, empty, []string{"?b"}, []string{"?a"}, 0}, + ?s "old_predicate_3"@[,] ?o3};`, + empty, + []string{"?b"}, + []string{"?a"}, + 0}, // Deconstruct data at multiple output graphs from multple input graphs. {`deconstruct {?s "predicate_1"@[] ?o1} @@ -377,7 +389,11 @@ func TestAcceptGraphOpsByParseAndSemantic(t *testing.T) { from ?c, ?d where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2. - ?s "old_predicate_3"@[,] ?o3};`, empty, []string{"?c", "?d"}, []string{"?a", "?b"}, 0}, + ?s "old_predicate_3"@[,] ?o3};`, + empty, + []string{"?c", "?d"}, + []string{"?a", "?b"}, + 0}, } p, err := NewParser(SemanticBQL()) if err != nil { From 6f1520cffc4a03356d082dbc612a31404dd5da01 Mon Sep 17 00:00:00 2001 From: Aravind Rao Date: Tue, 25 Jul 2017 16:06:33 -0700 Subject: [PATCH 6/6] Removed support for blank nodes for DECONSTRUCT --- bql/grammar/grammar.go | 8 -------- bql/grammar/grammar_test.go | 16 +++++++++++++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/bql/grammar/grammar.go b/bql/grammar/grammar.go index 75817775..2dc9471c 100644 --- a/bql/grammar/grammar.go +++ b/bql/grammar/grammar.go @@ -910,14 +910,6 @@ func BQL() *Grammar { NewSymbol("MORE_DECONSTRUCT_TRIPLES"), }, }, - { - Elements: []Element{ - NewTokenType(lexer.ItemBlankNode), - NewSymbol("CONSTRUCT_PREDICATE"), - NewSymbol("CONSTRUCT_OBJECT"), - NewSymbol("MORE_DECONSTRUCT_TRIPLES"), - }, - }, { Elements: []Element{ NewTokenType(lexer.ItemBinding), diff --git a/bql/grammar/grammar_test.go b/bql/grammar/grammar_test.go index a76975f6..8bef18a2 100644 --- a/bql/grammar/grammar_test.go +++ b/bql/grammar/grammar_test.go @@ -149,9 +149,9 @@ func TestAcceptByParse(t *testing.T) { `deconstruct {?s "new_predicate"@[] ?o} in ?a from ?b where {?s "old_predicate"@[,] ?o} having ?s = ?o;`, `deconstruct {?s "new_predicate"@[] ?o} in ?a from ?b where {?s "old_predicate"@[,] ?o};`, `deconstruct {?s ?p ?o. - _:v "_subject"@[] ?s. - _:v "_predicate"@[] ?p. - _:v "_object"@[] ?o} + ?n "_subject"@[] ?s. + ?n "_predicate"@[] ?p. + ?n "_object"@[] ?o} in ?a, ?b from ?c, ?d where {?n "_subject"@[] ?s. @@ -297,6 +297,16 @@ func TestRejectByParse(t *testing.T) { from ?b where {?s "old_predicate_1"@[,] ?o1. ?s "old_predicate_2"@[,] ?o2};`, + // Deconstruct clause with blank nodes. + `deconstruct {?s ?p ?o. + _:v "_subject"@[] ?s. + _:v "_predicate"@[] ?p. + _:v "_object"@[] ?o} + in ?a, ?b + from ?c, ?d + where {?n "_subject"@[] ?s. + ?n "_predicate"@[] ?p. + ?n "_object"@[] ?o};`, } p, err := NewParser(BQL()) if err != nil {