Skip to content

feat(sql): add within_box, within_radius, and geo_within_radius_latlon functions#6664

Merged
bluestreak01 merged 9 commits intomasterfrom
vi_geo
Jan 20, 2026
Merged

feat(sql): add within_box, within_radius, and geo_within_radius_latlon functions#6664
bluestreak01 merged 9 commits intomasterfrom
vi_geo

Conversation

@bluestreak01
Copy link
Copy Markdown
Member

@bluestreak01 bluestreak01 commented Jan 19, 2026

Summary

Adds four new SQL functions optimized for spatial filtering, particularly useful for indexing lidar scans and local area queries.

Naming convention: Functions operating on Cartesian/Euclidean coordinates use simple names (within_*), while functions operating on geographic lat/lon coordinates use the geo_ prefix.

within_box(x, y, min_x, min_y, max_x, max_y)

Returns true if point (x, y) is within the bounding box defined by (min_x, min_y) to (max_x, max_y), inclusive. Uses Cartesian coordinates.

Example:

-- Filter points within a rectangular area
SELECT * FROM lidar_points 
WHERE within_box(x, y, 0.0, 0.0, 100.0, 100.0);

-- Use in SELECT clause
SELECT x, y, within_box(x, y, 0.0, 0.0, 50.0, 50.0) as in_zone 
FROM points;

within_radius(x, y, center_x, center_y, radius)

Returns true if point (x, y) is within radius distance of (center_x, center_y) using Cartesian/Euclidean distance, inclusive.

Example:

-- Filter points within 50 units of origin
SELECT * FROM scan_points 
WHERE within_radius(x, y, 0.0, 0.0, 50.0);

-- Dynamic center from another column
SELECT * FROM measurements 
WHERE within_radius(x, y, sensor_x, sensor_y, detection_range);

geo_within_radius_latlon(lat, lon, center_lat, center_lon, radius_meters)

Returns true if point (lat, lon) is within radius_meters of (center_lat, center_lon). Uses equirectangular projection for fast local distance calculations. Lat/lon in degrees, radius in meters.

Accuracy: ~0.1% for distances < 100km. For lidar scans (typically < 1km), error is negligible.

Example:

-- Filter GPS points within 100m of a location (Times Square)
SELECT * FROM gps_readings 
WHERE geo_within_radius_latlon(lat, lon, 40.758, -73.9855, 100.0);

-- Lidar scan filtering
SELECT * FROM lidar_points 
WHERE geo_within_radius_latlon(lat, lon, scanner_lat, scanner_lon, 500.0);

geo_distance_meters(lat1, lon1, lat2, lon2)

Returns distance in meters between two lat/lon points using equirectangular projection. Optimized for small distances (lidar/local area scans).

Accuracy: ~0.1% for distances < 100km. For lidar scans (typically < 1km), error is negligible.

Example:

-- Calculate distance from a reference point
SELECT lat, lon, geo_distance_meters(40.758, -73.9855, lat, lon) as distance_m
FROM gps_readings;

-- Filter and order by distance
SELECT * FROM points 
WHERE geo_distance_meters(40.0, -73.0, lat, lon) < 1000.0
ORDER BY geo_distance_meters(40.0, -73.0, lat, lon);

-- Find the 10 closest points
SELECT * FROM points 
ORDER BY geo_distance_meters(40.0, -73.0, lat, lon) 
LIMIT 10;

Implementation Details

  • Branchless bit manipulation for optimal vectorized execution in boolean functions (no sqrt, no branches)
  • Constant folding optimization: When reference point/center/radius are constants, expensive operations (like Math.cos() for lat/lon) are precomputed at query compile time
  • Parallel execution support: All functions work with QuestDB's async filter workers
  • NaN handling: Boolean functions return false, distance function returns null
  • Null handling: Boolean functions return false, distance function returns null
  • Boundary inclusive: Points exactly on the boundary return true

Coordinate Validation (lat/lon functions)

The lat/lon functions validate geographic coordinates and throw errors with correct SQL positions:

Coordinate Valid Range Error Type
Latitude -90 to 90 SqlException (compile-time) or CairoException (runtime)
Longitude -180 to 180 SqlException (compile-time) or CairoException (runtime)

Compile-time validation (constant values):

-- Throws SqlException at position of invalid argument
SELECT geo_within_radius_latlon(40.0, -73.0, 91.0, -73.0, 100.0);
-- Error: [45] latitude must be between -90 and 90 [value=91.0]

