Fix: SPARQL count with optionals (#2448) · RDFLib/rdflib@46ff6cf (original) (raw)

2 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -89,7 +89,11 @@ def eval_full_row(self, row: FrozenBindings) -> FrozenBindings:
89 89 return row
90 90
91 91 def use_row(self, row: FrozenBindings) -> bool:
92 -return self.eval_row(row) not in self.seen
92 +try:
93 +return self.eval_row(row) not in self.seen
94 +except NotBoundError:
95 +# happens when counting zero optional nodes. See issue #2229
96 +return False
93 97
94 98
95 99 @overload
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
1 -from rdflib import Graph
1 +from rdflib import Graph, URIRef
2 +from rdflib.term import Literal
2 3
3 4 query_tpl = """
4 5 SELECT ?x (MIN(?y_) as ?y) (%s(DISTINCT ?z_) as ?z) {
@@ -116,3 +117,39 @@ def test_count_distinct():
116 117 """
117 118 )
118 119 assert list(results)[0][0].toPython() == 2
120 +
121 +
122 +def test_count_optional_values():
123 +"""Problematic query because ?inst may be not bound.
124 + So when counting over not bound variables it throws a NotBoundError.
125 + """
126 +g = Graph()
127 +g.bind("ex", "http://example.com/")
128 +g.parse(
129 +format="ttl",
130 +data="""@prefix ex: http://example.com/.
131 + ex:1 a ex:a;
132 + ex:d ex:b.
133 + ex:2 a ex:a;
134 + ex:d ex:c;
135 + ex:d ex:b.
136 + ex:3 a ex:a.
137 + """,
138 + )
139 +
140 +query = """
141 + SELECT DISTINCT ?x (COUNT(DISTINCT ?inst) as ?cnt)
142 + WHERE {
143 + ?x a ex:a
144 + OPTIONAL {
145 + VALUES ?inst {ex:b ex:c}.
146 + ?x ex:d ?inst.
147 + }
148 + } GROUP BY ?x
149 + """
150 +results = dict(g.query(query))
151 +assert results == {
152 +URIRef("http://example.com/1"): Literal(1),
153 +URIRef("http://example.com/2"): Literal(2),
154 +URIRef("http://example.com/3"): Literal(0),
155 + }