Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/test/test_expression_pattern_individual_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,11 @@ def test_expected_ep_queries_present(self):
if not self.ind:
self.skipTest("term_info unavailable (no live VFB backend)")
ind_menu = _menu(self.ind)
# SubclassesOf is intentionally NOT expected: it is gated on has_subClass,
# and this expression-pattern class is a leaf (no subclasses), so the query
# would only ever return empty.
for qid in ("AnatomyExpressedIn", "epFrag", "ListAllAvailableImages",
"NeuronsPartHere", "PartsOf", "SubclassesOf"):
"NeuronsPartHere", "PartsOf"):
self.assertIn(qid, ind_menu, f"expected {qid} inherited onto the EP instance")
self.assertEqual(ind_menu[qid], self.EP_CLASS)

Expand Down
24 changes: 15 additions & 9 deletions src/vfbquery/vfb_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -1102,9 +1102,14 @@ def term_info_parse_object(results, short_form):
q = PartsOf_to_schema(termInfo["Name"], {"short_form": vfbTerm.term.core.short_form})
queries.append(q)

# SubclassesOf query - for any Class
# Matches XMI criteria: Class (any)
if contains_all_tags(termInfo["SuperTypes"], ["Class"]):
# SubclassesOf query - for classes that actually have subclasses.
# Gate: Class + has_subClass. The has_subClass label is the KB's "has
# direct children" flag; ~93% of classes are leaves (no subclasses), so
# gating on it both drops the empty query from leaf pages and avoids the
# guaranteed-empty Owlery call that fill_query_results would otherwise
# run for the preview. The label is already in SuperTypes, so the check
# is free.
if contains_all_tags(termInfo["SuperTypes"], ["Class", "has_subClass"]):
q = SubclassesOf_to_schema(termInfo["Name"], {"short_form": vfbTerm.term.core.short_form})
queries.append(q)

Expand Down Expand Up @@ -1414,7 +1419,7 @@ def term_info_parse_object(results, short_form):
(DownstreamClassConnectivity_to_schema, lambda p: {"Class", "Neuron"} <= p),
(UpstreamClassConnectivity_to_schema, lambda p: {"Class", "Neuron"} <= p),
(PartsOf_to_schema, lambda p: {"Class", "Anatomy"} <= p and "Cell" not in p),
(SubclassesOf_to_schema, lambda p: "Class" in p),
(SubclassesOf_to_schema, lambda p: {"Class", "has_subClass"} <= p),
)
for schema_fn, predicate in inheritable_class_queries:
if predicate(p_types):
Expand Down Expand Up @@ -1835,18 +1840,19 @@ def SubclassesOf_to_schema(name, take_default):
"""
Schema for SubclassesOf query.
Finds subclasses of the specified class.

Matching criteria from XMI:
- Class (any)


Matching criteria:
- Class + has_subClass (only classes that actually have subclasses; gated
in term_info_parse_object).

Query chain: Owlery subclasses query → process → SOLR
OWL query: Direct subclasses of $ID
"""
query = "SubclassesOf"
label = f"Subclasses of {name}"
function = "get_subclasses_of"
takes = {
"short_form": {"$and": ["Class"]},
"short_form": {"$and": ["Class", "has_subClass"]},
"default": take_default,
}
preview = 5
Expand Down
Loading