Summary
Follow-up to #117. search_entities() iterates only the three OWL property subkinds (OWL.ObjectProperty, OWL.DatatypeProperty, OWL.AnnotationProperty). Properties typed as bare rdf:Property — common in pre-OWL ontologies, RDFS-only vocabularies, and loosely-typed legacy data — are silently excluded from search results.
Current code
ontokit/services/ontology.py (after #117):
type_mapping: dict[str, list[tuple[URIRef, str, str | None]]] = {
"class": [(OWL.Class, "class", None)],
"property": [
(OWL.ObjectProperty, "property", "object"),
(OWL.DatatypeProperty, "property", "data"),
(OWL.AnnotationProperty, "property", "annotation"),
],
"individual": [(OWL.NamedIndividual, "individual", None)],
}
A property declared as :hasFoo rdf:type rdf:Property (no OWL subtype) is invisible to the entity search endpoint and won't show up in any frontend autocomplete, picker, or tree.
Why it matters
- Pre-OWL ontologies (RDF/RDFS-only) declare every property as
rdf:Property.
- Many SKOS-style or hybrid vocabularies use bare
rdf:Property for properties they don't want to commit to a specific OWL kind.
- Some imported third-party vocabularies are loosely typed.
Proposed fix
Add (RDF.Property, "property", None) to the property mapping, then dedupe results by IRI so a property typed as both owl:ObjectProperty AND rdf:Property (belt-and-suspenders, not uncommon) isn't emitted twice. The OWL-subkind result wins; the bare rdf:Property result is dropped.
property_kind=None on the bare-rdf:Property results is already the shape the schema's defensive fallback was built for — see #117.
Sketch
type_mapping["property"].append((RDF.Property, "property", None))
# After the loop, dedupe: keep the entry with non-None property_kind
# when an IRI appears multiple times.
seen: dict[str, EntitySearchResult] = {}
for r in results:
existing = seen.get(r.iri)
if existing is None or (existing.property_kind is None and r.property_kind is not None):
seen[r.iri] = r
results = list(seen.values())
Acceptance
Out of scope
- Other RDF property characteristics (
owl:FunctionalProperty, TransitiveProperty, etc.) — those are modifiers, not kinds; they decorate an existing OWL subtype rather than replace it
owl:OntologyProperty (mostly built-in vocabulary, rarely user-defined)
owl:DeprecatedProperty (a flag, not a kind — already surfaced via the deprecated field on EntitySearchResult)
Origin
Surfaced during review of #117 / PR #120 — the defensive property_kind=None fallback was added there but doesn't actually fire today because the iteration is keyed on the three OWL subkinds. This issue closes that gap.
Summary
Follow-up to #117.
search_entities()iterates only the three OWL property subkinds (OWL.ObjectProperty,OWL.DatatypeProperty,OWL.AnnotationProperty). Properties typed as barerdf:Property— common in pre-OWL ontologies, RDFS-only vocabularies, and loosely-typed legacy data — are silently excluded from search results.Current code
ontokit/services/ontology.py(after #117):A property declared as
:hasFoo rdf:type rdf:Property(no OWL subtype) is invisible to the entity search endpoint and won't show up in any frontend autocomplete, picker, or tree.Why it matters
rdf:Property.rdf:Propertyfor properties they don't want to commit to a specific OWL kind.Proposed fix
Add
(RDF.Property, "property", None)to the property mapping, then dedupe results by IRI so a property typed as bothowl:ObjectPropertyANDrdf:Property(belt-and-suspenders, not uncommon) isn't emitted twice. The OWL-subkind result wins; the barerdf:Propertyresult is dropped.property_kind=Noneon the bare-rdf:Property results is already the shape the schema's defensive fallback was built for — see #117.Sketch
Acceptance
:hasFoo rdf:type rdf:Property(no OWL subtype) appears insearch_entitiesresultsproperty_kindisNoneowl:ObjectPropertyANDrdf:Propertyis emitted exactly once withproperty_kind="object"(no duplicate withNone)rdf:Propertycase and the dedupe caseOut of scope
owl:FunctionalProperty,TransitiveProperty, etc.) — those are modifiers, not kinds; they decorate an existing OWL subtype rather than replace itowl:OntologyProperty(mostly built-in vocabulary, rarely user-defined)owl:DeprecatedProperty(a flag, not a kind — already surfaced via thedeprecatedfield onEntitySearchResult)Origin
Surfaced during review of #117 / PR #120 — the defensive
property_kind=Nonefallback was added there but doesn't actually fire today because the iteration is keyed on the three OWL subkinds. This issue closes that gap.