Skip to content

Commit

Permalink
Merge pull request #154 from rogerlucena/filter-isTemporal
Browse files Browse the repository at this point in the history
FILTER isTemporal
  • Loading branch information
thiagovas authored Oct 26, 2020
2 parents f93f035 + 509dcf1 commit 73451a9
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 0 deletions.
4 changes: 4 additions & 0 deletions bql/planner/filter/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Operation int
const (
Latest Operation = iota + 1
IsImmutable
IsTemporal
)

// Field represents the position of the semantic.GraphClause that will be operated by the filter at storage level.
Expand All @@ -43,6 +44,7 @@ const (
var SupportedOperations = map[string]Operation{
"latest": Latest,
"isimmutable": IsImmutable,
"istemporal": IsTemporal,
}

// StorageOptions represent the storage level specifications for the filtering to be executed.
Expand All @@ -62,6 +64,8 @@ func (op Operation) String() string {
return "latest"
case IsImmutable:
return "isImmutable"
case IsTemporal:
return "isTemporal"
default:
return fmt.Sprintf(`not defined filter operation "%d"`, op)
}
Expand Down
9 changes: 9 additions & 0 deletions bql/planner/planner.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,15 @@ func compatibleBindingsInClauseForFilterOperation(operation filter.Operation) (c
return bindingsByField
}
return compatibleBindingsInClause, nil
case filter.IsTemporal:
compatibleBindingsInClause = func(cls *semantic.GraphClause) (bindingsByField map[filter.Field]map[string]bool) {
bindingsByField = map[filter.Field]map[string]bool{
filter.PredicateField: {cls.PBinding: true, cls.PAlias: true},
filter.ObjectField: {cls.OBinding: true, cls.OAlias: true},
}
return bindingsByField
}
return compatibleBindingsInClause, nil
default:
return nil, fmt.Errorf("filter function %q has no bindings in clause specified for it (planner level)", operation)
}
Expand Down
40 changes: 40 additions & 0 deletions bql/planner/planner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,46 @@ func TestPlannerQuery(t *testing.T) {
nBindings: 3,
nRows: 1,
},
{
q: `SELECT ?p, ?o
FROM ?test
WHERE {
/u<peter> ?p ?o .
FILTER isTemporal(?p)
};`,
nBindings: 2,
nRows: 4,
},
{
q: `SELECT ?s, ?p, ?o
FROM ?test
WHERE {
?s ?p ?o .
FILTER isTemporal(?o)
};`,
nBindings: 3,
nRows: 5,
},
{
q: `SELECT ?p_alias, ?o
FROM ?test
WHERE {
/u<peter> ?p AS ?p_alias ?o .
FILTER isTemporal(?p_alias)
};`,
nBindings: 2,
nRows: 4,
},
{
q: `SELECT ?s, ?p, ?o_alias
FROM ?test
WHERE {
?s ?p ?o AS ?o_alias .
FILTER isTemporal(?o_alias)
};`,
nBindings: 3,
nRows: 5,
},
}

s, ctx := memory.NewStore(), context.Background()
Expand Down
33 changes: 33 additions & 0 deletions storage/memory/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,37 @@ func isImmutableFilter(memoryTriples map[string]*triple.Triple, pQuery *predicat
return trps, nil
}

// isTemporalFilter executes the isTemporal filter operation over memoryTriples following filterOptions.
func isTemporalFilter(memoryTriples map[string]*triple.Triple, pQuery *predicate.Predicate, filterOptions *filter.StorageOptions) (map[string]*triple.Triple, error) {
if filterOptions.Field != filter.PredicateField && filterOptions.Field != filter.ObjectField {
return nil, fmt.Errorf("invalid field %q for %q filter operation, can accept only %q or %q", filterOptions.Field, filter.IsTemporal, filter.PredicateField, filter.ObjectField)
}

trps := make(map[string]*triple.Triple)
for _, t := range memoryTriples {
if pQuery != nil && pQuery.String() != t.Predicate().String() {
continue
}

var p *predicate.Predicate
if filterOptions.Field == filter.PredicateField {
p = t.Predicate()
} else if pObj, err := t.Object().Predicate(); filterOptions.Field == filter.ObjectField && err == nil {
p = pObj
} else {
continue
}

if p.Type() != predicate.Temporal {
continue
}

trps[t.UUID().String()] = t
}

return trps, nil
}

