Skip to content

feat(search): geo predicates in KQL (distance, bbox, polygon) - #3212

Draft
dschmidt wants to merge 5 commits into
tmp/refactor-search-mappingfrom
feat/search-geo-kql
Draft

feat(search): geo predicates in KQL (distance, bbox, polygon)#3212
dschmidt wants to merge 5 commits into
tmp/refactor-search-mappingfrom
feat/search-geo-kql

Conversation

@dschmidt

@dschmidt dschmidt commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

This is a non standard extension to KQL. If you want an ADR first, let me know...

Adds geo predicates to KQL, usable on any geopoint field wherever KQL is accepted (graph search, webdav search REPORT, ...):

  • location:geo.distance(lat, lon, 5km) (radius units km/m/mi)
  • location:geo.bbox(minLat, minLon, maxLat, maxLon)
  • location:geo.polygon(lat lon, lat lon, ...)

They compose in the boolean tree, e.g. mediatype:image AND location:geo.distance(48.2, 16.3, 5km). No proto, graph-endpoint or mapping change: the location_geopoint index sibling already comes from #2659, and the filter rides in the query string.

Stacked on #2659. Three commits: KQL grammar/AST/parse, bleve compiler, opensearch transpiler.

No sort-by-distance: KQL only filters, that would need a structured request param.

dschmidt added 2 commits July 31, 2026 01:10
Extends the KQL grammar with geo function values on any field, e.g.
location:geo.distance(lat, lon, 5km). Parses into GeoDistance/GeoBoundingBox/
GeoPolygon AST nodes; radius units (km/m/mi) are normalised to meters.
Translates the geo AST nodes to bleve GeoDistance/GeoBoundingBox/
GeoBoundingPolygon queries over the indexed <field>_geopoint sibling.
@codacy-production

codacy-production Bot commented Jul 30, 2026

Copy link
Copy Markdown

Not up to standards ⛔

🟢 Coverage 73.45% diff coverage · +0.29% coverage variation

Metric Results
Coverage variation +0.29% coverage variation (-1.00%)
Diff coverage 73.45% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (1b697e4) 82737 19353 23.39%
Head commit (8149819) 83205 (+468) 19706 (+353) 23.68% (+0.29%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#3212) 516 379 73.45%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Adds geo_distance/geo_bounding_box query builders plus a geo_shape polygon
query (relation intersects, the modern replacement for the deprecated
geo_polygon, still valid against geo_point). Wires the geo AST nodes through
the KQL transpiler onto the <field>_geopoint sibling.
// geoField maps a KQL geo key to its indexed geopoint sibling, e.g.
// "location" to "location_geopoint".
func geoField(key string) string {
return strings.ToLower(key) + mapping.GeopointSuffix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Uh, this is bad. Should do a proper lookup of the fieldname. This currently works, as we only have the location - but not in the generic case

dschmidt added 2 commits July 31, 2026 09:22
…n-geo fields

Replaces the ad-hoc key+"_geopoint" concatenation with a proper lookup
(query.ResolveGeoField) derived from the TypeGeopoint field overrides, so it
works for any geopoint field, not just location. Geo predicates on non-geopoint
fields now error instead of querying a nonexistent field.
…haviour

Sorts the two latitudes in the KQL layer so MinLat <= MaxLat. Only bleve
strictly requires it (it errors on an inverted box; OpenSearch tolerates it),
but doing it centrally keeps behaviour consistent across backends and the AST
easy to reason about. Longitude order is preserved so an antimeridian-crossing
box (minLon > maxLon) still works, which both backends handle identically.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant