Skip to content

Commit 46ff6cf

Browse files
WhiteGoboWhiteGobo
andauthored
Fix: SPARQL count with optionals (#2448)
Change SPARQL count aggregate to ignore optional that are unbound instead of raising an exception when they are encountered. Co-authored-by: WhiteGobo <richardfechner@posteo.net>
1 parent 1e5f56b commit 46ff6cf

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

rdflib/plugins/sparql/aggregates.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,11 @@ def eval_full_row(self, row: FrozenBindings) -> FrozenBindings:
8989
return row
9090

9191
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
9397

9498

9599
@overload

test/test_sparql/test_agg_distinct.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from rdflib import Graph
1+
from rdflib import Graph, URIRef
2+
from rdflib.term import Literal
23

34
query_tpl = """
45
SELECT ?x (MIN(?y_) as ?y) (%s(DISTINCT ?z_) as ?z) {
@@ -116,3 +117,39 @@ def test_count_distinct():
116117
"""
117118
)
118119
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+
}

0 commit comments

Comments
 (0)