module documentation

Managing JSON-path filter queries for oneM2M contentFilterQuery filterCriteria.

Implements the 'JSON-path' contentFilterSyntax for oneM2M contentFilterQuery filterCriteria, as specified in Annex J.2 of TS-0001 ("Syntaxes for content based discovery of <contentInstance>").

The following syntax rules are supported:

  • '$' refers to the entire target data.
  • '[n]' refers to the n-th member of a JSON array.
  • '.name' (dot operator followed by a name) refers to a member of a JSON object.
  • A name MUST be quoted with "'" (single quote) when it contains any of: '$', '.', ' ' (space), '[', ']', '{', '}'.
  • A single space character must separate a reserved keyword from the surrounding query string components.
  • Reserved keywords: EQ, NE, GT, LT, GE, LE, MATCH, AND, OR.

The following assumptions are made in the implementation, since the spec does not provide a complete grammar or examples:

  • AND and OR are evaluated strictly left-to-right, with no precedence between them.
  • MATCH is implemented as a case-SENSITIVE substring test.
  • The query is evaluated once per <contentInstance> (CIN) resource.
  • The implicit root '$' of the path expression is always the CIN's parsed 'con' attribute value (not some larger document).
  • The overall result of evaluating a filterCriteria query against one CIN is a boolean: match (include the CIN in the result set) or no match.
  • If a path expression does not resolve (missing object member, array index out of range,
    indexing into a scalar, etc.), the clause evaluates to "no match" (False) rather than raising an error, for every keyword. This makes filterCriteria evaluation robust against heterogeneous CON payloads.
  • GT/LT/GE/LE require BOTH the resolved value and the literal to be numbers (int or float);
    a type mismatch evaluates to False rather than raising, consistent with A5's "be permissive, don't except" stance. EQ/NE compare equal types only (a number is never == to its string representation); MATCH requires the resolved value to be a string.
  • Literal values (the right-hand side of a clause) are delimited asfollows:
    • A STRING literal is surrounded by DOUBLE quotes ("..."), and is escaped exactly like a JSON string. Decoding is delegated to json.loads so the escaping behaviour is JSON's by construction.
    • A NUMBER literal is a bare, unquoted token parsed as int then float (e.g. 21, 21.5).
Examples of valid filterCriteria queries:
  • $.temperature EQ 21
  • $.temperature GT 21 AND $.humidity LT 50
  • $.sensor.values[2] MATCH "abc"
  • $.sensor.'a.b'[0] NE 42 OR $.sensor.'a b' EQ "hello world"
