httk.data.optimade_query¶
Generic translation of OPTIMADE filter syntax trees into backend search expressions.
This module turns the OPTIMADE filter language — parsed by
httk.core.parse_optimade_filter() into a httk.core.FilterAst
nested-tuple syntax tree — into SearchExpression
objects over the backend-agnostic store/searcher protocols of
httk.data.query. It is pure and store-independent: any
Store whose search variables expose the queried
fields can execute the translated expressions.
The translation is driven by three inputs:
property_fulltypes— a minimal mapping from recognized property names to their OPTIMADEfulltypestrings ("string","integer","float","boolean","timestamp","dict","unknown", or"list of ..."), used to type-check and convert filter constants;handlers— aHandlerTablemapping property names to the callables that build the actual search expressions (seesimple_property_handlers()for the generic property-key-driven builder, andrelationship_id_handler()for<type>.idrelationship entries);recognized_prefixes— property-name prefixes the serving side claims: an unknown property carrying such a prefix is an error ("unrecognized-property"), while any other unknown property silently matches nothing, as the OPTIMADE specification prescribes.
Failures raise FilterTranslationError, which carries a neutral
category (no HTTP semantics); consumers map categories onto their
transport’s error codes.
Relationship filtering (dotted identifiers such as references.doi) is
supported for relationship types named in relationship_targets:
<type>.id HAS ... translates directly through the handler table, and any
other depth-1 dotted filter is resolved by a two-phase semi-join through a
supplied RelatedPropertyResolver (see translate_filter_ast()).
Each dotted filter node is resolved independently: in references.doi
CONTAINS "x" AND references.year >= 2000, some related reference must match
the doi condition and some (possibly different) related reference must match
the year condition.
Attributes¶
Why a filter could not be translated (see |
|
Per-property translation callables, keyed by property name. |
|
Resolve a depth-1 relationship filter to the matching related-entry ids. |
|
Exceptions¶
A filter cannot be translated into a search expression. |
Functions¶
|
Convert a filter constant node to a Python value of the property's |
|
A constant-true expression over |
|
A constant-false expression over |
|
|
|
|
|
|
|
|
|
|
|
Translate |
Fold |
|
Fold |
|
|
|
|
|
|
Translate one HAS family node over the list-valued field |
|
|
|
Translate one filter syntax-tree node into a search expression. |
|
Build a filter handler table for an entry type from a property-key map. |
|
A |
|
Build a |
Module Contents¶
- type httk.data.optimade_query.FilterTranslationCategory = Literal['unrecognized-property', 'not-implemented', 'type-mismatch', 'internal'][source]¶
Why a filter could not be translated (see
FilterTranslationError).
- type httk.data.optimade_query.HandlerTable = Mapping[str, Mapping[str, Callable[..., Any]]][source]¶
Per-property translation callables, keyed by property name.
The inner mapping’s keys name the filter-operation families:
'comparison'(=,!=,<,<=,>,>=),'stringmatching'(CONTAINS/STARTS/ENDS),'HAS'(the set operations),'length'(LENGTH), and'unknown'(IS KNOWN/IS UNKNOWN). A'HAS'handler is called ashandler(property, ops, values, search_variable, has_type)and returns a plainSearchExpression. The caller appliesNOTas~; handlers do not receive negation state or report post-filter placement. Dotted'<type>.id'entries provide relationship-id filtering (seerelationship_id_handler()).
- type httk.data.optimade_query.RelatedPropertyResolver = Callable[[str, FilterAst], tuple[str, ...]][source]¶
Resolve a depth-1 relationship filter to the matching related-entry ids.
Called as
resolver(related_type, sub_ast)wheresub_astis the filter node with the<related_type>.prefix stripped from its identifier (and without any surroundingNOT); returns the ids of the related entries matching the sub-filter, evaluated against the related type’s own properties.
- exception httk.data.optimade_query.FilterTranslationError(message: str, category: FilterTranslationCategory, detail: str | None = None)[source]¶
Bases:
ExceptionA filter cannot be translated into a search expression.
The exception message describes the failure;
categoryclassifies it neutrally (this module knows nothing about transports, so consumers map each category onto their own error codes):"unrecognized-property"— the filter names an unknown property carrying a recognized prefix (a caller error);"type-mismatch"— a filter constant does not match the property’s declared type (a caller error);"not-implemented"— the filter uses a construct this translation (or the supplied handler table) does not support;"internal"— an inconsistency in the translation itself.
detailoptionally carries extra machine-readable context.- category: FilterTranslationCategory[source]¶
- httk.data.optimade_query.format_value(fulltype: str, val: tuple[Any, Ellipsis], allow_null: bool = False) Any[source]¶
Convert a filter constant node to a Python value of the property’s
fulltype.- Raises:
FilterTranslationError – With category
"type-mismatch"when the constant does not fit the declared type, or"not-implemented"for dictionary-typed properties.
- httk.data.optimade_query.true_handler(search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
A constant-true expression over
search_variable.
- httk.data.optimade_query.false_handler(search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
A constant-false expression over
search_variable.
- httk.data.optimade_query.unknown_unknown_handler(entry: str, search_variable: httk.data.query.SearchVariable, unknown_type: str) httk.data.query.SearchExpression[source]¶
- httk.data.optimade_query.known_unknown_handler(entry: str, search_variable: httk.data.query.SearchVariable, unknown_type: str) httk.data.query.SearchExpression[source]¶
- httk.data.optimade_query.unknown_comparison_handler(entry: str, ops: Any, values: Any, search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
- httk.data.optimade_query.unknown_stringmatching_handler(entry: str, values: Any, stringmatching_type: str, search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
- httk.data.optimade_query.unknown_has_handler(entry: str, op: Any, value: Any, search_variable: httk.data.query.SearchVariable, has_type: str) httk.data.query.SearchExpression[source]¶
- httk.data.optimade_query.unknown_length_handler(entry: str, op: str, value: Any, search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
- httk.data.optimade_query.string_handler(entry: str, op: str, value: Any, search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
- httk.data.optimade_query.stringmatching_handler(entry: str, value: str, stringmatching_type: str, search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
Translate
CONTAINS/STARTS/ENDSonto the field’s literal matchers.The filter constant is passed through unescaped: the search-field protocol’s
contains/startswith/endswithtake literal text, so%and_match themselves (as OPTIMADE requires). Any escaping a backend’s pattern language needs is that backend’s own business.
- httk.data.optimade_query.constant_comparison_handler(val1: Any, op: str, val2: Any, search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
Fold
val1 <op> val2(both constants) to a constant expression.val1is the value being compared andval2the filter constant, the same left-to-right convention asconstant_set_handler();translate_filter_ast()has already invertedopfor constant-on-the-left filters, so this ordering is the filter’s own.
- httk.data.optimade_query.constant_stringmatching_handler(val1: Any, val2: Any, stringmatching_type: str, search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
Fold
val1 <CONTAINS|STARTS|ENDS> val2(both constants) to a constant expression.val1is the value being matched andval2the filter text, the same left-to-right convention asconstant_set_handler().
- httk.data.optimade_query.number_handler(entry: str, op: str, value: Any, search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
- httk.data.optimade_query.timestamp_handler(entry: str, op: str, value: Any, search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
- httk.data.optimade_query.set_handler(entry: str, ops: Any, values: Any, has_type: str, search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
Translate one HAS family node over the list-valued field
entry.HAS ALLbecomes a conjunction of one-valuehas_anycalls (each constraining an independently joined child row);HAS ANYandHAS ONLYmap straight ontohas_any/has_only. Negation is not the handler’s business: a surroundingNOTinverts the returned expression, and the backend’s~knows how to negate a set predicate.
- httk.data.optimade_query.constant_set_handler(val1: Any, ops: Any, val2: Any, has_type: str, search_variable: httk.data.query.SearchVariable) httk.data.query.SearchExpression[source]¶
- httk.data.optimade_query.translate_filter_ast(node: httk.core.FilterAst, search_variable: httk.data.query.SearchVariable, entry_type: str, property_fulltypes: collections.abc.Mapping[str, str], handlers: HandlerTable, recognized_prefixes: tuple[str, Ellipsis], *, recursion: int = 0, relationship_targets: tuple[str, Ellipsis] = (), related_property_resolver: RelatedPropertyResolver | None = None) httk.data.query.SearchExpression[source]¶
Translate one filter syntax-tree node into a search expression.
nodeis aFilterAstnode (as produced byhttk.core.parse_optimade_filter());search_variableis the backend search variable the expression is built against;property_fulltypes,handlers, andrecognized_prefixesdrive the translation as described in the module docstring;recursioncounts the nesting depth.Returns the translated expression, ready to hand to
add().NOTtranslates to the backend’s~; a backend whose set predicates need a second (post-filter) evaluation position derives that from the expression itself, so the caller never has to.Relationship filtering: an identifier dotted with a name in
relationship_targets(e.g.('Identifier', 'references', 'doi')) filters on the properties of related entries.<type>.id HAS ...is served directly by the'<type>.id'handler-table entry. Every other depth-1 dotted filter — comparisons (including<type>.id = ...), string matching,IS KNOWN/IS UNKNOWN, and the HAS family over related list properties — is resolved by a two-phase semi-join: the<type>.prefix is stripped from the node, the resulting sub-filter is passed torelated_property_resolver(without any surroundingNOT— inversion applies to the resulting id-set membership), and the returned ids are substituted back as<type>.id HAS ANY <ids>(an empty id set translates to a constant-false expression). Each dotted node is resolved independently:references.doi CONTAINS "x" AND references.year >= 2000matches entries where some related reference matches the doi condition and some — possibly different — related reference matches the year condition. Without a resolver, dotted filters other than<type>.id HAS ...are not implemented; nested (deeper than depth-1) paths and dottedLENGTHfilters are never supported.- Raises:
FilterTranslationError – See
FilterTranslationErrorfor the failure categories.
- httk.data.optimade_query.simple_property_handlers(entry_type: str, property_keys: collections.abc.Mapping[str, str], property_fulltypes: collections.abc.Mapping[str, str]) dict[str, collections.abc.Mapping[str, collections.abc.Callable[Ellipsis, Any]]][source]¶
Build a filter handler table for an entry type from a property-key map.
Always provides the standard
id(matched against the__idfield) andtype(a constant equal toentry_type) handlers. For every property named inproperty_keys(which maps property names to backend field names), handlers are generated from the property’s fulltype inproperty_fulltypes(default"string"): string properties get comparison and stringmatching handlers; integer and float properties get a numeric comparison handler;list of ...properties get a HAS (set membership) handler. Every generated property also gets aknownunknown handler.
- httk.data.optimade_query.relationship_id_handler(rel_key: str) collections.abc.Mapping[str, collections.abc.Callable[Ellipsis, Any]][source]¶
A
'<type>.id'handler-table entry matching related ids over a list-valued field.rel_keynames a list-valued backend field holding the related entry ids; the returned{'HAS': ...}mapping serves<type>.id HAS ...filters (and the semi-join rewrites of other dotted filters) with the standard set-operation semantics ofset_handler().
- httk.data.optimade_query.filter_searcher(store: httk.data.query.Store, target: Any, filter_string: str | httk.core.FilterAst, *, entry_type: str, property_fulltypes: collections.abc.Mapping[str, str], property_keys: collections.abc.Mapping[str, str] | None = None, handlers: HandlerTable | None = None, recognized_prefixes: tuple[str, Ellipsis] = (), relationship_targets: tuple[str, Ellipsis] = (), related_property_resolver: RelatedPropertyResolver | None = None) httk.data.query.Searcher[source]¶
Build a
Searcheroverstoreapplying an OPTIMADE filter.filter_stringis an OPTIMADE filter string (parsed withhttk.core.parse_optimade_filter()) or an already-parsedFilterAst. The searcher binds one search variable totarget(the store-specific query target, declared as the searcher output namedentry_type) and applies the translated filter. Whenhandlersis not supplied, a default table is built withsimple_property_handlers()fromproperty_keys(or, whenproperty_keysis also None, from an identity map overproperty_fulltypes). The remaining keyword arguments are passed through totranslate_filter_ast().- Raises:
FilterTranslationError – When the filter cannot be translated.
httk.core.ParserSyntaxError – When a filter string does not parse.