Repository: cayleygraph/cayley
Pull request #941 (2020-07-05 06:22:24)

Correct Query JSON-LD


<!-- 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/941)
<!-- Reviewable:end -->



Patch:
diff --git a/query/linkedql/registry.go b/query/linkedql/registry.go
index eca706f..b8c2ab0 100644
--- a/query/linkedql/registry.go
+++ b/query/linkedql/registry.go
@@ -7,6 +7,7 @@ import (
 	"strings"
 
 	"github.com/cayleygraph/quad"
+	"github.com/piprate/json-gold/ld"
 )
 
 var (
@@ -20,7 +21,7 @@ func TypeByName(name string) (reflect.Type, bool) {
 	return t, ok
 }
 
-// Registered types returns type names of all registered types.
+// RegisteredTypes returns type names of all registered types.
 func RegisteredTypes() []string {
 	out := make([]string, 0, len(typeByName))
 	for k := range typeByName {
@@ -43,7 +44,7 @@ func Register(typ RegistryItem) {
 	if tp.Kind() != reflect.Struct {
 		panic("only structs are allowed")
 	}
-	name := Prefix + tp.Name()
+	name := Namespace + tp.Name()
 	if _, ok := typeByName[name]; ok {
 		panic("this name was already registered")
 	}
@@ -62,6 +63,12 @@ var (
 // Unmarshal attempts to unmarshal an Item or returns error.
 func Unmarshal(data []byte) (RegistryItem, error) {
 	// TODO: make it a part of quad/jsonld package.
+	data, err := normalizeQuery(data)
+
+	if err != nil {
+		return nil, err
+	}
+
 	var m map[string]json.RawMessage
 	if err := json.Unmarshal(data, &m); err != nil {
 		return nil, err
@@ -83,7 +90,7 @@ func Unmarshal(data []byte) (RegistryItem, error) {
 		if tag == "-" {
 			continue
 		} else if tag != "" {
-			name = Prefix + tag
+			name = Namespace + tag
 		}
 		v, ok := m[name]
 		if !ok {
@@ -112,13 +119,17 @@ func Unmarshal(data []byte) (RegistryItem, error) {
 			fv.Set(reflect.ValueOf(value))
 			continue
 		case quadSliceValue:
-			var a []interface{}
+			var a interface{}
 			err := json.Unmarshal(v, &a)
 			if err != nil {
 				return nil, err
 			}
+			arr, ok := a.([]interface{})
+			if !ok {
+				arr = []interface{}{a}
+			}
 			var values []quad.Value
-			for _, item := range a {
+			for _, item := range arr {
 				value, err := parseValue(item)
 				if err != nil {
 					return nil, err
@@ -144,13 +155,17 @@ func Unmarshal(data []byte) (RegistryItem, error) {
 			fv.Set(reflect.ValueOf(val))
 			continue
 		case quadSliceIRI:
-			var a []interface{}
+			var a interface{}
 			err := json.Unmarshal(v, &a)
 			if err != nil {
 				return nil, err
 			}
+			arr, ok := a.([]interface{})
+			if !ok {
+				arr = []interface{}{a}
+			}
 			var values []quad.IRI
-			for _, item := range a {
+			for _, item := range arr {
 				s, ok := item.(string)
 				if !ok {
 					return nil, fmt.Errorf("Expected a string but received %v instead", item)
@@ -180,8 +195,14 @@ func Unmarshal(data []byte) (RegistryItem, error) {
 				}
 			} else {
 				var arr []json.RawMessage
-				if err := json.Unmarshal(v, &arr); err != nil {
-					return nil, err
+				err := json.Unmarshal(v, &arr)
+				if err != nil {
+					var i json.RawMessage
+					iErr := json.Unmarshal(v, &i)
+					if iErr != nil {
+						return nil, err
+					}
+					arr = []json.RawMessage{i}
 				}
 				if arr != nil {
 					va := reflect.MakeSlice(f.Type, len(arr), len(arr))
@@ -205,6 +226,20 @@ func Unmarshal(data []byte) (RegistryItem, error) {
 	return item.Addr().Interface().(RegistryItem), nil
 }
 
+func normalizeQuery(data []byte) ([]byte, error) {
+	var query interface{}
+	json.Unmarshal(data, &query)
+	processor := ld.NewJsonLdProcessor()
+	opts := ld.NewJsonLdOptions("")
+	compact, err := processor.Compact(query, nil, opts)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return json.Marshal(compact)
+}
+
 func parseBNode(s string) (quad.BNode, error) {
 	if !strings.HasPrefix(s, "_:") {
 		return "", fmt.Errorf("blank node ID must start with \"_:\"")
diff --git a/query/linkedql/steps/context.go b/query/linkedql/steps/context.go
deleted file mode 100644
index 5f26c75..0000000
--- a/query/linkedql/steps/context.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(&Context{})
-}
-
-var _ linkedql.PathStep = (*Context)(nil)
-
-// Context corresponds to context().
-type Context struct {
-	From  linkedql.PathStep `json:"from"`
-	Rules map[string]string `json:"rules"`
-}
-
-// Description implements Step.
-func (s *Context) Description() string {
-	return "A a set of rules for interpreting identifiers used in the query"
-}
-
-// BuildPath implements linkedql.PathStep.
-func (s *Context) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {
-	for name, iri := range s.Rules {
-		namespace := voc.Namespace{Prefix: name, Full: iri}
-		ns.Register(namespace)
-	}
-	fromPath, err := s.From.BuildPath(qs, ns)
-	if err != nil {
-		return nil, err
-	}
-	return fromPath, nil
-}


Test patch:
diff --git a/query/linkedql/registry_test.go b/query/linkedql/registry_test.go
index eec949a..51792a1 100644
--- a/query/linkedql/registry_test.go
+++ b/query/linkedql/registry_test.go
@@ -23,30 +23,33 @@ var unmarshalCases = []struct {
 	{
 		name: "simple",
 		data: `{
-	"@type": "linkedql:TestStep",
-	"linkedql:limit": 10
+	"@context": { "@vocab": "http://cayley.io/linkedql#" },
+	"@type": "TestStep",
+	"limit": 10
 }`,
 		exp: &TestStep{Limit: 10},
 	},
 	{
 		name: "simple",
 		data: `{
-	"@type": "linkedql:TestStep",
-	"linkedql:tags": ["a", "b"]
+	"@context": { "@vocab": "http://cayley.io/linkedql#" },
+	"@type": "TestStep",
+	"tags": ["a", "b"]
 }`,
 		exp: &TestStep{Tags: []string{"a", "b"}},
 	},
 	{
 		name: "nested",
 		data: `{
-	"@type": "linkedql:TestStep",
-	"linkedql:limit": 10,
-	"linkedql:from": {
-		"@type": "linkedql:TestStep",
-		"linkedql:limit": 15,
-		"linkedql:from": {
-			"@type": "linkedql:TestStep",
-			"linkedql:limit": 20
+	"@context": { "@vocab": "http://cayley.io/linkedql#" },
+	"@type": "TestStep",
+	"limit": 10,
+	"from": {
+		"@type": "TestStep",
+		"limit": 15,
+		"from": {
+			"@type": "TestStep",
+			"limit": 20
 		}
 	}
 }`,
@@ -63,16 +66,17 @@ var unmarshalCases = []struct {
 	{
 		name: "nested slice",
 		data: `{
-	"@type": "linkedql:TestStep",
-	"linkedql:limit": 10,
-	"linkedql:sub": [
+	"@context": { "@vocab": "http://cayley.io/linkedql#" },
+	"@type": "TestStep",
+	"limit": 10,
+	"sub": [
 		{
-			"@type": "linkedql:TestStep",
-			"linkedql:limit": 15
+			"@type": "TestStep",
+			"limit": 15
 		},
 		{
-			"@type": "linkedql:TestStep",
-			"linkedql:limit": 20
+			"@type": "TestStep",
+			"limit": 20
 		}
 	]
 }`,
diff --git a/query/linkedql/steps/steps_test.go b/query/linkedql/steps/steps_test.go
index ef8904f..d8e3db2 100644
--- a/query/linkedql/steps/steps_test.go
+++ b/query/linkedql/steps/steps_test.go
@@ -64,23 +64,23 @@ func TestLinkedQL(t *testing.T) {
 	for _, info := range files {
 		fileName := info.Name()
 		filePath := filepath.Join(direcotry, fileName)
-		file, err := ioutil.ReadFile(filePath)
-		require.NoError(t, err)
 
-		var c TestCase
-		err = json.Unmarshal(file, &c)
-		require.NoError(t, err)
+		testName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
+		t.Run(testName, func(t *testing.T) {
+			file, err := ioutil.ReadFile(filePath)
+			require.NoError(t, err)
 
-		data, err := readData(c.Data)
-		require.NoError(t, err, fileName)
-		require.NotEmpty(t, data, fileName)
+			var c TestCase
+			err = json.Unmarshal(file, &c)
+			require.NoError(t, err)
 
-		query, err := readQuery(c.Query)
-		require.NoError(t, err, fileName)
-		require.NotNil(t, query, fileName)
+			data, err := readData(c.Data)
+			require.NoError(t, err, fileName)
+			require.NotEmpty(t, data, fileName)
 
-		testName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
-		t.Run(testName, func(t *testing.T) {
+			query, err := readQuery(c.Query)
+			require.NoError(t, err, fileName)
+			require.NotNil(t, query, fileName)
 			store := memstore.New(data...)
 			voc := voc.Namespaces{}
 			ctx := context.TODO()
diff --git a/query/linkedql/steps/test-cases/Back.json b/query/linkedql/steps/test-cases/Back.json
index a2682bb..1a187f4 100644
--- a/query/linkedql/steps/test-cases/Back.json
+++ b/query/linkedql/steps/test-cases/Back.json
@@ -11,20 +11,20 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Back",
-    "linkedql:from": {
-      "@type": "linkedql:Visit",
-      "linkedql:from": {
-        "@type": "linkedql:Match",
-        "linkedql:pattern": {
+    "@type": "Back",
+    "from": {
+      "@type": "Visit",
+      "from": {
+        "@type": "Match",
+        "pattern": {
           "@id": "http://example.com/alice"
         }
       },
-      "linkedql:properties": ["http://example.com/likes"]
+      "properties": ["http://example.com/likes"]
     },
-    "linkedql:name": ""
+    "name": ""
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/Both.json b/query/linkedql/steps/test-cases/Both.json
index 0710e22..645b762 100644
--- a/query/linkedql/steps/test-cases/Both.json
+++ b/query/linkedql/steps/test-cases/Both.json
@@ -19,14 +19,14 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Both",
-    "linkedql:from": {
-      "@type": "linkedql:Match",
-      "linkedql:pattern": { "@id": "http://example.com/bob" }
+    "@type": "Both",
+    "from": {
+      "@type": "Match",
+      "pattern": { "@id": "http://example.com/bob" }
     },
-    "linkedql:properties": "http://example.com/likes"
+    "properties": "http://example.com/likes"
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/Context.json b/query/linkedql/steps/test-cases/Context.json
deleted file mode 100644
index 14a45ca..0000000
--- a/query/linkedql/steps/test-cases/Context.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  "data": [
-    {
-      "@id": "http://example.com/alice",
-      "http://example.com/likes": [
-        {
-          "@id": "http://example.com/bob"
-        }
-      ]
-    },
-    {
-      "@id": "http://example.com/bob",
-      "http://example.com/likes": [
-        {
-          "@id": "http://example.com/alice"
-        }
-      ]
-    }
-  ],
-  "query": {
-    "@context": {
-      "linkedql": "http://cayley.io/linkedql"
-    },
-    "@type": "linkedql:Context",
-    "linkedql:from": {
-      "@type": "linkedql:Has",
-      "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-      "linkedql:property": "likes",
-      "linkedql:values": [{ "@id": "http://example.com/bob" }]
-    },
-    "linkedql:rules": {
-      "bob": "http://example.com/bob",
-      "likes": "http://example.com/likes"
-    }
-  },
-  "results": [
-    {
-      "@id": "http://example.com/alice"
-    }
-  ]
-}
diff --git a/query/linkedql/steps/test-cases/Count.json b/query/linkedql/steps/test-cases/Count.json
index 145a509..db59258 100644
--- a/query/linkedql/steps/test-cases/Count.json
+++ b/query/linkedql/steps/test-cases/Count.json
@@ -11,10 +11,10 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Count",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} }
+    "@type": "Count",
+    "from": { "@type": "Match", "pattern": {} }
   },
   "results": [4]
 }
diff --git a/query/linkedql/steps/test-cases/Difference.json b/query/linkedql/steps/test-cases/Difference.json
index f142ba1..1a73d40 100644
--- a/query/linkedql/steps/test-cases/Difference.json
+++ b/query/linkedql/steps/test-cases/Difference.json
@@ -11,20 +11,20 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Difference",
-    "linkedql:from": {
-      "@type": "linkedql:Vertex",
-      "linkedql:values": [
+    "@type": "Difference",
+    "from": {
+      "@type": "Vertex",
+      "values": [
         { "@id": "http://example.com/alice" },
         { "@id": "http://example.com/likes" }
       ]
     },
-    "linkedql:steps": [
+    "steps": [
       {
-        "@type": "linkedql:Match",
-        "linkedql:pattern": {
+        "@type": "Match",
+        "pattern": {
           "@id": "http://example.com/likes"
         }
       }
diff --git a/query/linkedql/steps/test-cases/Documents.json b/query/linkedql/steps/test-cases/Documents.json
index dd45433..155cc45 100644
--- a/query/linkedql/steps/test-cases/Documents.json
+++ b/query/linkedql/steps/test-cases/Documents.json
@@ -21,13 +21,13 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Documents",
-    "linkedql:from": {
-      "@type": "linkedql:Properties",
-      "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-      "linkedql:names": ["http://example.com/name", "http://example.com/likes"]
+    "@type": "Documents",
+    "from": {
+      "@type": "Properties",
+      "from": { "@type": "Match", "pattern": {} },
+      "names": ["http://example.com/name", "http://example.com/likes"]
     }
   },
   "results": {
diff --git a/query/linkedql/steps/test-cases/Has.json b/query/linkedql/steps/test-cases/Has.json
index 32211eb..83c7c82 100644
--- a/query/linkedql/steps/test-cases/Has.json
+++ b/query/linkedql/steps/test-cases/Has.json
@@ -11,12 +11,12 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Has",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:property": "http://example.com/likes",
-    "linkedql:values": [
+    "@type": "Has",
+    "from": { "@type": "Match", "pattern": {} },
+    "property": "http://example.com/likes",
+    "values": [
       {
         "@id": "http://example.com/bob"
       }
diff --git a/query/linkedql/steps/test-cases/Intersect.json b/query/linkedql/steps/test-cases/Intersect.json
index 67d64b0..707db8d 100644
--- a/query/linkedql/steps/test-cases/Intersect.json
+++ b/query/linkedql/steps/test-cases/Intersect.json
@@ -19,29 +19,29 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Intersect",
-    "linkedql:from": {
-      "@type": "linkedql:Visit",
-      "linkedql:from": {
-        "@type": "linkedql:Match",
-        "linkedql:pattern": {
+    "@type": "Intersect",
+    "from": {
+      "@type": "Visit",
+      "from": {
+        "@type": "Match",
+        "pattern": {
           "@id": "http://example.com/bob"
         }
       },
-      "linkedql:properties": "http://example.com/likes"
+      "properties": "http://example.com/likes"
     },
-    "linkedql:steps": [
+    "steps": [
       {
-        "@type": "linkedql:Visit",
-        "linkedql:from": {
-          "@type": "linkedql:Match",
-          "linkedql:values": {
+        "@type": "Visit",
+        "from": {
+          "@type": "Match",
+          "values": {
             "@id": "http://example.com/bob"
           }
         },
-        "linkedql:properties": "http://example.com/likes"
+        "properties": "http://example.com/likes"
       }
     ]
   },
diff --git a/query/linkedql/steps/test-cases/Like.json b/query/linkedql/steps/test-cases/Like.json
index d0fe238..822b9a0 100644
--- a/query/linkedql/steps/test-cases/Like.json
+++ b/query/linkedql/steps/test-cases/Like.json
@@ -7,11 +7,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Like",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:likePattern": "%ali%"
+    "@type": "Like",
+    "from": { "@type": "Match", "pattern": {} },
+    "likePattern": "%ali%"
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/Limit.json b/query/linkedql/steps/test-cases/Limit.json
index cd607c4..20808a6 100644
--- a/query/linkedql/steps/test-cases/Limit.json
+++ b/query/linkedql/steps/test-cases/Limit.json
@@ -11,11 +11,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Limit",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:limit": 2
+    "@type": "Limit",
+    "from": { "@type": "Match", "pattern": {} },
+    "limit": 2
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/Optional.json b/query/linkedql/steps/test-cases/Optional.json
index 6435eb3..2084eca 100644
--- a/query/linkedql/steps/test-cases/Optional.json
+++ b/query/linkedql/steps/test-cases/Optional.json
@@ -16,25 +16,25 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Select",
-    "linkedql:from": {
-      "@type": "linkedql:Optional",
-      "linkedql:from": {
-        "@type": "linkedql:Properties",
-        "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-        "linkedql:names": ["http://example.com/name"]
+    "@type": "Select",
+    "from": {
+      "@type": "Optional",
+      "from": {
+        "@type": "Properties",
+        "from": { "@type": "Match", "pattern": {} },
+        "names": ["http://example.com/name"]
       },
-      "linkedql:step": {
-        "@type": "linkedql:Properties",
-        "linkedql:from": {
-          "@type": "linkedql:Placeholder"
+      "step": {
+        "@type": "Properties",
+        "from": {
+          "@type": "Placeholder"
         },
-        "linkedql:names": ["http://example.com/likes"]
+        "names": ["http://example.com/likes"]
       }
     },
-    "linkedql:tags": []
+    "tags": []
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/Order.json b/query/linkedql/steps/test-cases/Order.json
index c16d14d..9dd62e5 100644
--- a/query/linkedql/steps/test-cases/Order.json
+++ b/query/linkedql/steps/test-cases/Order.json
@@ -11,10 +11,10 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Order",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} }
+    "@type": "Order",
+    "from": { "@type": "Match", "pattern": {} }
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/Properties.json b/query/linkedql/steps/test-cases/Properties.json
index eaf6fcc..c3f5573 100644
--- a/query/linkedql/steps/test-cases/Properties.json
+++ b/query/linkedql/steps/test-cases/Properties.json
@@ -11,15 +11,15 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Select",
-    "linkedql:from": {
-      "@type": "linkedql:Properties",
-      "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-      "linkedql:names": ["http://example.com/likes"]
+    "@type": "Select",
+    "from": {
+      "@type": "Properties",
+      "from": { "@type": "Match", "pattern": {} },
+      "names": ["http://example.com/likes"]
     },
-    "linkedql:tags": []
+    "tags": []
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/Select.json b/query/linkedql/steps/test-cases/Select.json
index ac9aedf..a2e3057 100644
--- a/query/linkedql/steps/test-cases/Select.json
+++ b/query/linkedql/steps/test-cases/Select.json
@@ -11,19 +11,19 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Select",
-    "linkedql:from": {
-      "@type": "linkedql:Visit",
-      "linkedql:from": {
-        "@type": "linkedql:As",
-        "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-        "linkedql:name": "http://example.com/liker"
+    "@type": "Select",
+    "from": {
+      "@type": "Visit",
+      "from": {
+        "@type": "As",
+        "from": { "@type": "Match", "pattern": {} },
+        "name": "http://example.com/liker"
       },
-      "linkedql:properties": "http://example.com/likes"
+      "properties": "http://example.com/likes"
     },
-    "linkedql:tags": []
+    "tags": []
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/Skip.json b/query/linkedql/steps/test-cases/Skip.json
index e106272..591f030 100644
--- a/query/linkedql/steps/test-cases/Skip.json
+++ b/query/linkedql/steps/test-cases/Skip.json
@@ -11,11 +11,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Skip",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:offset": 2
+    "@type": "Skip",
+    "from": { "@type": "Match", "pattern": {} },
+    "offset": 2
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/Union.json b/query/linkedql/steps/test-cases/Union.json
index f3e4b44..6c295a1 100644
--- a/query/linkedql/steps/test-cases/Union.json
+++ b/query/linkedql/steps/test-cases/Union.json
@@ -11,19 +11,19 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Union",
-    "linkedql:from": {
-      "@type": "linkedql:Match",
-      "linkedql:pattern": {
+    "@type": "Union",
+    "from": {
+      "@type": "Match",
+      "pattern": {
         "@id": "http://example.com/alice"
       }
     },
-    "linkedql:steps": [
+    "steps": [
       {
-        "@type": "linkedql:Match",
-        "linkedql:pattern": {
+        "@type": "Match",
+        "pattern": {
           "@id": "http://example.com/bob"
         }
       }
diff --git a/query/linkedql/steps/test-cases/Unique.json b/query/linkedql/steps/test-cases/Unique.json
index 4582397..6a0c089 100644
--- a/query/linkedql/steps/test-cases/Unique.json
+++ b/query/linkedql/steps/test-cases/Unique.json
@@ -11,12 +11,12 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Unique",
-    "linkedql:from": {
-      "@type": "linkedql:Vertex",
-      "linkedql:values": [
+    "@type": "Unique",
+    "from": {
+      "@type": "Vertex",
+      "values": [
         { "@id": "http://example.com/alice" },
         { "@id": "http://example.com/alice" },
         { "@id": "http://example.com/bob" }
diff --git a/query/linkedql/steps/test-cases/View.json b/query/linkedql/steps/test-cases/View.json
index 4ccf165..b2def87 100644
--- a/query/linkedql/steps/test-cases/View.json
+++ b/query/linkedql/steps/test-cases/View.json
@@ -11,11 +11,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Visit",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:properties": "http://example.com/likes"
+    "@type": "Visit",
+    "from": { "@type": "Match", "pattern": {} },
+    "properties": "http://example.com/likes"
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/Where.json b/query/linkedql/steps/test-cases/Where.json
index 61bc3e3..f693898 100644
--- a/query/linkedql/steps/test-cases/Where.json
+++ b/query/linkedql/steps/test-cases/Where.json
@@ -16,24 +16,24 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Select",
-    "linkedql:from": {
-      "@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" }
+    "@type": "Select",
+    "from": {
+      "@type": "Properties",
+      "names": ["http://example.com/name"],
+      "from": {
+        "@type": "Where",
+        "from": { "@type": "Match", "pattern": {} },
+        "condition": {
+          "@type": "Has",
+          "property": "http://example.com/likes",
+          "values": [{ "@id": "http://example.com/bob" }],
+          "from": { "@type": "Placeholder" }
         }
       }
     },
-    "linkedql:tags": []
+    "tags": []
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/all-vertices.json b/query/linkedql/steps/test-cases/all-vertices.json
index e7730df..0e10172 100644
--- a/query/linkedql/steps/test-cases/all-vertices.json
+++ b/query/linkedql/steps/test-cases/all-vertices.json
@@ -11,10 +11,10 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Vertex",
-    "linkedql:values": []
+    "@type": "Vertex",
+    "values": []
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/back.json b/query/linkedql/steps/test-cases/back.json
index a2682bb..1a187f4 100644
--- a/query/linkedql/steps/test-cases/back.json
+++ b/query/linkedql/steps/test-cases/back.json
@@ -11,20 +11,20 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Back",
-    "linkedql:from": {
-      "@type": "linkedql:Visit",
-      "linkedql:from": {
-        "@type": "linkedql:Match",
-        "linkedql:pattern": {
+    "@type": "Back",
+    "from": {
+      "@type": "Visit",
+      "from": {
+        "@type": "Match",
+        "pattern": {
           "@id": "http://example.com/alice"
         }
       },
-      "linkedql:properties": ["http://example.com/likes"]
+      "properties": ["http://example.com/likes"]
     },
-    "linkedql:name": ""
+    "name": ""
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/both.json b/query/linkedql/steps/test-cases/both.json
index 0710e22..645b762 100644
--- a/query/linkedql/steps/test-cases/both.json
+++ b/query/linkedql/steps/test-cases/both.json
@@ -19,14 +19,14 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Both",
-    "linkedql:from": {
-      "@type": "linkedql:Match",
-      "linkedql:pattern": { "@id": "http://example.com/bob" }
+    "@type": "Both",
+    "from": {
+      "@type": "Match",
+      "pattern": { "@id": "http://example.com/bob" }
     },
-    "linkedql:properties": "http://example.com/likes"
+    "properties": "http://example.com/likes"
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/collect.json b/query/linkedql/steps/test-cases/collect.json
index d000148..2ab18f2 100644
--- a/query/linkedql/steps/test-cases/collect.json
+++ b/query/linkedql/steps/test-cases/collect.json
@@ -9,13 +9,13 @@
   },
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Documents",
-    "linkedql:from": {
-      "@type": "linkedql:Collect",
-      "linkedql:name": "http://example.com/friends",
-      "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} }
+    "@type": "Documents",
+    "from": {
+      "@type": "Collect",
+      "name": "http://example.com/friends",
+      "from": { "@type": "Match", "pattern": {} }
     }
   },
   "results": [
diff --git a/query/linkedql/steps/test-cases/context.json b/query/linkedql/steps/test-cases/context.json
deleted file mode 100644
index 14a45ca..0000000
--- a/query/linkedql/steps/test-cases/context.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  "data": [
-    {
-      "@id": "http://example.com/alice",
-      "http://example.com/likes": [
-        {
-          "@id": "http://example.com/bob"
-        }
-      ]
-    },
-    {
-      "@id": "http://example.com/bob",
-      "http://example.com/likes": [
-        {
-          "@id": "http://example.com/alice"
-        }
-      ]
-    }
-  ],
-  "query": {
-    "@context": {
-      "linkedql": "http://cayley.io/linkedql"
-    },
-    "@type": "linkedql:Context",
-    "linkedql:from": {
-      "@type": "linkedql:Has",
-      "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-      "linkedql:property": "likes",
-      "linkedql:values": [{ "@id": "http://example.com/bob" }]
-    },
-    "linkedql:rules": {
-      "bob": "http://example.com/bob",
-      "likes": "http://example.com/likes"
-    }
-  },
-  "results": [
-    {
-      "@id": "http://example.com/alice"
-    }
-  ]
-}
diff --git a/query/linkedql/steps/test-cases/count.json b/query/linkedql/steps/test-cases/count.json
index 145a509..db59258 100644
--- a/query/linkedql/steps/test-cases/count.json
+++ b/query/linkedql/steps/test-cases/count.json
@@ -11,10 +11,10 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Count",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} }
+    "@type": "Count",
+    "from": { "@type": "Match", "pattern": {} }
   },
   "results": [4]
 }
diff --git a/query/linkedql/steps/test-cases/difference.json b/query/linkedql/steps/test-cases/difference.json
index f142ba1..1a73d40 100644
--- a/query/linkedql/steps/test-cases/difference.json
+++ b/query/linkedql/steps/test-cases/difference.json
@@ -11,20 +11,20 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Difference",
-    "linkedql:from": {
-      "@type": "linkedql:Vertex",
-      "linkedql:values": [
+    "@type": "Difference",
+    "from": {
+      "@type": "Vertex",
+      "values": [
         { "@id": "http://example.com/alice" },
         { "@id": "http://example.com/likes" }
       ]
     },
-    "linkedql:steps": [
+    "steps": [
       {
-        "@type": "linkedql:Match",
-        "linkedql:pattern": {
+        "@type": "Match",
+        "pattern": {
           "@id": "http://example.com/likes"
         }
       }
diff --git a/query/linkedql/steps/test-cases/documents.json b/query/linkedql/steps/test-cases/documents.json
index dd45433..155cc45 100644
--- a/query/linkedql/steps/test-cases/documents.json
+++ b/query/linkedql/steps/test-cases/documents.json
@@ -21,13 +21,13 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Documents",
-    "linkedql:from": {
-      "@type": "linkedql:Properties",
-      "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-      "linkedql:names": ["http://example.com/name", "http://example.com/likes"]
+    "@type": "Documents",
+    "from": {
+      "@type": "Properties",
+      "from": { "@type": "Match", "pattern": {} },
+      "names": ["http://example.com/name", "http://example.com/likes"]
     }
   },
   "results": {
diff --git a/query/linkedql/steps/test-cases/greater-than-equals.json b/query/linkedql/steps/test-cases/greater-than-equals.json
index 6e11ab6..c63cf36 100644
--- a/query/linkedql/steps/test-cases/greater-than-equals.json
+++ b/query/linkedql/steps/test-cases/greater-than-equals.json
@@ -7,11 +7,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:GreaterThanEquals",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:value": 1
+    "@type": "GreaterThanEquals",
+    "from": { "@type": "Match", "pattern": {} },
+    "value": 1
   },
   "results": [1, 2]
 }
diff --git a/query/linkedql/steps/test-cases/greater-than.json b/query/linkedql/steps/test-cases/greater-than.json
index d42ef4e..fd5d9fe 100644
--- a/query/linkedql/steps/test-cases/greater-than.json
+++ b/query/linkedql/steps/test-cases/greater-than.json
@@ -7,11 +7,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:GreaterThan",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:value": 0
+    "@type": "GreaterThan",
+    "from": { "@type": "Match", "pattern": {} },
+    "value": 0
   },
   "results": [1]
 }
diff --git a/query/linkedql/steps/test-cases/has-reverse.json b/query/linkedql/steps/test-cases/has-reverse.json
index 939386c..50de0f7 100644
--- a/query/linkedql/steps/test-cases/has-reverse.json
+++ b/query/linkedql/steps/test-cases/has-reverse.json
@@ -11,12 +11,12 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:HasReverse",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:property": "http://example.com/likes",
-    "linkedql:values": [
+    "@type": "HasReverse",
+    "from": { "@type": "Match", "pattern": {} },
+    "property": "http://example.com/likes",
+    "values": [
       {
         "@id": "http://example.com/alice"
       }
diff --git a/query/linkedql/steps/test-cases/has.json b/query/linkedql/steps/test-cases/has.json
index 32211eb..83c7c82 100644
--- a/query/linkedql/steps/test-cases/has.json
+++ b/query/linkedql/steps/test-cases/has.json
@@ -11,12 +11,12 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Has",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:property": "http://example.com/likes",
-    "linkedql:values": [
+    "@type": "Has",
+    "from": { "@type": "Match", "pattern": {} },
+    "property": "http://example.com/likes",
+    "values": [
       {
         "@id": "http://example.com/bob"
       }
diff --git a/query/linkedql/steps/test-cases/intersect.json b/query/linkedql/steps/test-cases/intersect.json
index 67d64b0..707db8d 100644
--- a/query/linkedql/steps/test-cases/intersect.json
+++ b/query/linkedql/steps/test-cases/intersect.json
@@ -19,29 +19,29 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Intersect",
-    "linkedql:from": {
-      "@type": "linkedql:Visit",
-      "linkedql:from": {
-        "@type": "linkedql:Match",
-        "linkedql:pattern": {
+    "@type": "Intersect",
+    "from": {
+      "@type": "Visit",
+      "from": {
+        "@type": "Match",
+        "pattern": {
           "@id": "http://example.com/bob"
         }
       },
-      "linkedql:properties": "http://example.com/likes"
+      "properties": "http://example.com/likes"
     },
-    "linkedql:steps": [
+    "steps": [
       {
-        "@type": "linkedql:Visit",
-        "linkedql:from": {
-          "@type": "linkedql:Match",
-          "linkedql:values": {
+        "@type": "Visit",
+        "from": {
+          "@type": "Match",
+          "values": {
             "@id": "http://example.com/bob"
           }
         },
-        "linkedql:properties": "http://example.com/likes"
+        "properties": "http://example.com/likes"
       }
     ]
   },
diff --git a/query/linkedql/steps/test-cases/less-than-equals.json b/query/linkedql/steps/test-cases/less-than-equals.json
index 617b6e0..ee94d38 100644
--- a/query/linkedql/steps/test-cases/less-than-equals.json
+++ b/query/linkedql/steps/test-cases/less-than-equals.json
@@ -7,11 +7,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:LessThanEquals",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:value": 0
+    "@type": "LessThanEquals",
+    "from": { "@type": "Match", "pattern": {} },
+    "value": 0
   },
   "results": [-1, 0]
 }
diff --git a/query/linkedql/steps/test-cases/less-than.json b/query/linkedql/steps/test-cases/less-than.json
index b867cdc..2d1def3 100644
--- a/query/linkedql/steps/test-cases/less-than.json
+++ b/query/linkedql/steps/test-cases/less-than.json
@@ -7,11 +7,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:LessThan",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:value": 1
+    "@type": "LessThan",
+    "from": { "@type": "Match", "pattern": {} },
+    "value": 1
   },
   "results": [0]
 }
diff --git a/query/linkedql/steps/test-cases/like.json b/query/linkedql/steps/test-cases/like.json
index d0fe238..822b9a0 100644
--- a/query/linkedql/steps/test-cases/like.json
+++ b/query/linkedql/steps/test-cases/like.json
@@ -7,11 +7,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Like",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:likePattern": "%ali%"
+    "@type": "Like",
+    "from": { "@type": "Match", "pattern": {} },
+    "likePattern": "%ali%"
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/limit.json b/query/linkedql/steps/test-cases/limit.json
index cd607c4..20808a6 100644
--- a/query/linkedql/steps/test-cases/limit.json
+++ b/query/linkedql/steps/test-cases/limit.json
@@ -11,11 +11,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Limit",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:limit": 2
+    "@type": "Limit",
+    "from": { "@type": "Match", "pattern": {} },
+    "limit": 2
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/match-all.json b/query/linkedql/steps/test-cases/match-all.json
index 08033c4..ddde68d 100644
--- a/query/linkedql/steps/test-cases/match-all.json
+++ b/query/linkedql/steps/test-cases/match-all.json
@@ -11,10 +11,10 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Match",
-    "linkedql:pattern": {}
+    "@type": "Match",
+    "pattern": {}
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/match-exact.json b/query/linkedql/steps/test-cases/match-exact.json
index ae01a86..0ccb829 100644
--- a/query/linkedql/steps/test-cases/match-exact.json
+++ b/query/linkedql/steps/test-cases/match-exact.json
@@ -11,10 +11,10 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Match",
-    "linkedql:pattern": {
+    "@type": "Match",
+    "pattern": {
       "@id": "http://example.com/alice"
     }
   },
diff --git a/query/linkedql/steps/test-cases/optional.json b/query/linkedql/steps/test-cases/optional.json
index 6435eb3..2084eca 100644
--- a/query/linkedql/steps/test-cases/optional.json
+++ b/query/linkedql/steps/test-cases/optional.json
@@ -16,25 +16,25 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Select",
-    "linkedql:from": {
-      "@type": "linkedql:Optional",
-      "linkedql:from": {
-        "@type": "linkedql:Properties",
-        "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-        "linkedql:names": ["http://example.com/name"]
+    "@type": "Select",
+    "from": {
+      "@type": "Optional",
+      "from": {
+        "@type": "Properties",
+        "from": { "@type": "Match", "pattern": {} },
+        "names": ["http://example.com/name"]
       },
-      "linkedql:step": {
-        "@type": "linkedql:Properties",
-        "linkedql:from": {
-          "@type": "linkedql:Placeholder"
+      "step": {
+        "@type": "Properties",
+        "from": {
+          "@type": "Placeholder"
         },
-        "linkedql:names": ["http://example.com/likes"]
+        "names": ["http://example.com/likes"]
       }
     },
-    "linkedql:tags": []
+    "tags": []
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/order.json b/query/linkedql/steps/test-cases/order.json
index c16d14d..9dd62e5 100644
--- a/query/linkedql/steps/test-cases/order.json
+++ b/query/linkedql/steps/test-cases/order.json
@@ -11,10 +11,10 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Order",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} }
+    "@type": "Order",
+    "from": { "@type": "Match", "pattern": {} }
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/properties.json b/query/linkedql/steps/test-cases/properties.json
index eaf6fcc..c3f5573 100644
--- a/query/linkedql/steps/test-cases/properties.json
+++ b/query/linkedql/steps/test-cases/properties.json
@@ -11,15 +11,15 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Select",
-    "linkedql:from": {
-      "@type": "linkedql:Properties",
-      "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-      "linkedql:names": ["http://example.com/likes"]
+    "@type": "Select",
+    "from": {
+      "@type": "Properties",
+      "from": { "@type": "Match", "pattern": {} },
+      "names": ["http://example.com/likes"]
     },
-    "linkedql:tags": []
+    "tags": []
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/property-names-as.json b/query/linkedql/steps/test-cases/property-names-as.json
index a668784..421d028 100644
--- a/query/linkedql/steps/test-cases/property-names-as.json
+++ b/query/linkedql/steps/test-cases/property-names-as.json
@@ -11,15 +11,15 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Select",
-    "linkedql:from": {
-      "@type": "linkedql:PropertyNamesAs",
-      "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-      "linkedql:tag": "property"
+    "@type": "Select",
+    "from": {
+      "@type": "PropertyNamesAs",
+      "from": { "@type": "Match", "pattern": {} },
+      "tag": "property"
     },
-    "linkedql:tags": []
+    "tags": []
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/property-names.json b/query/linkedql/steps/test-cases/property-names.json
index 9bb4446..45f7563 100644
--- a/query/linkedql/steps/test-cases/property-names.json
+++ b/query/linkedql/steps/test-cases/property-names.json
@@ -11,10 +11,10 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:PropertyNames",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} }
+    "@type": "PropertyNames",
+    "from": { "@type": "Match", "pattern": {} }
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/reg-exp.json b/query/linkedql/steps/test-cases/reg-exp.json
index 824b9a8..a78a16a 100644
--- a/query/linkedql/steps/test-cases/reg-exp.json
+++ b/query/linkedql/steps/test-cases/reg-exp.json
@@ -7,12 +7,12 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:RegExp",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:includeIRIs": false,
-    "linkedql:expression": "A"
+    "@type": "RegExp",
+    "from": { "@type": "Match", "pattern": {} },
+    "includeIRIs": false,
+    "expression": "A"
   },
   "results": ["Alice"]
 }
diff --git a/query/linkedql/steps/test-cases/reverse-properties.json b/query/linkedql/steps/test-cases/reverse-properties.json
index ae2ae14..78914b6 100644
--- a/query/linkedql/steps/test-cases/reverse-properties.json
+++ b/query/linkedql/steps/test-cases/reverse-properties.json
@@ -11,15 +11,15 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Select",
-    "linkedql:from": {
-      "@type": "linkedql:ReverseProperties",
-      "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-      "linkedql:names": ["http://example.com/likes"]
+    "@type": "Select",
+    "from": {
+      "@type": "ReverseProperties",
+      "from": { "@type": "Match", "pattern": {} },
+      "names": ["http://example.com/likes"]
     },
-    "linkedql:tags": []
+    "tags": []
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/reverse-property-names-as.json b/query/linkedql/steps/test-cases/reverse-property-names-as.json
index e3aed41..cc55ffe 100644
--- a/query/linkedql/steps/test-cases/reverse-property-names-as.json
+++ b/query/linkedql/steps/test-cases/reverse-property-names-as.json
@@ -11,15 +11,15 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Select",
-    "linkedql:from": {
-      "@type": "linkedql:ReversePropertyNamesAs",
-      "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-      "linkedql:tag": "property"
+    "@type": "Select",
+    "from": {
+      "@type": "ReversePropertyNamesAs",
+      "from": { "@type": "Match", "pattern": {} },
+      "tag": "property"
     },
-    "linkedql:tags": []
+    "tags": []
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/select-with-tags.json b/query/linkedql/steps/test-cases/select-with-tags.json
index 20e6ef1..f647476 100644
--- a/query/linkedql/steps/test-cases/select-with-tags.json
+++ b/query/linkedql/steps/test-cases/select-with-tags.json
@@ -11,26 +11,26 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Select",
-    "linkedql:from": {
-      "@type": "linkedql:As",
-      "linkedql:from": {
-        "@type": "linkedql:Visit",
-        "linkedql:from": {
-          "@type": "linkedql:As",
-          "linkedql:from": {
-            "@type": "linkedql:Match",
-            "linkedql:pattern": {}
+    "@type": "Select",
+    "from": {
+      "@type": "As",
+      "from": {
+        "@type": "Visit",
+        "from": {
+          "@type": "As",
+          "from": {
+            "@type": "Match",
+            "pattern": {}
           },
-          "linkedql:name": "http://example.com/liker"
+          "name": "http://example.com/liker"
         },
-        "linkedql:properties": "http://example.com/likes"
+        "properties": "http://example.com/likes"
       },
-      "linkedql:name": "liked"
+      "name": "liked"
     },
-    "linkedql:tags": ["http://example.com/liker"]
+    "tags": ["http://example.com/liker"]
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/select.json b/query/linkedql/steps/test-cases/select.json
index ac9aedf..a2e3057 100644
--- a/query/linkedql/steps/test-cases/select.json
+++ b/query/linkedql/steps/test-cases/select.json
@@ -11,19 +11,19 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Select",
-    "linkedql:from": {
-      "@type": "linkedql:Visit",
-      "linkedql:from": {
-        "@type": "linkedql:As",
-        "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-        "linkedql:name": "http://example.com/liker"
+    "@type": "Select",
+    "from": {
+      "@type": "Visit",
+      "from": {
+        "@type": "As",
+        "from": { "@type": "Match", "pattern": {} },
+        "name": "http://example.com/liker"
       },
-      "linkedql:properties": "http://example.com/likes"
+      "properties": "http://example.com/likes"
     },
-    "linkedql:tags": []
+    "tags": []
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/skip.json b/query/linkedql/steps/test-cases/skip.json
index e106272..591f030 100644
--- a/query/linkedql/steps/test-cases/skip.json
+++ b/query/linkedql/steps/test-cases/skip.json
@@ -11,11 +11,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Skip",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:offset": 2
+    "@type": "Skip",
+    "from": { "@type": "Match", "pattern": {} },
+    "offset": 2
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/union.json b/query/linkedql/steps/test-cases/union.json
index f3e4b44..6c295a1 100644
--- a/query/linkedql/steps/test-cases/union.json
+++ b/query/linkedql/steps/test-cases/union.json
@@ -11,19 +11,19 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Union",
-    "linkedql:from": {
-      "@type": "linkedql:Match",
-      "linkedql:pattern": {
+    "@type": "Union",
+    "from": {
+      "@type": "Match",
+      "pattern": {
         "@id": "http://example.com/alice"
       }
     },
-    "linkedql:steps": [
+    "steps": [
       {
-        "@type": "linkedql:Match",
-        "linkedql:pattern": {
+        "@type": "Match",
+        "pattern": {
           "@id": "http://example.com/bob"
         }
       }
diff --git a/query/linkedql/steps/test-cases/unique.json b/query/linkedql/steps/test-cases/unique.json
index 4582397..6a0c089 100644
--- a/query/linkedql/steps/test-cases/unique.json
+++ b/query/linkedql/steps/test-cases/unique.json
@@ -11,12 +11,12 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Unique",
-    "linkedql:from": {
-      "@type": "linkedql:Vertex",
-      "linkedql:values": [
+    "@type": "Unique",
+    "from": {
+      "@type": "Vertex",
+      "values": [
         { "@id": "http://example.com/alice" },
         { "@id": "http://example.com/alice" },
         { "@id": "http://example.com/bob" }
diff --git a/query/linkedql/steps/test-cases/view-reverse.json b/query/linkedql/steps/test-cases/view-reverse.json
index 9da5b50..44fe8d5 100644
--- a/query/linkedql/steps/test-cases/view-reverse.json
+++ b/query/linkedql/steps/test-cases/view-reverse.json
@@ -11,11 +11,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:VisitReverse",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:properties": "http://example.com/likes"
+    "@type": "VisitReverse",
+    "from": { "@type": "Match", "pattern": {} },
+    "properties": "http://example.com/likes"
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/view.json b/query/linkedql/steps/test-cases/view.json
index 4ccf165..b2def87 100644
--- a/query/linkedql/steps/test-cases/view.json
+++ b/query/linkedql/steps/test-cases/view.json
@@ -11,11 +11,11 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Visit",
-    "linkedql:from": { "@type": "linkedql:Match", "linkedql:pattern": {} },
-    "linkedql:properties": "http://example.com/likes"
+    "@type": "Visit",
+    "from": { "@type": "Match", "pattern": {} },
+    "properties": "http://example.com/likes"
   },
   "results": [
     {
diff --git a/query/linkedql/steps/test-cases/where.json b/query/linkedql/steps/test-cases/where.json
index 61bc3e3..f693898 100644
--- a/query/linkedql/steps/test-cases/where.json
+++ b/query/linkedql/steps/test-cases/where.json
@@ -16,24 +16,24 @@
   ],
   "query": {
     "@context": {
-      "linkedql": "http://cayley.io/linkedql"
+      "@vocab": "http://cayley.io/linkedql#"
     },
-    "@type": "linkedql:Select",
-    "linkedql:from": {
-      "@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" }
+    "@type": "Select",
+    "from": {
+      "@type": "Properties",
+      "names": ["http://example.com/name"],
+      "from": {
+        "@type": "Where",
+        "from": { "@type": "Match", "pattern": {} },
+        "condition": {
+          "@type": "Has",
+          "property": "http://example.com/likes",
+          "values": [{ "@id": "http://example.com/bob" }],
+          "from": { "@type": "Placeholder" }
         }
       }
     },
-    "linkedql:tags": []
+    "tags": []
   },
   "results": [
     {