Class Clause A single clause in a filterCriteria query: a path expression, a comparison keyword, and a literal value.
Class FilterExpression A parsed filterCriteria query: a flat sequence of clauses joined by AND / OR combinators (one fewer combinator than there are clauses).
Class Keyword The reserved keywords of the J.2 query syntax.
Class PathStep A single step in a path expression: either a member-name access or an array index access.
Class StepKind Kind of a single step in a path expression.
Class Token A single token produced by the tokenizer.
Class TokenKind Lexical category of a token produced by the tokenizer.
Exception FilterQuerySyntaxError Raised when a filterCriteria query string cannot be parsed.
Function evaluateClause Evaluates a single clause against rootValue (the parsed CON value).
Function evaluateFilterExpression Evaluates a fully parsed FilterExpression against conValue (the parsed JSON value of a CIN's 'con' attribute), returning True if the CIN should be included in the result set.
Function matchesFilterQuery One-shot convenience function: parses query (a filterCriteria string) and evaluates it against conValue (the already-JSON-parsed value of a CIN's 'con' attribute), returning True if the CIN matches and should be included in the result set.
Function parseFilterQuery Parses a full filterCriteria query string into a FilterExpression object.
Function parsePathExpression Parses a oneM2M-style path expression (e.g. "$.sensor.values[2]" or "$.'a.b'[0]") into a list of PathStep objects.
Function resolvePathValue Resolves a parsed path expression against rootValue (the CIN's already-JSON-parsed 'con' attribute). Returns the resolved value, or the _ABSENT sentinel if any step cannot be applied.
Function tokenizeFilterQuery Tokenizes a full filterCriteria query string of the form: pathExpr KEYWORD literal (AND|OR pathExpr KEYWORD literal)*`
Constant COMBINATOR_KEYWORDS The set of keywords that are used to combine clauses in a filterCriteria query.
Constant COMBINATOR_PRECEDENCE_AND_BINDS_TIGHTER If True, AND binds more tightly than OR in filterCriteria evaluation.
Constant COMPARISON_KEYWORDS The set of keywords that are used for comparison in a single clause of a filterCriteria query.
Function _isNumber Returns True if the value is an int or float (but not bool), False otherwise.
Function _parseLiteralValue Parses a literal's raw text into a Python value.
Function _splitOnSpaces Splits the raw query string into whitespace-delimited components, while keeping quoted spans intact.
Function _unquote Strips a single layer of surrounding single quotes, if present.
Constant _ABSENT Internal sentinel. A path did not resolve to any value.
Constant _SPECIAL_CHARS The set of characters that force quoting of a name or literal per the spec rule.
def evaluateClause(clause: Clause, rootValue: Any) -> bool: (source)

Evaluates a single clause against rootValue (the parsed CON value).

Parameters
clause:ClauseThe Clause object to evaluate.
rootValue:AnyThe root value to resolve the path against (the parsed 'con' attribute of a <contentInstance>).
Returns
boolTrue if the clause matches, False otherwise.
Raises
FilterQuerySyntaxErrorIf the clause's keyword is unknown.
def evaluateFilterExpression(expr: FilterExpression, conValue: Any) -> bool: (source)

Evaluates a fully parsed FilterExpression against conValue (the parsed JSON value of a CIN's 'con' attribute), returning True if the CIN should be included in the result set.

Parameters
expr:FilterExpressionThe FilterExpression object to evaluate.
conValue:AnyThe parsed JSON value of the CIN's 'con' attribute.
Returns
boolTrue if the CIN matches the filter expression, False otherwise.
def matchesFilterQuery(query: str, conValue: Any) -> bool: (source)

One-shot convenience function: parses query (a filterCriteria string) and evaluates it against conValue (the already-JSON-parsed value of a CIN's 'con' attribute), returning True if the CIN matches and should be included in the result set.

For repeated evaluation of the same query against many CIN resources, call parseFilterQuery () once and reuse the FilterExpression with evaluateFilterExpression () to avoid re-parsing on every resource.

Raises:

Parameters
query:strThe raw filterCriteria query string.
conValue:AnyThe parsed JSON value of the CIN's 'con' attribute.
Returns
boolTrue if the CIN matches the filter query, False otherwise.
def parseFilterQuery(query: str) -> FilterExpression: (source)

Parses a full filterCriteria query string into a FilterExpression object.

Parameters
query:strThe raw filterCriteria query string.
Returns
FilterExpressionA FilterExpression object representing the parsed query.
Raises
FilterQuerySyntaxErrorIf the query string is malformed.
def parsePathExpression(pathExpr: str) -> list[PathStep]: (source)

Parses a oneM2M-style path expression (e.g. "$.sensor.values[2]" or "$.'a.b'[0]") into a list of PathStep objects.

Grammar (per the spec's stated rules):

pathExpr := '$' step*
step := '.' name | '[' index ']'
name := plainName | "'" quotedName "'"
  • plainName is read up to the next '.' or '[' (whichever comes first);
  • quotedName runs until the matching closing quote and may contain any character, including '.', '[', ']', '{', '}', and spaces.
Parameters
pathExpr:strThe raw path expression string.
Returns
list[PathStep]A list of PathStep objects representing the steps in the path expression.
Raises
FilterQuerySyntaxErrorIf the path expression is malformed.
def resolvePathValue(rootValue: Any, steps: list[PathStep]) -> Any: (source)

Resolves a parsed path expression against rootValue (the CIN's already-JSON-parsed 'con' attribute). Returns the resolved value, or the _ABSENT sentinel if any step cannot be applied.

Parameters
rootValue:AnyThe root value to resolve the path against (the parsed 'con' attribute of a <contentInstance>).
steps:list[PathStep]The list of PathStep objects representing the parsed path expression.
Returns
Any
The resolved value if the path resolves successfully, or the _ABSENT sentinel if any step
cannot be applied (e.g., missing member, out-of-bounds index, type mismatch).
def tokenizeFilterQuery(query: str) -> list[Token]: (source)

Tokenizes a full filterCriteria query string of the form: pathExpr KEYWORD literal (AND|OR pathExpr KEYWORD literal)*`

into a flat list of Token objects.

Parameters
query:strThe raw filterCriteria query string.
Returns
list[Token]A list of Token objects representing the components of the query string.
COMBINATOR_KEYWORDS = (source)

The set of keywords that are used to combine clauses in a filterCriteria query.

Value
(Keyword.AND, Keyword.OR)
COMBINATOR_PRECEDENCE_AND_BINDS_TIGHTER: bool = (source)

If True, AND binds more tightly than OR in filterCriteria evaluation.

Value
False
COMPARISON_KEYWORDS = (source)

The set of keywords that are used for comparison in a single clause of a filterCriteria query.

Value
(Keyword.EQ,
 Keyword.NE,
 Keyword.GT,
 Keyword.LT,
 Keyword.GE,
 Keyword.LE,
 Keyword.MATCH)
def _isNumber(value: Any) -> bool: (source)

Returns True if the value is an int or float (but not bool), False otherwise.

Parameters
value:AnyThe value to check.
Returns
boolTrue if the value is an int or float (but not bool), False otherwise.
def _parseLiteralValue(rawToken: str) -> Any: (source)

Parses a literal's raw text into a Python value.

Parameters
rawToken:strThe raw token text, possibly surrounded by double quotes.
Returns
The parsed Python valuestr, int, float, or the raw token text if it cannot be parsed.
Raises
FilterQuerySyntaxErrorIf the token is a malformed double-quoted string literal.
def _splitOnSpaces(query: str) -> list[str]: (source)

Splits the raw query string into whitespace-delimited components, while keeping quoted spans intact.

Two independent quote contexts are recognised:

  • single-quoted spans ('...') protect spaces inside path member NAMES;
  • double-quoted spans ("...") protect spaces inside string VALUE literals, using JSON escaping, so a backslash escapes the following character and an escaped '"' does not close the span.

A space outside of any quoted span is a component separator. An unterminated span of either kind raises FilterQuerySyntaxError.

Parameters
query:strThe raw filterCriteria query string.
Returns
list[str]A list of raw components (tokens) of the query string, with quotes preserved.
Raises
FilterQuerySyntaxErrorIf a quoted span is unterminated.
def _unquote(rawToken: str) -> str: (source)

Strips a single layer of surrounding single quotes, if present.

Used for path member NAMES (single-quoted per the spec). String VALUE literals use double quotes and are handled by _parseLiteralValue via json.loads, not here.

Parameters
rawToken:strThe raw token text, possibly surrounded by single quotes.
Returns
strThe token text with surrounding single quotes removed, if they were present.

Internal sentinel. A path did not resolve to any value.

Value
object()
_SPECIAL_CHARS = (source)

The set of characters that force quoting of a name or literal per the spec rule.

Value
set('$. []{}')