SELECT geo_distance_meters(91.0, -73.0, 40.0, -73.0);
-- Error: [27] latitude must be between -90 and 90 [value=91.0]

Runtime validation (column values):

-- Throws CairoException when invalid value is encountered
SELECT geo_within_radius_latlon(lat, lon, 40.0, -73.0, 100.0) FROM points;
-- Error: [42] latitude must be between -90 and 90 [value=91.0]

Edge Cases

Condition Boolean Functions Distance Function
Any input is NaN false null
Any input is NULL false null
Negative radius false N/A
Inverted box (min > max) false N/A
Point on boundary true N/A
Zero radius (point at center) true N/A
Same point N/A 0.0
Invalid latitude (outside -90 to 90) Error Error
Invalid longitude (outside -180 to 180) Error Error

Test Plan

  • Unit tests for all edge cases (NaN, null, boundaries, negative values)
  • Parallel execution tests with large datasets (1M rows)
  • Correctness verification against manual bounds/distance checks
  • Query plan verification for constant optimization
  • Real-world coordinate tests (NYC landmarks for lat/lon)
  • Latitude/longitude validation tests (compile-time and runtime errors)
  • Boundary latitude/longitude tests (±90, ±180)
  • ORDER BY distance tests

🤖 Generated with Claude Code

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 19, 2026

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Adds four new geospatial function factories (distance, within-box, within-radius, within-radius-latlon) with optimized paths for constant inputs, comprehensive input validation, NaN handling, and extensive test suites. Minor refactoring of a switch statement to switch expression in an existing factory.

Changes

Cohort / File(s) Summary
Syntax Refactoring
core/src/main/java/io/questdb/griffin/engine/functions/eq/EqDoubleFunctionFactory.java
Switch statement converted to switch expression in dispatchUnaryFunc; functional behavior unchanged
New Geo Spatial Functions
core/src/main/java/io/questdb/griffin/engine/functions/geo/GeoDistanceMetersFunctionFactory.java, GeoWithinBoxFunctionFactory.java, GeoWithinRadiusFunctionFactory.java, GeoWithinRadiusLatLonFunctionFactory.java
Four new function factories implementing geospatial operations with optimized constant-path variants, input validation for coordinate bounds, NaN handling via bitwise operations, and internal function classes for specialized evaluation
Geo Distance Tests
core/src/test/java/io/questdb/test/griffin/engine/functions/geo/GeoDistanceMetersFunctionFactoryTest.java
Comprehensive test coverage for distance calculations, including boundary conditions, parallel execution, joins, and integration scenarios
Geo Box Tests
core/src/test/java/io/questdb/test/griffin/engine/functions/geo/GeoWithinBoxFunctionFactoryTest.java
Tests for bounding-box containment, NaN propagation, inverted boxes, null handling, parallel filters, and join scenarios with zones
Geo Radius Tests
core/src/test/java/io/questdb/test/griffin/engine/functions/geo/GeoWithinRadiusFunctionFactoryTest.java
Tests for Cartesian radius-based filtering, constant/dynamic center and radius paths, edge cases, parallel execution, and multi-table joins
Geo Radius LatLon Tests
core/src/test/java/io/questdb/test/griffin/engine/functions/geo/GeoWithinRadiusLatLonFunctionFactoryTest.java
Tests for equirectangular-projection radius filtering with latitude/longitude validation, boundary precision, dynamic parameters, real-world scenarios, and error cases

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~120 minutes

Suggested labels

New feature, SQL

