@@ -450,8 +450,9 @@ def _contains_union_with_marker(ce: OWLClassExpression) -> bool:
450450
451451 When a UNION involves the marker variable (``?class``), the standard
452452 sub-query approach does not work because ``?class`` is only bound
453- inside one branch of the UNION. In this case the query must use
454- ``?anything a ?class .`` to bind ``?class`` independently.
453+ inside one branch of the UNION. In this case the query must
454+ pre-enumerate ``?class`` via a selective ``SELECT DISTINCT ?class``
455+ subquery scoped to the example individuals.
455456 """
456457 if isinstance (ce , OWLClass ):
457458 return False
@@ -552,13 +553,24 @@ def as_class_query(
552553
553554 # -- 3. Assemble final query ------------------------------------------
554555 # When the context contains a UNION involving the marker, we need
555- # a different structure: bind ?class independently first with
556- # ``?anything a ?class .`` so that it is visible across UNION branches.
556+ # a different structure: pre-enumerate ?class with a selective
557+ # ``SELECT DISTINCT ?class`` subquery scoped to the example
558+ # individuals so that ?class is visible across UNION branches.
557559 if self ._contains_union_with_marker (context ):
560+ # Use a selective subquery to pre-enumerate only ?class values
561+ # that appear among the example individuals, avoiding a full
562+ # graph scan that ``?anything a ?class .`` would cause.
563+ binding_subquery = (
564+ " { SELECT DISTINCT ?class WHERE {\n "
565+ " { " + context_string + " }\n "
566+ " UNION\n "
567+ " { " + neg_context + " }\n "
568+ " } }\n "
569+ )
558570 query_parts = [
559571 "SELECT ?class (COUNT(DISTINCT " + root_variable_pos + ") AS ?posHits) "
560572 "(COUNT(DISTINCT " + root_variable_neg + ") AS ?negHits) WHERE {\n " ,
561- " ?anything a ?class . \n " ,
573+ binding_subquery ,
562574 " {\n " ,
563575 context_string ,
564576 "\n }\n " ,
@@ -773,13 +785,24 @@ def as_property_query(
773785
774786 # -- 3. Assemble final query -------------------------------------------
775787 # When the context contains a UNION involving the marker, we need
776- # a different structure: bind ?prop independently first with
777- # ``?anything ?prop [] .`` so that it is visible across UNION branches.
788+ # a different structure: pre-enumerate ?prop with a selective
789+ # ``SELECT DISTINCT ?prop`` subquery scoped to the example
790+ # individuals so that ?prop is visible across UNION branches.
778791 if self ._contains_union_with_marker (context ):
792+ # Use a selective subquery to pre-enumerate only ?prop values
793+ # that appear among the example individuals, avoiding a full
794+ # graph scan that ``?anything ?prop [] .`` would cause.
795+ binding_subquery = (
796+ " { SELECT DISTINCT ?prop WHERE {\n "
797+ " { " + context_string + " }\n "
798+ " UNION\n "
799+ " { " + neg_context + " }\n "
800+ " } }\n "
801+ )
779802 query_parts = [
780803 "SELECT ?prop (COUNT(DISTINCT " + root_variable_pos + ") AS ?posHits) "
781804 "(COUNT(DISTINCT " + root_variable_neg + ") AS ?negHits) WHERE {\n " ,
782- " ?anything ?prop [] . \n " ,
805+ binding_subquery ,
783806 " {\n " ,
784807 context_string ,
785808 "\n }\n " ,
0 commit comments