// latestFilter executes the latest filter operation over memoryTriples following filterOptions.
func latestFilter(memoryTriples map[string]*triple.Triple, pQuery *predicate.Predicate, filterOptions *filter.StorageOptions) (map[string]*triple.Triple, error) {
if filterOptions.Field != filter.PredicateField && filterOptions.Field != filter.ObjectField {
Expand Down Expand Up @@ -360,6 +391,8 @@ func executeFilter(memoryTriples map[string]*triple.Triple, pQuery *predicate.Pr
return latestFilter(memoryTriples, pQuery, filterOptions)
case filter.IsImmutable:
return isImmutableFilter(memoryTriples, pQuery, filterOptions)
case filter.IsTemporal:
return isTemporalFilter(memoryTriples, pQuery, filterOptions)
default:
return nil, fmt.Errorf("filter operation %q not supported in the driver", filterOptions.Operation)
}
Expand Down
140 changes: 140 additions & 0 deletions storage/memory/memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,20 @@ func TestObjectsFilter(t *testing.T) {
p: testutil.MustBuildPredicate(t, `"_predicate"@[]`),
want: map[string]int{`"height_cm"@[]`: 1},
},
{
id: "FILTER isTemporal predicate",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.PredicateField}},
s: testutil.MustBuildNodeFromStrings(t, "/u", "john"),
p: testutil.MustBuildPredicate(t, `"parent_of"@[]`),
want: map[string]int{},
},
{
id: "FILTER isTemporal object",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.ObjectField}},
s: testutil.MustBuildNodeFromStrings(t, "/_", "bn"),
p: testutil.MustBuildPredicate(t, `"_predicate"@[]`),
want: map[string]int{`"meet"@[2020-04-10T04:21:00Z]`: 1, `"meet"@[2021-04-10T04:21:00Z]`: 1},
},
}

for _, entry := range testTable {
Expand Down Expand Up @@ -498,6 +512,20 @@ func TestSubjectsFilter(t *testing.T) {
o: triple.NewPredicateObject(testutil.MustBuildPredicate(t, `"height_cm"@[]`)),
want: map[string]int{"/_<bn>": 1},
},
{
id: "FILTER isTemporal predicate",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.PredicateField}},
p: testutil.MustBuildPredicate(t, `"parent_of"@[]`),
o: triple.NewNodeObject(testutil.MustBuildNodeFromStrings(t, "/u", "paul")),
want: map[string]int{},
},
{
id: "FILTER isTemporal object",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.ObjectField}},
p: testutil.MustBuildPredicate(t, `"_predicate"@[]`),
o: triple.NewPredicateObject(testutil.MustBuildPredicate(t, `"height_cm"@[]`)),
want: map[string]int{},
},
}

for _, entry := range testTable {
Expand Down Expand Up @@ -621,6 +649,20 @@ func TestPredicatesForSubjectAndObjectFilter(t *testing.T) {
o: triple.NewPredicateObject(testutil.MustBuildPredicate(t, `"meet"@[2020-04-10T04:21:00Z]`)),
want: map[string]int{},
},
{
id: "FILTER isTemporal predicate",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.PredicateField}},
s: testutil.MustBuildNodeFromStrings(t, "/u", "john"),
o: triple.NewNodeObject(testutil.MustBuildNodeFromStrings(t, "/u", "bob")),
want: map[string]int{`"meet"@[2014-04-10T04:21:00Z]`: 1},
},
{
id: "FILTER isTemporal object",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.ObjectField}},
s: testutil.MustBuildNodeFromStrings(t, "/_", "bn"),
o: triple.NewPredicateObject(testutil.MustBuildPredicate(t, `"meet"@[2020-04-10T04:21:00Z]`)),
want: map[string]int{`"_predicate"@[]`: 1},
},
}

for _, entry := range testTable {
Expand Down Expand Up @@ -739,6 +781,18 @@ func TestPredicatesForSubjectFilter(t *testing.T) {
s: testutil.MustBuildNodeFromStrings(t, "/_", "bn"),
want: map[string]int{`"_predicate"@[]`: 1},
},
{
id: "FILTER isTemporal predicate",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.PredicateField}},
s: testutil.MustBuildNodeFromStrings(t, "/u", "john"),
want: map[string]int{`"meet"@[2012-04-10T04:21:00Z]`: 1, `"meet"@[2013-04-10T04:21:00Z]`: 1, `"meet"@[2014-04-10T04:21:00Z]`: 2},
},
{
id: "FILTER isTemporal object",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.ObjectField}},
s: testutil.MustBuildNodeFromStrings(t, "/_", "bn"),
want: map[string]int{`"_predicate"@[]`: 2},
},
}

for _, entry := range testTable {
Expand Down Expand Up @@ -856,6 +910,18 @@ func TestPredicatesForObjectFilter(t *testing.T) {
o: triple.NewPredicateObject(testutil.MustBuildPredicate(t, `"height_cm"@[]`)),
want: map[string]int{`"_predicate"@[]`: 1},
},
{
id: "FILTER isTemporal predicate",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.PredicateField}},
o: triple.NewNodeObject(testutil.MustBuildNodeFromStrings(t, "/u", "bob")),
want: map[string]int{`"meet"@[2014-04-10T04:21:00Z]`: 1},
},
{
id: "FILTER isTemporal object",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.ObjectField}},
o: triple.NewPredicateObject(testutil.MustBuildPredicate(t, `"meet"@[2020-04-10T04:21:00Z]`)),
want: map[string]int{`"_predicate"@[]`: 1},
},
}

for _, entry := range testTable {
Expand Down Expand Up @@ -970,6 +1036,18 @@ func TestTriplesForSubjectFilter(t *testing.T) {
s: testutil.MustBuildNodeFromStrings(t, "/_", "bn"),
want: map[string]int{`/_<bn> "_predicate"@[] "height_cm"@[]`: 1},
},
{
id: "FILTER isTemporal predicate",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.PredicateField}},
s: testutil.MustBuildNodeFromStrings(t, "/u", "john"),
want: map[string]int{`/u<john> "meet"@[2012-04-10T04:21:00Z] /u<mary>`: 1, `/u<john> "meet"@[2013-04-10T04:21:00Z] /u<mary>`: 1, `/u<john> "meet"@[2014-04-10T04:21:00Z] /u<mary>`: 1, `/u<john> "meet"@[2014-04-10T04:21:00Z] /u<bob>`: 1},
},
{
id: "FILTER isTemporal object",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.ObjectField}},
s: testutil.MustBuildNodeFromStrings(t, "/_", "bn"),
want: map[string]int{`/_<bn> "_predicate"@[] "meet"@[2020-04-10T04:21:00Z]`: 1, `/_<bn> "_predicate"@[] "meet"@[2021-04-10T04:21:00Z]`: 1},
},
}

for _, entry := range testTable {
Expand Down Expand Up @@ -1090,6 +1168,18 @@ func TestTriplesForPredicateFilter(t *testing.T) {
p: testutil.MustBuildPredicate(t, `"_predicate"@[]`),
want: map[string]int{`/_<bn> "_predicate"@[] "height_cm"@[]`: 1},
},
{
id: "FILTER isTemporal predicate",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.PredicateField}},
p: testutil.MustBuildPredicate(t, `"meet"@[2014-04-10T04:21:00Z]`),
want: map[string]int{`/u<john> "meet"@[2014-04-10T04:21:00Z] /u<mary>`: 1, `/u<john> "meet"@[2014-04-10T04:21:00Z] /u<bob>`: 1},
},
{
id: "FILTER isTemporal object",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.ObjectField}},
p: testutil.MustBuildPredicate(t, `"_predicate"@[]`),
want: map[string]int{`/_<bn> "_predicate"@[] "meet"@[2020-04-10T04:21:00Z]`: 1, `/_<bn> "_predicate"@[] "meet"@[2021-04-10T04:21:00Z]`: 1},
},
}

