Repository: cayleygraph/cayley
Pull request #898 (2019-12-16 20:06:00)

LinkedQL: Fix Step Names
 - View -> Visit
 - ViewBoth -> Both

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



Patch:
diff --git a/inference/inference.go b/inference/inference.go
index 4bcd872..6e172cd 100644
--- a/inference/inference.go
+++ b/inference/inference.go
@@ -19,7 +19,7 @@
 //
 // Exported from: https://www.researchgate.net/figure/RDF-RDFS-entailment-rules_tbl1_268419911
 //
-// Implemented here: 1 5 6 8 10 11
+// Implemented here: 1 2 3 5 6 8 10 11
 package inference
 
 import (
@@ -154,7 +154,9 @@ func (p *Property) IsSubPropertyOf(super *Property) bool {
 func (p *Property) isReferenced() bool {
 	return p.explicit || p.references > 0 ||
 		len(p.super) > 0 ||
-		len(p.sub) > 0
+		len(p.sub) > 0 ||
+		p.domain != nil ||
+		p.prange != nil
 }
 
 func (p *Property) deleteIfUnreferenced() {
@@ -264,12 +266,13 @@ func (s *Store) addClassInstance(name quad.Value) {
 	c.references++
 }
 
-func (s *Store) addPropertyInstance(name quad.Value) {
+func (s *Store) addPropertyInstance(name quad.Value) *Property {
 	p := s.GetProperty(name)
 	if p == nil {
 		p = s.getOrCreateImplicitProperty(name)
 	}
 	p.references++
+	return p
 }
 
 // ProcessQuads is used to update the store with multiple quads
@@ -310,7 +313,15 @@ func (s *Store) processQuad(q quad.Quad) {
 	case rdfs.Range:
 		s.setPropertyRange(sub, obj)
 	default:
-		s.addPropertyInstance(pred)
+		p := s.addPropertyInstance(pred)
+		domain := p.Domain()
+		if domain != nil {
+			domain.references++
+		}
+		prange := p.Range()
+		if prange != nil {
+			prange.references++
+		}
 	}
 }
 
@@ -393,13 +404,14 @@ func (s *Store) deleteClassInstance(name quad.Value) {
 	c.deleteIfUnreferenced()
 }
 
-func (s *Store) deletePropertyInstance(name quad.Value) {
+func (s *Store) deletePropertyInstance(name quad.Value) *Property {
 	p := s.GetProperty(name)
 	if p == nil {
-		return
+		return nil
 	}
 	p.references--
 	p.deleteIfUnreferenced()
+	return p
 }
 
 // UnprocessQuads is used to delete multiple quads from the store
@@ -439,6 +451,14 @@ func (s *Store) unprocessQuad(q quad.Quad) {
 	case rdfs.Range:
 		s.unsetPropertyRange(sub, obj)
 	default:
-		s.deletePropertyInstance(pred)
+		p := s.deletePropertyInstance(pred)
+		if p != nil {
+			if domain := p.Domain(); domain != nil {
+				s.deleteClassInstance(domain.Name())
+			}
+			if prange := p.Range(); prange != nil {
+				s.deleteClassInstance(prange.Name())
+			}
+		}
 	}
 }
diff --git a/query/linkedql/steps.go b/query/linkedql/steps.go
index e6b4e2a..45d6b7f 100644
--- a/query/linkedql/steps.go
+++ b/query/linkedql/steps.go
@@ -10,13 +10,13 @@ import (
 func init() {
 	Register(&Vertex{})
 	Register(&Placeholder{})
-	Register(&View{})
+	Register(&Visit{})
 	Register(&Out{})
 	Register(&As{})
 	Register(&Intersect{})
 	Register(&Is{})
 	Register(&Back{})
-	Register(&ViewBoth{})
+	Register(&Both{})
 	Register(&Count{})
 	Register(&Difference{})
 	Register(&Filter{})
@@ -24,7 +24,7 @@ func init() {
 	Register(&FollowReverse{})
 	Register(&Has{})
 	Register(&HasReverse{})
-	Register(&ViewReverse{})
+	Register(&VisitReverse{})
 	Register(&In{})
 	Register(&ReversePropertyNames{})
 	Register(&Labels{})
@@ -246,32 +246,32 @@ func (s *Placeholder) BuildPath(qs graph.QuadStore) (*path.Path, error) {
 	return path.StartMorphism(), nil
 }
 
-var _ IteratorStep = (*View)(nil)
-var _ PathStep = (*View)(nil)
+var _ IteratorStep = (*Visit)(nil)
+var _ PathStep = (*Visit)(nil)
 
-// View corresponds to .view().
-type View struct {
+// Visit corresponds to .view().
+type Visit struct {
 	From       PathStep     `json:"from"`
 	Properties PropertyPath `json:"properties"`
 }
 
 // Type implements Step.
-func (s *View) Type() quad.IRI {
-	return Prefix + "View"
+func (s *Visit) Type() quad.IRI {
+	return Prefix + "Visit"
 }
 
 // Description implements Step.
-func (s *View) Description() string {
+func (s *Visit) Description() string {
 	return "resolves to the values of the given property or properties in via of the current objects. If via is a path it's resolved values will be used as properties."
 }
 
 // BuildIterator implements IteratorStep.
-func (s *View) BuildIterator(qs graph.QuadStore) (query.Iterator, error) {
+func (s *Visit) BuildIterator(qs graph.QuadStore) (query.Iterator, error) {
 	return NewValueIteratorFromPathStep(s, qs)
 }
 
 // BuildPath implements PathStep.
-func (s *View) BuildPath(qs graph.QuadStore) (*path.Path, error) {
+func (s *Visit) BuildPath(qs graph.QuadStore) (*path.Path, error) {
 	fromPath, err := s.From.BuildPath(qs)
 	if err != nil {
 		return nil, err
@@ -288,7 +288,7 @@ var _ PathStep = (*Out)(nil)
 
 // Out is an alias for View.
 type Out struct {
-	View
+	Visit
 }
 
 // Type implements Step.
@@ -441,32 +441,32 @@ func (s *Back) BuildPath(qs graph.QuadStore) (*path.Path, error) {
 	return fromPath.Back(s.Name), nil
 }
 
-var _ IteratorStep = (*ViewBoth)(nil)
-var _ PathStep = (*ViewBoth)(nil)
+var _ IteratorStep = (*Both)(nil)
+var _ PathStep = (*Both)(nil)
 
-// ViewBoth corresponds to .viewBoth().
-type ViewBoth struct {
+// Both corresponds to .both().
+type Both struct {
 	From       PathStep     `json:"from"`
 	Properties PropertyPath `json:"properties"`
 }
 
 // Type implements Step.
-func (s *ViewBoth) Type() quad.IRI {
-	return Prefix + "ViewBoth"
+func (s *Both) Type() quad.IRI {
+	return Prefix + "Both"
 }
 
 // Description implements Step.
-func (s *ViewBoth) Description() string {
+func (s *Both) Description() string {
 	return "is like View but resolves to both the object values and references to the values of the given properties in via. It is the equivalent for the Union of View and ViewReverse of the same property."
 }
 
 // BuildIterator implements IteratorStep.
-func (s *ViewBoth) BuildIterator(qs graph.QuadStore) (query.Iterator, error) {
+func (s *Both) BuildIterator(qs graph.QuadStore) (query.Iterator, error) {
 	return NewValueIteratorFromPathStep(s, qs)
 }
 
 // BuildPath implements PathStep.
-func (s *ViewBoth) BuildPath(qs graph.QuadStore) (*path.Path, error) {
+func (s *Both) BuildPath(qs graph.QuadStore) (*path.Path, error) {
 	fromPath, err := s.From.BuildPath(qs)
 	if err != nil {
 		return nil, err
@@ -478,24 +478,6 @@ func (s *ViewBoth) BuildPath(qs graph.QuadStore) (*path.Path, error) {
 	return fromPath.Both(viaPath), nil
 }
 
-var _ IteratorStep = (*Both)(nil)
-var _ PathStep = (*Both)(nil)
-
-// Both corresponds to .both().
-type Both struct {
-	ViewBoth
-}
-
-// Type implements Step.
-func (s *Both) Type() quad.IRI {
-	return Prefix + "Both"
-}
-
-// Description implements Step.
-func (s *Both) Description() string {
-	return "aliases for ViewBoth"
-}
-
 var _ IteratorStep = (*Count)(nil)
 var _ PathStep = (*Count)(nil)
 
@@ -752,32 +734,32 @@ func (s *HasReverse) BuildPath(qs graph.QuadStore) (*path.Path, error) {
 	return fromPath.HasReverse(viaPath, s.Values...), nil
 }
 
-var _ IteratorStep = (*ViewReverse)(nil)
-var _ PathStep = (*ViewReverse)(nil)
+var _ IteratorStep = (*VisitReverse)(nil)
+var _ PathStep = (*VisitReverse)(nil)
 
-// ViewReverse corresponds to .viewReverse().
-type ViewReverse struct {
+// VisitReverse corresponds to .viewReverse().
+type VisitReverse struct {
 	From       PathStep     `json:"from"`
 	Properties PropertyPath `json:"properties"`
 }
 
 // Type implements Step.
-func (s *ViewReverse) Type() quad.IRI {
-	return Prefix + "ViewReverse"
+func (s *VisitReverse) Type() quad.IRI {
+	return Prefix + "VisitReverse"
 }
 
 // Description implements Step.
-func (s *ViewReverse) Description() string {
+func (s *VisitReverse) Description() string {
 	return "is the inverse of View. Starting with the nodes in `path` on the object, follow the quads with predicates defined by `predicatePath` to their subjects."
 }
 
 // BuildIterator implements IteratorStep.
-func (s *ViewReverse) BuildIterator(qs graph.QuadStore) (query.Iterator, error) {
+func (s *VisitReverse) BuildIterator(qs graph.QuadStore) (query.Iterator, error) {
 	return NewValueIteratorFromPathStep(s, qs)
 }
 
 // BuildPath implements PathStep.
-func (s *ViewReverse) BuildPath(qs graph.QuadStore) (*path.Path, error) {
+func (s *VisitReverse) BuildPath(qs graph.QuadStore) (*path.Path, error) {
 	fromPath, err := s.From.BuildPath(qs)
 	if err != nil {
 		return nil, err
@@ -794,7 +776,7 @@ var _ PathStep = (*In)(nil)
 
 // In is an alias for ViewReverse.
 type In struct {
-	ViewReverse
+	VisitReverse
 }
 
 // Type implements Step.


Test patch:
diff --git a/inference/inference_test.go b/inference/inference_test.go
index e2148f6..6716e16 100644
--- a/inference/inference_test.go
+++ b/inference/inference_test.go
@@ -23,6 +23,7 @@ var (
 	subClassOf       = quad.IRI(rdfs.SubClassOf)
 	subPropertyOf    = quad.IRI(rdfs.SubPropertyOf)
 	alice            = quad.IRI("alice")
+	aliceName        = quad.String("Alice")
 	bob              = quad.IRI("bob")
 	engineer         = quad.IRI("Engineer")
 	information      = quad.IRI("information")
@@ -35,6 +36,7 @@ var (
 var (
 	aliceIsPerson                    = triple(alice, ptype, person)
 	aliceLikesBob                    = triple(alice, likes, bob)
+	likesRangePerson                 = triple(likes, prange, person)
 	engineerClass                    = triple(engineer, ptype, class)
 	engineerSubClass                 = triple(engineer, subClassOf, person)
 	nameDomainPerson                 = triple(name, domain, person)
@@ -44,6 +46,7 @@ var (
 	personalSubPropertyOfInformation = triple(personal, subPropertyOf, information)
 	personClass                      = triple(person, ptype, class)
 	softwareEngineerClass            = triple(softwareEngineer, ptype, class)
+	aliceNameAlice                   = triple(alice, name, aliceName)
 )
 var (
 	engineerAndSoftwareEngineerSubClasses = []quad.Quad{
@@ -186,9 +189,9 @@ func TestPropertyDomain(t *testing.T) {
 
 func TestPropertyRange(t *testing.T) {
 	store := NewStore()
-	q := triple(name, prange, person)
+	q := likesRangePerson
 	store.ProcessQuads(q)
-	createdProperty := store.GetProperty(name)
+	createdProperty := store.GetProperty(likes)
 	createdClass := store.GetClass(person)
 	require.NotNil(t, createdProperty, "Property was not created")
 	require.NotNil(t, createdClass, "Range class was not created")
@@ -533,3 +536,61 @@ func TestPropertyUnreference(t *testing.T) {
 	store.UnprocessQuads(q)
 	require.Nil(t, store.GetProperty(likes), "property was not garbage collected")
 }
+
+func TestDomainClassInstance(t *testing.T) {
+	store := NewStore()
+	quads := []quad.Quad{
+		nameDomainPerson,
+		aliceNameAlice,
+	}
+	store.ProcessQuads(quads...)
+	class := store.GetClass(person)
+	require.NotNil(t, class)
+	require.True(t, class.isReferenced())
+	require.Equal(t, 1, class.references)
+	store.UnprocessQuads(aliceNameAlice)
+	require.True(t, class.isReferenced())
+	require.Equal(t, 0, class.references)
+	store.UnprocessQuads(nameDomainPerson)
+	require.False(t, class.isReferenced())
+	require.Equal(t, 0, class.references)
+}
+
+func TestRangeClassInstance(t *testing.T) {
+	store := NewStore()
+	quads := []quad.Quad{
+		likesRangePerson,
+		aliceLikesBob,
+	}
+	store.ProcessQuads(quads...)
+	class := store.GetClass(person)
+	require.NotNil(t, class)
+	require.Equal(t, 1, class.references)
+	require.True(t, class.isReferenced())
+	store.UnprocessQuads(aliceLikesBob)
+	require.Equal(t, 0, class.references)
+	require.True(t, class.isReferenced())
+	store.UnprocessQuads(likesRangePerson)
+	require.Equal(t, 0, class.references)
+	require.False(t, class.isReferenced())
+}
+
+func TestDeleteNonExistingClass(t *testing.T) {
+	store := NewStore()
+	store.UnprocessQuads(personClass)
+}
+
+func TestDeleteNonExistingProperty(t *testing.T) {
+	store := NewStore()
+	store.UnprocessQuads(personalProperty)
+}
+
+func TestDeleteNonExistingClassInstance(t *testing.T) {
+	store := NewStore()
+	store.UnprocessQuads(aliceIsPerson)
+}
+
+func TestDeleteNonExistingUsedProperty(t *testing.T) {
+	store := NewStore()
+	store.UnprocessQuads(aliceNameAlice)
+}
diff --git a/query/linkedql/steps_test.go b/query/linkedql/steps_test.go
index df298a3..8cecea9 100644
--- a/query/linkedql/steps_test.go
+++ b/query/linkedql/steps_test.go
@@ -42,7 +42,7 @@ var testCases = []struct {
 		data: singleQuadData,
 		query: &Select{
 			From: &As{
-				From: &View{
+				From: &Visit{
 					From: &As{
 						From: &Vertex{},
 						Name: "liker",
@@ -65,7 +65,7 @@ var testCases = []struct {
 		query: &Select{
 			Tags: []string{"liker"},
 			From: &As{
-				From: &View{
+				From: &Visit{
 					From: &As{
 						From: &Vertex{},
 						Name: "liker",
@@ -85,7 +85,7 @@ var testCases = []struct {
 		name: "Back",
 		data: singleQuadData,
 		query: &Back{
-			From: &View{
+			From: &Visit{
 				From: &Vertex{
 					Values: []quad.Value{quad.IRI("alice")},
 				},
@@ -101,12 +101,12 @@ var testCases = []struct {
 		},
 	},
 	{
-		name: "ViewBoth",
+		name: "Both",
 		data: []quad.Quad{
 			quad.MakeIRI("alice", "likes", "bob", ""),
 			quad.MakeIRI("bob", "likes", "dan", ""),
 		},
-		query: &ViewBoth{
+		query: &Both{
 			From: &Vertex{
 				Values: []quad.Value{quad.IRI("bob")},
 			},
@@ -265,7 +265,7 @@ var testCases = []struct {
 	{
 		name: "ViewReverse",
 		data: singleQuadData,
-		query: &ViewReverse{
+		query: &VisitReverse{
 			From:       &Vertex{Values: []quad.Value{}},
 			Properties: &Vertex{Values: []quad.Value{quad.IRI("likes")}},
 		},
@@ -290,14 +290,14 @@ var testCases = []struct {
 			quad.MakeIRI("dani", "likes", "alice", ""),
 		},
 		query: &Intersect{
-			From: &View{
+			From: &Visit{
 				From: &Vertex{Values: []quad.Value{quad.IRI("bob")}},
 				Properties: &Vertex{
 					Values: []quad.Value{quad.IRI("likes")},
 				},
 			},
 			Steps: []PathStep{
-				&View{
+				&Visit{
 					From:       &Vertex{Values: []quad.Value{quad.IRI("bob")}},
 					Properties: &Vertex{Values: []quad.Value{quad.IRI("likes")}},
 				},
@@ -312,7 +312,7 @@ var testCases = []struct {
 		data: singleQuadData,
 		query: &Is{
 			Values: []quad.Value{quad.IRI("bob")},
-			From: &View{
+			From: &Visit{
 				From: &Vertex{Values: []quad.Value{quad.IRI("alice")}},
 				Properties: &Vertex{
 					Values: []quad.Value{quad.IRI("likes")},
@@ -340,7 +340,7 @@ var testCases = []struct {
 	{
 		name: "View",
 		data: singleQuadData,
-		query: &View{
+		query: &Visit{
 			From:       &Vertex{Values: []quad.Value{}},
 			Properties: &Vertex{Values: []quad.Value{quad.IRI("likes")}},
 		},
@@ -454,7 +454,7 @@ var testCases = []struct {
 		data: singleQuadData,
 		query: &SelectFirst{
 			From: &As{
-				From: &View{
+				From: &Visit{
 					From: &As{
 						Name: "liker",
 						From: &Vertex{},
@@ -538,8 +538,8 @@ var testCases = []struct {
 					From: &Vertex{},
 					Steps: []PathStep{
 						&As{
-							From: &View{
-								From: &View{
+							From: &Visit{
+								From: &Visit{
 									From:       &Placeholder{},
 									Properties: &Vertex{Values: []quad.Value{quad.IRI("likes")}},
 								},
@@ -548,7 +548,7 @@ var testCases = []struct {
 							Name: "likesName",
 						},
 						&As{
-							From: &View{
+							From: &Visit{
 								From:       &Placeholder{},
 								Properties: &Vertex{Values: []quad.Value{quad.IRI("name")}},
 							},