Your first filter

In this tutorial you will create an in-memory table, parse a CQL2 JSON filter, and execute a query with separately bound values.

This example introduces the DuckDB query workflow used for both attribute and spatial filters. Here you select rows by a string property; in the spatial tutorial, you use the same parser, SQL translator, and execution API to select rows by geometry intersection.

From a checkout of this repository, create a Python 3.11 environment:

# Create and activate an isolated Python environment.
python3.11 -m venv .venv
. .venv/bin/activate
# Install this checkout so the example uses its evaluator implementation.
python -m pip install -e .

Save the following as first_filter.py and run python first_filter.py:

"""Translate a CQL2 attribute filter and execute it against DuckDB rows."""

import duckdb
from pygeofilter import ast
from pygeofilter.parsers.cql2_json import parse
from pygeofilter_duckdb import to_sql_where_params

# Pygeofilter parses the CQL2 JSON expression into an abstract syntax tree.
# This expression means: the property named "name" equals the value "O'Brien".
root = parse({"op": "=", "args": [{"property": "name"}, "O'Brien"]})
assert isinstance(root, ast.Node)

# Pygeofilter-duckdb translates the tree into a DuckDB WHERE predicate.
# The application maps the exposed property "name" to the database column
# "name". The returned SQL contains a ? placeholder; its value stays separate.
predicate, parameters = to_sql_where_params(root, {"name": "name"})

# DuckDB stores and queries the example data in a temporary in-memory database.
with duckdb.connect(":memory:") as connection:
    connection.execute("CREATE TABLE items (id INTEGER, name VARCHAR)")
    connection.executemany(
        "INSERT INTO items VALUES (?, ?)",
        [(1, "O'Brien"), (2, "alpha"), (3, "' OR TRUE --")],
    )
    # Append the generated predicate to an application-controlled query and
    # pass the values separately so DuckDB treats them as data, including quotes.
    rows = connection.execute(
        "SELECT id FROM items WHERE " + predicate + " ORDER BY id",
        parameters,
    ).fetchall()

print(predicate)
print(parameters)
print(rows)
# Only the row whose name equals the requested literal value should match.
assert rows == [(1,)]

Expected output, in order: the generated SQL predicate, its separately bound value list, and the matching row IDs:

("name" = ?)
["O'Brien"]
[(1,)]

Change the filter value to "' OR TRUE --" and the final assertion to assert rows == [(3,)]. Running the script again selects that literal value.

Next, try spatial filtering, or look up the API contract.