Suggested reviewers

  • puzpuzpuz
  • kafka1991
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main additions: four new SQL functions for spatial filtering and distance calculations, with accurate naming conventions.
Description check ✅ Passed The description is directly related to the changeset, providing comprehensive details about the four new functions, their signatures, use cases, implementation details, and test coverage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@bluestreak01
Copy link
Copy Markdown
Member Author

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 19, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@core/src/main/java/io/questdb/griffin/engine/functions/bool/GeoWithinRadiusFunctionFactory.java`:
- Around line 227-381: GeoWithinRadiusFunction is missing an isEquivalentTo
override so equivalence checks fall back to identity; add an override in
GeoWithinRadiusFunction that first returns true for this==other, then checks
other instanceof GeoWithinRadiusFunction and compares child functions using
xFunc.isEquivalentTo(that.xFunc), yFunc.isEquivalentTo(that.yFunc),
centerXFunc.isEquivalentTo(that.centerXFunc),
centerYFunc.isEquivalentTo(that.centerYFunc), and
radiusFunc.isEquivalentTo(that.radiusFunc) (matching the pattern used in
GeoWithinBoxFunction).

bluestreak01 and others added 4 commits January 19, 2026 02:13
… radius, fix javadoc

- Store original radius in const-optimized functions instead of computing Math.sqrt(radiusSq)
- Add latitude validation (-90 to 90) and longitude validation (-180 to 180)
- Use SqlException for compile-time validation of constant values
- Use CairoException.nonCritical().position() for runtime validation
- Escape < characters in javadoc comments

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…geo package

- Add geo_distance_meters(lat1, lon1, lat2, lon2) function that returns
  distance in meters between two lat/lon points using equirectangular
  projection. Optimized for small distances with ~0.1% error for < 100km.
- Move all geo spatial functions from bool package to geo package
- Add isEquivalentTo() implementations to all geo function classes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests geo functions with dynamic parameters from joined tables:
- geo_within_box: join points with zones table by box boundaries
- geo_within_radius: join detections with sensors by center/radius
- geo_within_radius_latlon: join customers with stores by delivery radius
- geo_distance_meters: join on distance < threshold, distance matrix

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@bluestreak01
Copy link
Copy Markdown
Member Author

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 19, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@bluestreak01 bluestreak01 added New feature Feature requests SQL Issues or changes relating to SQL execution labels Jan 19, 2026
@bluestreak01 bluestreak01 changed the title feat(sql): add geo_within_box, geo_within_radius, and geo_within_radius_latlon functions feat(sql): add within_box, within_radius, and geo_within_radius_latlon functions Jan 19, 2026
@glasstiger
Copy link
Copy Markdown
Contributor

[PR Coverage check]

😍 pass : 481 / 717 (67.09%)

file detail

path covered line new line coverage
🔵 io/questdb/griffin/engine/functions/geo/WithinRadiusFunctionFactory.java 102 162 62.96%
🔵 io/questdb/griffin/engine/functions/geo/WithinBoxFunctionFactory.java 120 187 64.17%
🔵 io/questdb/griffin/engine/functions/geo/GeoDistanceMetersFunctionFactory.java 107 154 69.48%
🔵 io/questdb/griffin/engine/functions/geo/GeoWithinRadiusLatLonFunctionFactory.java 145 207 70.05%
🔵 io/questdb/griffin/engine/functions/eq/EqDoubleFunctionFactory.java 7 7 100.00%

@bluestreak01
Copy link
Copy Markdown
Member Author

@CodeRabbit did you find any bugs?

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 20, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch all review comments made by coderabbitai on PR `#6664`
gh pr view 6664 --json reviews --jq '.reviews[] | select(.author.login == "coderabbitai" or .author.login == "coderabbitai[bot]") | {state: .state, body: .body}'

# Also fetch review thread comments (inline comments)
gh api repos/questdb/questdb/pulls/6664/comments --jq '.[] | select(.user.login == "coderabbitai" or .user.login == "coderabbitai[bot]") | {path: .path, line: .line, body: .body}' 2>/dev/null || echo "No inline comments found or API call failed"

Length of output: 6161


@bluestreak01, I found one issue during my review, which has already been addressed:

Missing isEquivalentTo override (Minor) - The GeoWithinRadiusFunction class was missing an isEquivalentTo override, which meant equivalence checks would fall back to identity comparison rather than properly comparing child functions. This was flagged in WithinRadiusFunctionFactory.java around line 415.

Status: This was addressed in your subsequent commits (2b4deb5 to 5e91c22).


Overall assessment: No critical bugs were identified. The implementation demonstrates:

  • Thorough NaN/null handling
  • Proper validation with appropriate error positioning
  • Correct lifecycle management (close, init, cursorClosed)
  • Good optimization strategies (constant folding, precomputation)
  • Comprehensive test coverage including edge cases, parallel execution, and joins

The code quality is solid, and the one minor issue I flagged has been resolved. 🎉

🐰✨

@bluestreak01 bluestreak01 merged commit 7301c6e into master Jan 20, 2026
43 checks passed
@bluestreak01 bluestreak01 deleted the vi_geo branch January 20, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

New feature Feature requests SQL Issues or changes relating to SQL execution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants