Skip to content

feat(cql2-text): parse the A_* array predicates - #177

Open
C1-BA-B1-F3 wants to merge 4 commits into
geopython:mainfrom
C1-BA-B1-F3:fix/cql2-text-array-predicates
Open

C1-BA-B1-F3 wants to merge 4 commits into
geopython:mainfrom
C1-BA-B1-F3:fix/cql2-text-array-predicates

Conversation

@C1-BA-B1-F3

Copy link
Copy Markdown

Fixes #160.

A_CONTAINS(arrayattr, ('a', 'b', 'c')) raised
Unexpected token Token('COMMA', ','): the cql2-text grammar had no array
literal and no A_* predicate, so the call fell through to the generic
func rule (attribute "(" expression ("," expression)* ")") and died on the
comma — a parenthesized list is not an expression.

cql2-json already parses the same filter, and cql2.ARRAY_PREDICATES_MAP
already carries the mapping, so the gap was only in the text parser:

  • add an array rule — a parenthesized list — reachable only from the array
    predicate, so its parentheses cannot be confused with a parenthesized
    expression elsewhere;
  • add the A_EQUALS / A_CONTAINS / A_CONTAINEDBY / A_OVERLAPS
    keywords, case-insensitive like the S_* ones;
  • add a transformer method that reuses ARRAY_PREDICATES_MAP, so both
    parsers produce the same AST.

Tests

8 cases added to tests/parsers/cql2_text/test_parser.py: the issue's exact
reproduction, all four operators, a single-element array, lowercase input, and
an array predicate inside an AND condition.

  • On the pre-fix code all 8 fail; after the fix the module is 56/56.
  • Full suite with the test extra (excluding the modules that need a live
    Elasticsearch/OpenSearch/Solr or GDAL): 324 passed / 62 skipped / 37
    errors
    , against a 316 / 62 / 37 baseline on main — the delta is
    exactly the 8 new tests; the 37 errors are the network-backed backend
    suites and are unrelated.
  • ruff / ruff format clean; mypy reports the same pre-existing
    values.py errors as main.

The grammar's only reported conflict is the pre-existing
Shift/Reduce conflict for terminal RPAR ... <literal : bbox>; the new rules
add none, because the A_* and S_* keyword terminals disambiguate the two
spatial_predicate alternatives before any ( is read.

The unquoted attribute regex /[a-zA-Z][a-zA-Z_:0-9.]+/ required at
least two characters, so expressions like 'a = 1' or 'S_INTERSECTS(g,
BBOX(...))' failed to parse.

Change the trailing .+ to .* so a single-letter identifier is accepted.

Fixes geopython#165
A GeoJSON bbox literal is [minx, miny, maxx, maxy], but
parsers/cql2_json/parser.py unpacked it directly into
values.Envelope(*bbox) whose field order is (x1, x2, y1, y2) ==
(minx, maxx, miny, maxy). The two conventions were conflated, so
longitudes and latitudes were silently swapped.

Reindex explicitly: Envelope(b[0], b[2], b[1], b[3]).

Fixes geopython#163
OGC 21-065r2 defines BETWEEN with a flat 3-element args array
[operand, low, high]. The parser only accepted the draft nested shape
[operand, [low, high]], so a spec-conformant client raised TypeError.

Accept both: nested when args[1] is a list, flat when it is not.

Fixes geopython#162
@C1-BA-B1-F3

Copy link
Copy Markdown
Author

Heads-up for the maintainer: #161 (open since 2026-08-02, "Add CQL2 array predicates, cql-json ne support, and parser validation") also implements the cql2-text A_* predicates for #160, so this PR overlaps with it.

Differences, so you can pick whichever you prefer:

Happy to close this one if you would rather take #161 — just say so.

…NEDBY, A_OVERLAPS)

The cql2_text grammar had no rules for array predicates, so expressions
like A_CONTAINS(values, ('a', 'b', 'c')) raised UnexpectedToken on the
comma. The cql2_json parser already handles them.

Add an array_predicate rule with a parenthesised expression list on the
right-hand side, wire it into the predicate alternatives, and map the
operators through ARRAY_PREDICATES_MAP in the transformer.

Fixes geopython#160
@C1-BA-B1-F3
C1-BA-B1-F3 force-pushed the fix/cql2-text-array-predicates branch from bd23b71 to 3138da8 Compare October 3, 2026 07:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A_CONTAINS with cql2_text throws error

1 participant