Skip to content

Row-level intersects() filtering via optional WKB-decoder dependency #414

Description

@gunnarmorling

Follow-up to #173 (geospatial statistics + bbox-only intersects pushdown).

Context

The intersects(column, xmin, ymin, xmax, ymax) predicate added in #173
performs bbox-only pushdown at the row group and page level, then returns
every surviving row to the caller. The reader does not decode the per-row
WKB payload, so:

  • Rows whose individual geometry falls outside the query bbox are still
    emitted (false positives at the row level).
  • FilterPredicate.not(intersects(...)) is rejected at resolve time —
    correct per-row evaluation would require decoding every WKB.

This is a deliberate trade-off: it keeps the runtime dependency-free
(arithmetic on the doubles already in the footer / column index).

Proposal

Add opt-in row-level evaluation that decodes the per-row WKB only when
a geometry library is on the classpath, mirroring how the existing optional
compression codecs (snappy-java, zstd-jni, lz4-java, brotli4j) are wired in
core/pom.xml (<optional>true</optional>).

Likely shape:

  • A small GeometryDecoder SPI inside dev.hardwood.internal.geo with a
    default no-op implementation. The implementation is selected via
    ServiceLoader (or a classpath probe) at reader construction time.
  • A JTS-backed implementation shipped as a separate optional artifact
    (hardwood-geo-jts or core with org.locationtech.jts:jts-core as
    <optional>true</optional>), so users who don't need exact filtering pay
    nothing.
  • RecordFilterEvaluator / RecordFilterCompiler: when the decoder is
    available, replace the unconditional true for GeospatialPredicate
    with a real WKB-decode + envelope/intersection test against the query
    bbox.
  • Resolver: drop the UnsupportedOperationException for
    not(intersects(...)) once row-level evaluation is wired up. The
    chunk-level dual (containment instead of overlap) can then be added in
    RowGroupFilterEvaluator / PageFilterEvaluator for completeness, but
    is independent of this change.

Out of scope

  • Adding new spatial predicates (within, contains, distance).
  • 3D / Z-M filtering — initial predicate stays 2D.
  • Custom CRS reprojection — query and column CRS are assumed to match.

Acceptance criteria

  • With jts-core on the test classpath, an intersects query returns
    only rows whose decoded geometry actually intersects the query bbox
    (verified by an end-to-end test that builds row groups straddling the
    query box).
  • Without any geometry library on the classpath, behavior is unchanged
    from Support geospatial statistics and bounding box metadata #173 (bbox-only pushdown, every surviving row returned, negation
    still rejected).
  • No mandatory new runtime dependency for core.
  • Docs (docs/content/usage.md) updated to describe the opt-in.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions