Repository: cayleygraph/cayley
Pull request #940 (2020-07-03 20:54:04)

LinkedQL: New Where Step
The new where step takes a single step and by that replaces the Follow step and the previous Where step.

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/cayleygraph/cayley/940)
<!-- Reviewable:end -->



Patch:
diff --git a/query/linkedql/steps/follow.go b/query/linkedql/steps/follow.go
deleted file mode 100644
index 78dce6b..0000000
--- a/query/linkedql/steps/follow.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package steps
-
-import (
-	"github.com/cayleygraph/cayley/graph"
-	"github.com/cayleygraph/cayley/query/linkedql"
-	"github.com/cayleygraph/cayley/query/path"
-	"github.com/cayleygraph/quad/voc"
-)
-
-func init() {
-	linkedql.Register(&Follow{})
-}
-
-var _ linkedql.PathStep = (*Follow)(nil)
-
-// Follow corresponds to .follow().
-type Follow struct {
-	From     linkedql.PathStep `json:"from"`
-	Followed linkedql.PathStep `json:"followed"`
-}
-
-// Description implements Step.
-func (s *Follow) Description() string {
-	return "is the way to use a path prepared with Morphism. Applies the path chain on the morphism object to the current path. Starts as if at the g.M() and follows through the morphism path."
-}
-
-// BuildPath implements linkedql.PathStep.
-func (s *Follow) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {
-	fromPath, err := s.From.BuildPath(qs, ns)
-	if err != nil {
-		return nil, err
-	}
-	p, err := s.Followed.BuildPath(qs, ns)
-	if err != nil {
-		return nil, err
-	}
-	return fromPath.Follow(p), nil
-}
diff --git a/query/linkedql/steps/jsonld_util.go b/query/linkedql/steps/jsonld_util.go
index 7755a8f..0e38176 100644
--- a/query/linkedql/steps/jsonld_util.go
+++ b/query/linkedql/steps/jsonld_util.go
@@ -64,7 +64,7 @@ func isomorphic(source interface{}, target interface{}) error {
 	case ldMap:
 		t, ok := target.(ldMap)
 		if !ok {
-			return fmt.Errorf("Expected %#v to be a map but instead received %T", target, target)
+			return fmt.Errorf("Expected %#v to be a map or a slice with a single map but instead received %T", target, target)
 		}
 		for k, v := range s {
 			tV, _ := t[k]
diff --git a/query/linkedql/steps/where.go b/query/linkedql/steps/where.go
index a30aa2f..2b7fc13 100644
--- a/query/linkedql/steps/where.go
+++ b/query/linkedql/steps/where.go
@@ -15,13 +15,13 @@ var _ linkedql.PathStep = (*Where)(nil)
 
 // Where corresponds to .where().
 type Where struct {
-	From  linkedql.PathStep   `json:"from"`
-	Steps []linkedql.PathStep `json:"steps"`
+	From      linkedql.PathStep `json:"from"`
+	Condition linkedql.PathStep `json:"condition"`
 }
 
 // Description implements Step.
 func (s *Where) Description() string {
-	return "applies each provided step in steps in isolation on from"
+	return "filters results that fulfill a specified condition"
 }
 
 // BuildPath implements linkedql.PathStep.
@@ -30,13 +30,9 @@ func (s *Where) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, e
 	if err != nil {
 		return nil, err
 	}
-	p := fromPath
-	for _, step := range s.Steps {
-		stepPath, err := step.BuildPath(qs, ns)
-		if err != nil {
-			return nil, err
-		}
-		p = p.And(stepPath.Reverse())
+	stepPath, err := s.Condition.BuildPath(qs, ns)
+	if err != nil {
+		return nil, err
 	}
-	return p, nil
+	return fromPath.And(stepPath.Reverse()), nil
 }


Test patch:
diff --git a/query/linkedql/steps/test-cases/Where.json b/query/linkedql/steps/test-cases/Where.json
index eaaf496..61bc3e3 100644
--- a/query/linkedql/steps/test-cases/Where.json
+++ b/query/linkedql/steps/test-cases/Where.json
@@ -20,42 +20,23 @@
     },
     "@type": "linkedql:Select",
     "linkedql:from": {
-      "@type": "linkedql:Where",
-      "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-      "linkedql:steps": [
-        {
-          "@type": "linkedql:As",
-          "linkedql:from": {
-            "@type": "linkedql:Visit",
-            "linkedql:from": {
-              "@type": "linkedql:Visit",
-              "linkedql:from": {
-                "@type": "linkedql:Placeholder"
-              },
-              "linkedql:properties": "http://example.com/likes"
-            },
-            "linkedql:properties": "http://example.com/name"
-          },
-          "linkedql:name": "http://example.com/likesName"
-        },
-        {
-          "@type": "linkedql:As",
-          "linkedql:from": {
-            "@type": "linkedql:Visit",
-            "linkedql:from": {
-              "@type": "linkedql:Placeholder"
-            },
-            "linkedql:properties": "http://example.com/name"
-          },
-          "linkedql:name": "http://example.com/name"
+      "@type": "linkedql:Properties",
+      "linkedql:names": ["http://example.com/name"],
+      "linkedql:from": {
+        "@type": "linkedql:Where",
+        "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
+        "linkedql:condition": {
+          "@type": "linkedql:Has",
+          "linkedql:property": "http://example.com/likes",
+          "linkedql:values": [{ "@id": "http://example.com/bob" }],
+          "linkedql:from": { "@type": "linkedql:Placeholder" }
         }
-      ]
+      }
     },
     "linkedql:tags": []
   },
   "results": [
     {
-      "http://example.com/likesName": "Bob",
       "http://example.com/name": "Alice"
     }
   ]
diff --git a/query/linkedql/steps/test-cases/where.json b/query/linkedql/steps/test-cases/where.json
index eaaf496..61bc3e3 100644
--- a/query/linkedql/steps/test-cases/where.json
+++ b/query/linkedql/steps/test-cases/where.json
@@ -20,42 +20,23 @@
     },
     "@type": "linkedql:Select",
     "linkedql:from": {
-      "@type": "linkedql:Where",
-      "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-      "linkedql:steps": [
-        {
-          "@type": "linkedql:As",
-          "linkedql:from": {
-            "@type": "linkedql:Visit",
-            "linkedql:from": {
-              "@type": "linkedql:Visit",
-              "linkedql:from": {
-                "@type": "linkedql:Placeholder"
-              },
-              "linkedql:properties": "http://example.com/likes"
-            },
-            "linkedql:properties": "http://example.com/name"
-          },
-          "linkedql:name": "http://example.com/likesName"
-        },
-        {
-          "@type": "linkedql:As",
-          "linkedql:from": {
-            "@type": "linkedql:Visit",
-            "linkedql:from": {
-              "@type": "linkedql:Placeholder"
-            },
-            "linkedql:properties": "http://example.com/name"
-          },
-          "linkedql:name": "http://example.com/name"
+      "@type": "linkedql:Properties",
+      "linkedql:names": ["http://example.com/name"],
+      "linkedql:from": {
+        "@type": "linkedql:Where",
+        "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
+        "linkedql:condition": {
+          "@type": "linkedql:Has",
+          "linkedql:property": "http://example.com/likes",
+          "linkedql:values": [{ "@id": "http://example.com/bob" }],
+          "linkedql:from": { "@type": "linkedql:Placeholder" }
         }
-      ]
+      }
     },
     "linkedql:tags": []
   },
   "results": [
     {
-      "http://example.com/likesName": "Bob",
       "http://example.com/name": "Alice"
     }
   ]