Skip to main content

cratestack_sql/filter/
field_ref_ext.rs

1use super::expr::FilterExpr;
2use super::field_ref::FieldRef;
3use super::json::{JsonFilter, JsonTextPath};
4use super::spatial::{SpatialFilter, SpatialPoint};
5
6impl<M, T> FieldRef<M, T> {
7    /// PG: `col ? 'key'` — the JSON document contains `key` as a
8    /// top-level field. SQLite (no native `?` operator): lowers to
9    /// `json_extract(col, '$.key') IS NOT NULL`.
10    ///
11    /// Intended for `jsonb` / JSON columns. Using this on a non-JSON
12    /// column compiles fine but errors at the engine layer when the
13    /// SQL runs — Rust's type system doesn't gate this for you.
14    ///
15    /// The key is taken as `impl Into<String>` so callers can pass
16    /// either a `&'static str` literal or a runtime-owned `String`
17    /// (e.g. user-driven analytics queries that pivot on a metric
18    /// name from the request).
19    pub fn json_has_key(self, key: impl Into<String>) -> FilterExpr {
20        FilterExpr::Json(JsonFilter::HasKey {
21            column: self.column,
22            key: key.into(),
23        })
24    }
25
26    /// PG: `col ->> 'key' <op> $1` — extract the value at `key` as
27    /// text, then compare. SQLite: `json_extract(col, '$.key') <op>
28    /// $1`. Returns a [`JsonTextPath`] that supports the standard
29    /// comparison ops via chained methods. See [`Self::json_has_key`]
30    /// for the key-ownership rationale.
31    pub fn json_get_text(self, key: impl Into<String>) -> JsonTextPath {
32        JsonTextPath::new(self.column, key.into())
33    }
34
35    /// PG-only: `ST_Covers(col::geography, point::geography)` — the
36    /// column's geography contains `point` (including boundary).
37    /// Use for "is this caller-supplied point inside the row's
38    /// service area" filters on `geography(Polygon, 4326)` columns.
39    ///
40    /// The embedded rusqlite backend doesn't ship SpatiaLite, so
41    /// this filter fails loud at the render layer there. Document at
42    /// the schema level whether a model supports the embedded
43    /// backend at all before using spatial ops on it.
44    pub fn covers_geography(self, point: SpatialPoint) -> FilterExpr {
45        FilterExpr::Spatial(SpatialFilter::CoversGeographyPoint {
46            column: self.column,
47            lng: point.lng,
48            lat: point.lat,
49        })
50    }
51
52    /// PG-only: `ST_DWithin(col::geography, point::geography,
53    /// radius_meters)` — the column's geography is within
54    /// `radius_meters` of the given point (great-circle distance,
55    /// since `::geography` triggers the spheroid path).
56    pub fn dwithin_geography(self, point: SpatialPoint, radius_meters: f64) -> FilterExpr {
57        FilterExpr::Spatial(SpatialFilter::DWithinGeographyPoint {
58            column: self.column,
59            lng: point.lng,
60            lat: point.lat,
61            radius_meters,
62        })
63    }
64}