Skip to main content

drizzle_postgres/expr/
json_ops.rs

1//! `PostgreSQL` JSON/JSONB operators.
2//!
3//! Provides type-safe access to `PostgreSQL` JSON operators:
4//! - `->` (get JSON object field by key, returns JSON)
5//! - `->>` (get JSON object field by key, returns text)
6//! - `#>` (get JSON object at path, returns JSON)
7//! - `#>>` (get JSON object at path, returns text)
8//! - `@>` (JSON contains)
9//! - `?` (JSON key exists)
10
11#[cfg(not(feature = "std"))]
12use crate::prelude::*;
13use crate::values::PostgresValue;
14use drizzle_core::ToSQL;
15use drizzle_core::expr::{Expr, NonNull, Null, SQLExpr, Scalar};
16use drizzle_core::sql::{SQL, SQLChunk};
17use drizzle_types::postgres::types::{Boolean, Json, Text};
18
19/// `PostgreSQL` `->` operator - get JSON object field by key, returns JSON.
20///
21/// # Example
22///
23/// ```
24/// # use drizzle_postgres::expr::json_get;
25/// # use drizzle_core::{SQL, ToSQL};
26/// # use drizzle_postgres::values::PostgresValue;
27/// let data = SQL::<PostgresValue>::raw("data");
28/// let field = json_get(data, "name");
29/// assert!(field.to_sql().sql().contains("->"));
30/// ```
31pub fn json_get<'a, E>(expr: E, key: &'a str) -> SQLExpr<'a, PostgresValue<'a>, Json, Null, Scalar>
32where
33    E: Expr<'a, PostgresValue<'a>>,
34{
35    SQLExpr::new(
36        expr.to_sql()
37            .push(SQLChunk::Raw("->".into()))
38            .append(SQL::param(PostgresValue::Text(key.into()))),
39    )
40}
41
42/// `PostgreSQL` `->` operator with integer index - get JSON array element.
43///
44/// # Example
45///
46/// ```
47/// # use drizzle_postgres::expr::json_get_idx;
48/// # use drizzle_core::{SQL, ToSQL};
49/// # use drizzle_postgres::values::PostgresValue;
50/// let data = SQL::<PostgresValue>::raw("data");
51/// let elem = json_get_idx(data, 0);
52/// assert!(elem.to_sql().sql().contains("->"));
53/// ```
54pub fn json_get_idx<'a, E>(
55    expr: E,
56    index: i32,
57) -> SQLExpr<'a, PostgresValue<'a>, Json, Null, Scalar>
58where
59    E: Expr<'a, PostgresValue<'a>>,
60{
61    SQLExpr::new(
62        expr.to_sql()
63            .push(SQLChunk::Raw("->".into()))
64            .append(SQL::param(PostgresValue::Integer(index))),
65    )
66}
67
68/// `PostgreSQL` `->>` operator - get JSON object field as text.
69///
70/// # Example
71///
72/// ```
73/// # use drizzle_postgres::expr::json_get_text;
74/// # use drizzle_core::{SQL, ToSQL};
75/// # use drizzle_postgres::values::PostgresValue;
76/// let data = SQL::<PostgresValue>::raw("data");
77/// let name = json_get_text(data, "name");
78/// assert!(name.to_sql().sql().contains("->>"));
79/// ```
80pub fn json_get_text<'a, E>(
81    expr: E,
82    key: &'a str,
83) -> SQLExpr<'a, PostgresValue<'a>, Text, Null, Scalar>
84where
85    E: Expr<'a, PostgresValue<'a>>,
86{
87    SQLExpr::new(
88        expr.to_sql()
89            .push(SQLChunk::Raw("->>".into()))
90            .append(SQL::param(PostgresValue::Text(key.into()))),
91    )
92}
93
94/// `PostgreSQL` `->>` operator with integer index - get JSON array element as text.
95///
96/// # Example
97///
98/// ```
99/// # use drizzle_postgres::expr::json_get_text_idx;
100/// # use drizzle_core::{SQL, ToSQL};
101/// # use drizzle_postgres::values::PostgresValue;
102/// let data = SQL::<PostgresValue>::raw("data");
103/// let elem = json_get_text_idx(data, 0);
104/// assert!(elem.to_sql().sql().contains("->>"));
105/// ```
106pub fn json_get_text_idx<'a, E>(
107    expr: E,
108    index: i32,
109) -> SQLExpr<'a, PostgresValue<'a>, Text, Null, Scalar>
110where
111    E: Expr<'a, PostgresValue<'a>>,
112{
113    SQLExpr::new(
114        expr.to_sql()
115            .push(SQLChunk::Raw("->>".into()))
116            .append(SQL::param(PostgresValue::Integer(index))),
117    )
118}
119
120/// `PostgreSQL` `#>` operator - get JSON object at specified path, returns JSON.
121///
122/// # Example
123///
124/// ```
125/// # use drizzle_postgres::expr::json_get_path;
126/// # use drizzle_core::{SQL, ToSQL};
127/// # use drizzle_postgres::values::PostgresValue;
128/// let data = SQL::<PostgresValue>::raw("data");
129/// let nested = json_get_path(data, "{a,b}");
130/// assert!(nested.to_sql().sql().contains("#>"));
131/// ```
132pub fn json_get_path<'a, E>(
133    expr: E,
134    path: &'a str,
135) -> SQLExpr<'a, PostgresValue<'a>, Json, Null, Scalar>
136where
137    E: Expr<'a, PostgresValue<'a>>,
138{
139    SQLExpr::new(
140        expr.to_sql()
141            .push(SQLChunk::Raw("#>".into()))
142            .append(SQL::param(PostgresValue::Text(path.into()))),
143    )
144}
145
146/// `PostgreSQL` `#>>` operator - get JSON object at specified path as text.
147///
148/// # Example
149///
150/// ```
151/// # use drizzle_postgres::expr::json_get_path_text;
152/// # use drizzle_core::{SQL, ToSQL};
153/// # use drizzle_postgres::values::PostgresValue;
154/// let data = SQL::<PostgresValue>::raw("data");
155/// let nested = json_get_path_text(data, "{a,b}");
156/// assert!(nested.to_sql().sql().contains("#>>"));
157/// ```
158pub fn json_get_path_text<'a, E>(
159    expr: E,
160    path: &'a str,
161) -> SQLExpr<'a, PostgresValue<'a>, Text, Null, Scalar>
162where
163    E: Expr<'a, PostgresValue<'a>>,
164{
165    SQLExpr::new(
166        expr.to_sql()
167            .push(SQLChunk::Raw("#>>".into()))
168            .append(SQL::param(PostgresValue::Text(path.into()))),
169    )
170}
171
172/// `PostgreSQL` `@>` operator for JSONB - left JSON contains right JSON.
173///
174/// # Example
175///
176/// ```
177/// # use drizzle_postgres::expr::jsonb_contains;
178/// # use drizzle_core::{SQL, ToSQL};
179/// # use drizzle_postgres::values::PostgresValue;
180/// let data = SQL::<PostgresValue>::raw("data");
181/// let cond = jsonb_contains(data, r#"{"key": "value"}"#);
182/// assert!(cond.to_sql().sql().contains("@>"));
183/// ```
184pub fn jsonb_contains<'a, L, R>(
185    left: L,
186    right: R,
187) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
188where
189    L: Expr<'a, PostgresValue<'a>>,
190    R: ToSQL<'a, PostgresValue<'a>>,
191{
192    SQLExpr::new(
193        left.to_sql()
194            .push(SQLChunk::Raw("@>".into()))
195            .append(right.to_sql()),
196    )
197}
198
199/// `PostgreSQL` `<@` operator for JSONB - left JSON is contained by right JSON.
200///
201/// # Example
202///
203/// ```
204/// # use drizzle_postgres::expr::jsonb_contained;
205/// # use drizzle_core::{SQL, ToSQL};
206/// # use drizzle_postgres::values::PostgresValue;
207/// let data = SQL::<PostgresValue>::raw("data");
208/// let cond = jsonb_contained(data, r#"{"key": "value", "other": 1}"#);
209/// assert!(cond.to_sql().sql().contains("<@"));
210/// ```
211pub fn jsonb_contained<'a, L, R>(
212    left: L,
213    right: R,
214) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
215where
216    L: Expr<'a, PostgresValue<'a>>,
217    R: ToSQL<'a, PostgresValue<'a>>,
218{
219    SQLExpr::new(
220        left.to_sql()
221            .push(SQLChunk::Raw("<@".into()))
222            .append(right.to_sql()),
223    )
224}
225
226/// `PostgreSQL` `?` operator for JSONB - does the key exist in the JSON object?
227///
228/// # Example
229///
230/// ```
231/// # use drizzle_postgres::expr::jsonb_exists_key;
232/// # use drizzle_core::{SQL, ToSQL};
233/// # use drizzle_postgres::values::PostgresValue;
234/// let data = SQL::<PostgresValue>::raw("data");
235/// let cond = jsonb_exists_key(data, "name");
236/// assert!(cond.to_sql().sql().contains("?"));
237/// ```
238pub fn jsonb_exists_key<'a, E>(
239    expr: E,
240    key: &'a str,
241) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
242where
243    E: Expr<'a, PostgresValue<'a>>,
244{
245    SQLExpr::new(
246        expr.to_sql()
247            .push(SQLChunk::Raw("?".into()))
248            .append(SQL::param(PostgresValue::Text(key.into()))),
249    )
250}
251
252/// `PostgreSQL` `?|` operator for JSONB - do any of the keys exist?
253///
254/// # Example
255///
256/// ```
257/// # use drizzle_postgres::expr::jsonb_exists_any;
258/// # use drizzle_core::{SQL, ToSQL};
259/// # use drizzle_postgres::values::PostgresValue;
260/// let data = SQL::<PostgresValue>::raw("data");
261/// let cond = jsonb_exists_any(data, &["name", "email"]);
262/// assert!(cond.to_sql().sql().contains("?|"));
263/// ```
264pub fn jsonb_exists_any<'a, E>(
265    expr: E,
266    keys: &[&'a str],
267) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
268where
269    E: Expr<'a, PostgresValue<'a>>,
270{
271    let arr: Vec<PostgresValue<'a>> = keys
272        .iter()
273        .map(|k| PostgresValue::Text((*k).into()))
274        .collect();
275    SQLExpr::new(
276        expr.to_sql()
277            .push(SQLChunk::Raw("?|".into()))
278            .append(SQL::param(PostgresValue::Array(arr))),
279    )
280}
281
282/// `PostgreSQL` `?&` operator for JSONB - do all of the keys exist?
283///
284/// # Example
285///
286/// ```
287/// # use drizzle_postgres::expr::jsonb_exists_all;
288/// # use drizzle_core::{SQL, ToSQL};
289/// # use drizzle_postgres::values::PostgresValue;
290/// let data = SQL::<PostgresValue>::raw("data");
291/// let cond = jsonb_exists_all(data, &["name", "email"]);
292/// assert!(cond.to_sql().sql().contains("?&"));
293/// ```
294pub fn jsonb_exists_all<'a, E>(
295    expr: E,
296    keys: &[&'a str],
297) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
298where
299    E: Expr<'a, PostgresValue<'a>>,
300{
301    let arr: Vec<PostgresValue<'a>> = keys
302        .iter()
303        .map(|k| PostgresValue::Text((*k).into()))
304        .collect();
305    SQLExpr::new(
306        expr.to_sql()
307            .push(SQLChunk::Raw("?&".into()))
308            .append(SQL::param(PostgresValue::Array(arr))),
309    )
310}
311
312/// Extension trait providing method-based JSON operators for `PostgreSQL` expressions.
313pub trait JsonExprExt<'a>: Expr<'a, PostgresValue<'a>> + Sized {
314    /// Get JSON object field by key (`->` operator), returns JSON.
315    fn json_get(self, key: &'a str) -> SQLExpr<'a, PostgresValue<'a>, Json, Null, Scalar> {
316        json_get(self, key)
317    }
318
319    /// Get JSON array element by index (`->` operator), returns JSON.
320    fn json_get_idx(self, index: i32) -> SQLExpr<'a, PostgresValue<'a>, Json, Null, Scalar> {
321        json_get_idx(self, index)
322    }
323
324    /// Get JSON object field as text (`->>` operator).
325    fn json_get_text(self, key: &'a str) -> SQLExpr<'a, PostgresValue<'a>, Text, Null, Scalar> {
326        json_get_text(self, key)
327    }
328
329    /// Get JSON array element as text (`->>` operator).
330    fn json_get_text_idx(self, index: i32) -> SQLExpr<'a, PostgresValue<'a>, Text, Null, Scalar> {
331        json_get_text_idx(self, index)
332    }
333
334    /// Get JSON object at path (`#>` operator), returns JSON.
335    fn json_get_path(self, path: &'a str) -> SQLExpr<'a, PostgresValue<'a>, Json, Null, Scalar> {
336        json_get_path(self, path)
337    }
338
339    /// Get JSON object at path as text (`#>>` operator).
340    fn json_get_path_text(
341        self,
342        path: &'a str,
343    ) -> SQLExpr<'a, PostgresValue<'a>, Text, Null, Scalar> {
344        json_get_path_text(self, path)
345    }
346
347    /// JSONB contains (`@>` operator).
348    fn jsonb_contains<R>(self, other: R) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
349    where
350        R: ToSQL<'a, PostgresValue<'a>>,
351    {
352        jsonb_contains(self, other)
353    }
354
355    /// JSONB is contained by (`<@` operator).
356    fn jsonb_contained<R>(
357        self,
358        other: R,
359    ) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
360    where
361        R: ToSQL<'a, PostgresValue<'a>>,
362    {
363        jsonb_contained(self, other)
364    }
365
366    /// JSONB key exists (`?` operator).
367    fn jsonb_exists_key(
368        self,
369        key: &'a str,
370    ) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar> {
371        jsonb_exists_key(self, key)
372    }
373}
374
375/// Blanket implementation for all `PostgreSQL` `Expr` types.
376impl<'a, E: Expr<'a, PostgresValue<'a>>> JsonExprExt<'a> for E {}