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}