for _, entry := range testTable {
Expand Down Expand Up @@ -1204,6 +1294,18 @@ func TestTriplesForObjectFilter(t *testing.T) {
o: triple.NewPredicateObject(testutil.MustBuildPredicate(t, `"height_cm"@[]`)),
want: map[string]int{`/_<bn> "_predicate"@[] "height_cm"@[]`: 1},
},
{
id: "FILTER isTemporal predicate",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.PredicateField}},
o: triple.NewNodeObject(testutil.MustBuildNodeFromStrings(t, "/u", "bob")),
want: map[string]int{`/u<john> "meet"@[2014-04-10T04:21:00Z] /u<bob>`: 1},
},
{
id: "FILTER isTemporal object",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.ObjectField}},
o: triple.NewPredicateObject(testutil.MustBuildPredicate(t, `"height_cm"@[]`)),
want: map[string]int{},
},
}

for _, entry := range testTable {
Expand Down Expand Up @@ -1373,6 +1475,20 @@ func TestTriplesForSubjectAndPredicateFilter(t *testing.T) {
p: testutil.MustBuildPredicate(t, `"_predicate"@[]`),
want: map[string]int{`/_<bn> "_predicate"@[] "height_cm"@[]`: 1},
},
{
id: "FILTER isTemporal predicate",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.PredicateField}},
s: testutil.MustBuildNodeFromStrings(t, "/u", "john"),
p: testutil.MustBuildPredicate(t, `"meet"@[2014-04-10T04:21:00Z]`),
want: map[string]int{`/u<john> "meet"@[2014-04-10T04:21:00Z] /u<mary>`: 1, `/u<john> "meet"@[2014-04-10T04:21:00Z] /u<bob>`: 1},
},
{
id: "FILTER isTemporal object",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.ObjectField}},
s: testutil.MustBuildNodeFromStrings(t, "/_", "bn"),
p: testutil.MustBuildPredicate(t, `"_predicate"@[]`),
want: map[string]int{`/_<bn> "_predicate"@[] "meet"@[2020-04-10T04:21:00Z]`: 1, `/_<bn> "_predicate"@[] "meet"@[2021-04-10T04:21:00Z]`: 1},
},
}

for _, entry := range testTable {
Expand Down Expand Up @@ -1500,6 +1616,20 @@ func TestTriplesForPredicateAndObjectFilter(t *testing.T) {
o: triple.NewPredicateObject(testutil.MustBuildPredicate(t, `"meet"@[2020-04-10T04:21:00Z]`)),
want: map[string]int{},
},
{
id: "FILTER isTemporal predicate",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.PredicateField}},
p: testutil.MustBuildPredicate(t, `"meet"@[2014-04-10T04:21:00Z]`),
o: triple.NewNodeObject(testutil.MustBuildNodeFromStrings(t, "/u", "mary")),
want: map[string]int{`/u<john> "meet"@[2014-04-10T04:21:00Z] /u<mary>`: 1},
},
{
id: "FILTER isTemporal object",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.ObjectField}},
p: testutil.MustBuildPredicate(t, `"_predicate"@[]`),
o: triple.NewPredicateObject(testutil.MustBuildPredicate(t, `"height_cm"@[]`)),
want: map[string]int{},
},
}

for _, entry := range testTable {
Expand Down Expand Up @@ -1627,6 +1757,16 @@ func TestTriplesFilter(t *testing.T) {
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsImmutable, Field: filter.ObjectField}},
want: map[string]int{`/_<bn> "_predicate"@[] "height_cm"@[]`: 1},
},
{
id: "FILTER isTemporal predicate",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.PredicateField}},
want: map[string]int{`/u<john> "meet"@[2012-04-10T04:21:00Z] /u<mary>`: 1, `/u<john> "meet"@[2013-04-10T04:21:00Z] /u<mary>`: 1, `/u<john> "meet"@[2014-04-10T04:21:00Z] /u<mary>`: 1, `/u<john> "meet"@[2014-04-10T04:21:00Z] /u<bob>`: 1},
},
{
id: "FILTER isTemporal object",
lo: &storage.LookupOptions{FilterOptions: &filter.StorageOptions{Operation: filter.IsTemporal, Field: filter.ObjectField}},
want: map[string]int{`/_<bn> "_predicate"@[] "meet"@[2020-04-10T04:21:00Z]`: 1, `/_<bn> "_predicate"@[] "meet"@[2021-04-10T04:21:00Z]`: 1},
},
}

for _, entry := range testTable {
Expand Down

0 comments on commit 73451a9

Please sign in to comment.