Structured Queries
query() and query_properties() are composable structured filters added in 0.3.0. Unlike search_by_label — which returns a ranked fuzzy result set — these methods combine text and structural filters with an explicit match mode and return a plain, unranked list of classes (or object properties) that satisfy every filter.
Querying classes
query() walks the full class list once and returns everything that matches. All filters are AND’d together, so each additional filter narrows the result set further.
f.query(
label=None,
definition=None,
alt_label=None,
example=None,
any_text=None,
branch=None,
parent_iri=None,
has_children=None,
deprecated=False,
country=None,
match_mode="substring",
limit=20,
)It returns List[OWLClass] — no scores, unlike search_by_label which returns List[Tuple[OWLClass, float]]. Results come back in class-list order and are capped at limit (default 20). Deprecated classes are excluded by default; set deprecated=True to include them.
Text filters
Every text filter respects the current match_mode. You usually specify at least one, but query() also works with only structural filters (e.g. “all leaf classes in Area of Law”).
label
Matches against the class label field (rdfs:label).
from folio import FOLIO
f = FOLIO()
for c in f.query(label="Michigan")[:5]:
print(c.label)
# Output:
# U.S. Bankruptcy Court - E.D. Michigan
# U.S. District Court - D. Michigan
# U.S. Bankruptcy Court - W.D. Michigan
# U.S. District Court - W.D. Michigan
# Michigan Supreme Courtf.query(label="Michigan") returns 16 classes in total — every class whose label contains michigan (case-insensitively).
definition
Matches against the definition field (skos:definition).
for c in f.query(definition="bankruptcy", limit=3):
print(c.label)
# Output:
# Bankruptcy Claims Practice
# Domestic Support Obligation Form
# Chapter 11 Bankruptcy Planalt_label
Matches against any entry in alternative_labels (skos:altLabel) — how you find classes by acronym or synonym.
for c in f.query(alt_label="SDNY"):
print(c.label, "→", c.alternative_labels)
# Output:
# U.S. District Court - S.D. New York → ['S.D. N.Y.', 'S.D.N.Y.', 'SDNY', 'Southern District of New York', 'NYSD']example
Matches against any entry in examples (skos:example).
for c in f.query(example="merger"):
print(c.label)
# Output:
# Cross-Border Objectiveany_text
A union filter that matches if the pattern appears in label, definition, any alternative_labels, any examples, any notes, comment, or description. Use this when you don’t care which field holds the match.
for c in f.query(any_text="arbitration", limit=5):
print(c.label)
# Output:
# Arbitration and Award
# 896 Arbitration (PACER NoS)
# AAA Employment Arbitration Rules and Mediation Procedures
# Litigation Practice
# Alternative Dispute Resolution VenuesLitigation Practice appears because arbitration lives in its definition or notes, not its label — that’s the whole point of any_text.
Match modes
match_mode controls how every text filter compares its pattern against a field. It applies uniformly to label, definition, alt_label, example, and any_text within a single call.
substring
Default. Case-insensitive substring match — pattern.lower() in text.lower(). Right for most interactive use.
exact
Case-sensitive strict equality — text == pattern. Use when you already know the canonical label.
for c in f.query(label="Contract Law", match_mode="exact"):
print(c.label, c.iri)
# Output:
# Contract Law https://folio.openlegalstandard.org/RCIPwpgRpMs1eVz4vPid0pVregex
re.search(pattern, text, re.IGNORECASE). The pattern is a full Python regular expression, not a glob.
for c in f.query(label="^Tax", match_mode="regex", limit=5):
print(c.label)
# Output:
# Taxi and Limousine Service
# Tax Rate Percent
# Tax Detail
# Tax Practice
# Tax CourtWatch out: ^Tax matches Taxi and Limousine Service — the anchor only constrains position, not word boundaries. Use ^Tax\b for whole-word matches.
fuzzy
rapidfuzz.fuzz.partial_ratio(pattern.lower(), text.lower()) >= 70. Requires the [search] extra — the method raises RuntimeError("search extra required for fuzzy matching") if rapidfuzz is not installed.
for c in f.query(label="contrct", match_mode="fuzzy", limit=5):
print(c.label)
# Output:
# Other Converted Paper Product Manufacturing
# Confectionery Merchant Wholesalers
# Chocolate and Confectionery Manufacturing from Cacao Beans
# Food Service Contractors
# Masonry ContractorsThe fuzzy threshold is fixed at 70 and the scorer is partial_ratio, which is generous with long strings — expect surprising matches. For ranked autocomplete-style search, prefer search_by_label.
Structural filters
Structural filters narrow the candidate set alongside the text filters and compose freely with them.
branch
Limits results to a single top-level FOLIO branch. Pass either the FOLIOTypes enum name ("AREA_OF_LAW") or its human-readable value ("Area of Law") — both resolve to the same branch.
len(f.query(branch="AREA_OF_LAW", limit=1000)) # 161
len(f.query(branch="Area of Law", limit=1000)) # 161
len(f.query(branch="NotARealBranch")) # 0 (unknown branch → [])Both forms return 161 classes — every descendant of the Area of Law branch root (RSYBzf149Mi5KE0YtmpUmr). The 24 valid names are the members of FOLIOTypes: ACTOR_PLAYER, AREA_OF_LAW, ASSET_TYPE, COMMUNICATION_MODALITY, CURRENCY, DATA_FORMAT, DOCUMENT_ARTIFACT, ENGAGEMENT_TERMS, EVENT, FORUMS_VENUES, GOVERNMENTAL_BODY, INDUSTRY, LANGUAGE, FOLIO_TYPE, LEGAL_AUTHORITIES, LEGAL_ENTITY, LOCATION, MATTER_NARRATIVE, MATTER_NARRATIVE_FORMAT, OBJECTIVES, SERVICE, STANDARDS_COMPATIBILITY, STATUS, SYSTEM_IDENTIFIERS.
parent_iri
Restricts results to descendants of the given IRI (transitive rdfs:subClassOf). The parent itself is not included in the result set.
for c in f.query(parent_iri="RCIPwpgRpMs1eVz4vPid0pV", limit=1000):
print(c.label)
# Output:
# Property Rights and Transactions Law
# Civil Contract Law
# Commercial Transactions Law
# Government Contracts Law
# Employment Contracts LawSix entries come back, not five — Property Rights and Transactions Law appears twice because it is reachable via two inheritance paths under Contract Law. Dedupe by IRI if that matters. And because parent_iri filters to descendants, passing a leaf IRI returns zero (f.query(parent_iri="R8BD30978Ccbc4C2f0f8459f") on Michigan is []) — for meaningful examples, pick a non-leaf like Contract Law (RCIPwpgRpMs1eVz4vPid0pV) or Area of Law (RSYBzf149Mi5KE0YtmpUmr).
has_children
True keeps only non-leaf classes; False keeps only leaves; None (default) keeps both. Combined with branch, this is the cleanest way to enumerate the leaves of a branch:
for c in f.query(branch="Area of Law", has_children=False, limit=5):
print(c.label)
# Output:
# Patent Law
# Reduction in Force Law
# Trade Secret Law
# Cybersecurity Law
# Indigenous People's Lawdeprecated
By default, query() skips classes where cls.deprecated is True. Set deprecated=True to include them in the result set — note that this does not restrict the result to only deprecated classes; it just stops filtering them out. If you want only the truly-deprecated classes, you need to filter the results yourself:
# `label="DEPRECATED"` does a substring match on the label, so it finds many classes
# whose label happens to start with the word "DEPRECATED:" — most of which are NOT
# actually flagged with `deprecated=True`. Filter the results explicitly:
deprecated_classes = [
c for c in f.query(label="DEPRECATED", deprecated=True)
if c.deprecated
]
print(len(deprecated_classes))
print(deprecated_classes[0].label, deprecated_classes[0].deprecated)
# Output:
# 1
# DEPRECATED Activities TrueFOLIO 2.0.0 has one truly-deprecated class today (DEPRECATED Activities); the rest just carry “DEPRECATED” in their label as a maintainer signal. The deprecated= filter on query() is there primarily for forward compatibility as more classes get flagged.
country
Filters against OWLClass.country, a sparse field set on a few jurisdictional and legal-entity classes. The match respects match_mode.
for c in f.query(country="UK", limit=5):
print(c.label, "→", repr(c.country))
# Output:
# Hotels and Public Houses → 'UK (for Public Houses, also known as "pubs")'Only two classes in 2.0.0 populate country. For nationality-style filtering, the Location branch (branch="Location") is usually a better hook.
Combining filters
Every filter is AND’d with every other filter, so you tighten a query by stacking them:
for c in f.query(any_text="trust", branch="AREA_OF_LAW", limit=5):
print(c.label)
# Output:
# Trusts and Estate Planning Law
# Estates, Gifts, and Trusts Law
# Antitrust and Competition LawThree areas of law mention “trust” in their text fields — and the third (Antitrust and Competition Law) is a substring false-positive on the prefix anti**trust**. That’s structured-query behavior: honest, literal, and up to you to filter further. Mixing text with multiple structural filters works the same way:
for c in f.query(
label="court",
branch="GOVERNMENTAL_BODY",
has_children=False,
limit=5,
):
print(c.label)
# Output:
# U.S. Bankruptcy Court - M.D. Tennessee
# U.S. Bankruptcy Court - N.D. Oklahoma
# U.S. Bankruptcy Court - E.D. Arkansas
# U.S. Bankruptcy Court - W.D. Virginia
# U.S. District Court - D. OregonReads as “leaf classes under the Governmental Body branch whose label contains ‘court’” — and that’s exactly what comes back.
Querying object properties
query_properties() is the sibling method for object properties. It uses the same match-mode machinery but its structural filters target property-specific fields: domain, range, and inverse_of.
f.query_properties(
label=None,
definition=None,
domain_iri=None,
range_iri=None,
has_inverse=None,
match_mode="substring",
limit=20,
)It returns List[OWLObjectProperty]. With no filters it returns every object property in the ontology (175 in FOLIO 2.0.0):
props = f.query_properties(limit=1000)
print(len(props))
for p in props[:3]:
print(p.label)
# Output:
# 175
# hasFigure
# folio:opposed
# folio:observedA label filter finds a property by name — f.query_properties(label="authored") returns [folio:authored]. domain_iri / range_iri filter by which classes a property can apply to; IRIs are normalized, so short IDs and full URIs both work:
# Actor / Player short IRI
props = f.query_properties(domain_iri="R8CdMpOM0RmyrgCCvbpiLS0")
print(len(props), "→", [p.label for p in props[:5]])
# Output:
# 13 → ['folio:observed', 'folio:drafted', 'folio:authored', 'folio:received', 'folio:preparedFor']Thirteen object properties declare Actor / Player in their owl:Domain — the complete catalog of things an actor can do to or with another FOLIO class. The rest include folio:signed, folio:asserted, folio:isDefending, folio:sent, folio:workedFor, folio:took, folio:cited, and folio:participatedIn.
has_inverse
has_inverse=True keeps only properties with a populated owl:inverseOf; has_inverse=False keeps only properties without one. Use this to find bidirectional relationships — pairs of properties that let you traverse the graph in either direction.
for p in f.query_properties(has_inverse=True):
print(p.label, "↔", p.inverse_of)
# Output:
# folio:hasNext ↔ folio:precededByOnly one object property in FOLIO 2.0.0 declares an explicit inverse, but the filter exists so bidirectional traversal keeps working as the ontology grows.
query() vs search_by_label()
query() is a filter: substring/exact/regex/fuzzy text filters AND’d with structural filters (branch, parent, leafness, deprecation, country), returning an unranked List[OWLClass]. Its fuzzy mode is a fixed partial_ratio >= 70 threshold — good for typo tolerance, not for ranking quality. search_by_label() is a ranker: rapidfuzz.WRatio scoring that returns List[Tuple[OWLClass, float]] sorted by score, with no structural filters — the right primitive for typeahead, autocomplete, and “did you mean” workflows (see Searching). Rule of thumb: reach for search_by_label when you’re showing results to a user one at a time, and for query() when you’re building a result set to process programmatically.
See also
See also: Searching for fuzzy ranked search and prefix lookups, Properties & Relationships for find_connections and the triples API, or the API Reference for the full method catalog.