mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-12 21:58:58 -04:00
fix(kql): normalise geo.bbox latitude order for backend-consistent behaviour
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.
This commit is contained in:
1 parent
ea47069d7c
commit
e43fe13066
4 files changed
+28
-1
No files matched your search
+3
-1
@@ -101,7 +101,9 @@ type GeoDistanceNode struct {
|
||||
Radius float64
|
||||
}
|
||||
|
||||
// GeoBoundingBoxNode represents a geo.bbox(minLat, minLon, maxLat, maxLon) predicate.
|
||||
// GeoBoundingBoxNode represents a geo.bbox(minLat, minLon, maxLat, maxLon)
|
||||
// predicate. Latitude is normalised so MinLat <= MaxLat; longitude order is
|
||||
// preserved, so MinLon > MaxLon denotes a box crossing the antimeridian.
|
||||
type GeoBoundingBoxNode struct {
|
||||
*Base
|
||||
Key string
|
||||
|
||||
@@ -276,6 +276,14 @@ func buildGeoBoundingBoxNode(k, a any, text []byte, pos position) (*ast.GeoBound
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Latitude does not wrap, so the two values just delimit the box. Only bleve
|
||||
// strictly requires MinLat <= MaxLat (OpenSearch tolerates the inverted
|
||||
// order), but we normalise centrally here for consistency and easier
|
||||
// debugging. Longitude order is kept: minLon > maxLon denotes a box crossing
|
||||
// the antimeridian, which both backends interpret the same way.
|
||||
if minLat > maxLat {
|
||||
minLat, maxLat = maxLat, minLat
|
||||
}
|
||||
return &ast.GeoBoundingBoxNode{Base: b, Key: key, MinLat: minLat, MinLon: minLon, MaxLat: maxLat, MaxLon: maxLon}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,17 @@ var _ = Describe("geo KQL predicates", func() {
|
||||
}))
|
||||
})
|
||||
|
||||
It("normalises inverted latitude order but keeps longitude order", func() {
|
||||
// minLat (48.3) > maxLat (47.9) on input; longitude kept as given.
|
||||
n, err := firstNode("location:geo.bbox(48.3, 16.1, 47.9, 16.5)")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
b := n.(*ast.GeoBoundingBoxNode)
|
||||
Expect(b.MinLat).To(Equal(47.9))
|
||||
Expect(b.MaxLat).To(Equal(48.3))
|
||||
Expect(b.MinLon).To(Equal(16.1))
|
||||
Expect(b.MaxLon).To(Equal(16.5))
|
||||
})
|
||||
|
||||
It("parses geo.polygon into a GeoPolygonNode with its vertices", func() {
|
||||
n, err := firstNode("location:geo.polygon(48.3 16.1, 48.3 16.5, 47.9 16.5)")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
@@ -46,6 +46,12 @@ var _ = Describe("Geo KQL predicates", func() {
|
||||
Expect(hits(near + " AND location:geo.bbox(52.0, 13.0, 53.0, 14.0)")).To(Equal(0))
|
||||
})
|
||||
|
||||
It("matches a bbox given with inverted latitude order", func() {
|
||||
// Latitude args inverted (max, ..., min): normalised centrally so bleve
|
||||
// gets a well-formed box instead of erroring. Same box as the passing case.
|
||||
Expect(hits("location:geo.bbox(50.0, 11.0, 49.0, 12.0)")).To(Equal(1))
|
||||
})
|
||||
|
||||
It("rejects a geo predicate on a non-geopoint field", func() {
|
||||
_, err := qbleve.DefaultCreator.Create("name:geo.distance(48.2, 16.3, 5km)")
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
Reference in new issue
Block a user