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 {}