diesel/expression_methods/json_expression_methods.rs
1use crate::expression::Expression;
2use crate::expression::grouped::Grouped;
3use crate::expression::operators::RetrieveAsTextJson;
4use crate::sql_types::SqlType;
5
6/// PostgreSQL specific methods present on JSON and JSONB expressions.
7#[cfg(any(feature = "postgres_backend", feature = "__sqlite-shared"))]
8pub trait AnyJsonExpressionMethods: Expression + Sized {
9 /// Creates a `->>` expression JSON.
10 ///
11 /// This operator extracts the value associated with the given key, that is provided on the
12 /// Right Hand Side of the operator.
13 ///
14 /// Extracts n'th element of JSON array (array elements are indexed from zero, but negative integers count from the end).
15 /// Extracts JSON object field as Text with the given key.
16 /// # Example
17 ///
18 /// ```rust
19 /// # include!("../doctest_setup.rs");
20 /// #
21 /// # table! {
22 /// # contacts {
23 /// # id -> Integer,
24 /// # name -> VarChar,
25 /// # address -> Jsonb,
26 /// # }
27 /// # }
28 /// #
29 /// # fn main() {
30 /// # run_test().unwrap();
31 /// # }
32 ///
33 /// # #[cfg(feature = "serde_json")]
34 /// # fn run_test() -> QueryResult<()> {
35 /// # use self::contacts::dsl::*;
36 /// # let conn = &mut establish_connection();
37 /// # diesel::sql_query("DROP TABLE IF EXISTS contacts").execute(conn).unwrap();
38 /// # diesel::sql_query("CREATE TABLE contacts (
39 /// # id SERIAL PRIMARY KEY,
40 /// # name VARCHAR NOT NULL,
41 /// # address JSONB NOT NULL
42 /// # )").execute(conn)
43 /// # .unwrap();
44 /// #
45 /// let santas_address: serde_json::Value = serde_json::json!({
46 /// "street": "Article Circle Expressway 1",
47 /// "city": "North Pole",
48 /// "postcode": "99705",
49 /// "state": "Alaska"
50 /// });
51 /// diesel::insert_into(contacts)
52 /// .values((name.eq("Claus"), address.eq(&santas_address)))
53 /// .execute(conn)?;
54 ///
55 /// let santas_postcode = contacts.select(address.retrieve_as_text("postcode")).get_result::<String>(conn)?;
56 /// assert_eq!(santas_postcode, "99705");
57 ///
58 ///
59 /// let robert_downey_jr_addresses: serde_json::Value = serde_json::json!([
60 /// {
61 /// "street": "Somewhere In La 251",
62 /// "city": "Los Angeles",
63 /// "postcode": "12231223",
64 /// "state": "California"
65 /// },
66 /// {
67 /// "street": "Somewhere In Ny 251",
68 /// "city": "New York",
69 /// "postcode": "3213212",
70 /// "state": "New York"
71 /// }
72 /// ]);
73 ///
74 /// diesel::insert_into(contacts)
75 /// .values((name.eq("Robert Downey Jr."), address.eq(&robert_downey_jr_addresses)))
76 /// .execute(conn)?;
77 ///
78 /// let roberts_second_address_in_db = contacts
79 /// .filter(name.eq("Robert Downey Jr."))
80 /// .select(address.retrieve_as_text(1))
81 /// .get_result::<String>(conn)?;
82 ///
83 /// let roberts_second_address = serde_json::json!{{
84 /// "city": "New York",
85 /// "state": "New York",
86 /// "street": "Somewhere In Ny 251",
87 /// "postcode": "3213212"
88 /// }};
89 /// assert_eq!(roberts_second_address, serde_json::from_str::<serde_json::Value>(&roberts_second_address_in_db).unwrap());
90 /// # Ok(())
91 /// # }
92 /// # #[cfg(not(feature = "serde_json"))]
93 /// # fn run_test() -> QueryResult<()> {
94 /// # Ok(())
95 /// # }
96 /// ```
97 fn retrieve_as_text<T>(
98 self,
99 other: T,
100 ) -> crate::expression::helper_types::RetrieveAsText<Self, T>
101 where
102 T: JsonIndex,
103 <T::Expression as Expression>::SqlType: SqlType,
104 {
105 Grouped(RetrieveAsTextJson::new(
106 self,
107 other.into_json_index_expression(),
108 ))
109 }
110}
111
112/// A marker trait indicating which types can be used as index into a json field
113pub trait JsonIndex: self::private::Sealed {
114 #[doc(hidden)]
115 type Expression: Expression;
116
117 #[doc(hidden)]
118 fn into_json_index_expression(self) -> Self::Expression;
119}
120
121impl<T> AnyJsonExpressionMethods for T
122where
123 T: Expression,
124 T::SqlType: private::JsonOrNullableJsonOrJsonbOrNullableJsonb,
125{
126}
127
128pub(crate) mod private {
129 use super::JsonIndex;
130 use crate::Expression;
131 use crate::expression::IntoSql;
132 use crate::sql_types::{Integer, Json, Jsonb, Nullable, Text};
133 use alloc::string::String;
134
135 pub trait Sealed {}
136
137 #[diagnostic::on_unimplemented(
138 message = "`{Self}` is neither `diesel::sql_types::Text` nor `diesel::sql_types::Integer`",
139 note = "try to provide an expression that produces one of the expected sql types"
140 )]
141 pub trait TextOrInteger {}
142 impl TextOrInteger for Text {}
143 impl TextOrInteger for Integer {}
144
145 /// Marker trait used to implement `PgAnyJsonExpressionMethods` on the appropriate types.
146 #[diagnostic::on_unimplemented(
147 message = "`{Self}` is neither `diesel::sql_types::Json`, `diesel::sql_types::Jsonb`, `diesel::sql_types::Nullable<Json>` nor `diesel::sql_types::Nullable<Jsonb>`",
148 note = "try to provide an expression that produces one of the expected sql types"
149 )]
150 pub trait JsonOrNullableJsonOrJsonbOrNullableJsonb {}
151 impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Json {}
152 impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Nullable<Json> {}
153 impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Jsonb {}
154 impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Nullable<Jsonb> {}
155
156 impl Sealed for &'_ str {}
157 impl Sealed for String {}
158 impl Sealed for i32 {}
159 impl<T> Sealed for T
160 where
161 T: Expression,
162 T::SqlType: TextOrInteger,
163 {
164 }
165
166 impl<'a> JsonIndex for &'a str {
167 type Expression = crate::dsl::AsExprOf<&'a str, crate::sql_types::Text>;
168
169 fn into_json_index_expression(self) -> Self::Expression {
170 self.into_sql::<Text>()
171 }
172 }
173
174 impl JsonIndex for String {
175 type Expression = crate::dsl::AsExprOf<String, crate::sql_types::Text>;
176
177 fn into_json_index_expression(self) -> Self::Expression {
178 self.into_sql::<Text>()
179 }
180 }
181
182 impl JsonIndex for i32 {
183 type Expression = crate::dsl::AsExprOf<i32, crate::sql_types::Int4>;
184
185 fn into_json_index_expression(self) -> Self::Expression {
186 self.into_sql::<crate::sql_types::Int4>()
187 }
188 }
189
190 impl<T> JsonIndex for T
191 where
192 T: Expression,
193 T::SqlType: TextOrInteger,
194 {
195 type Expression = Self;
196
197 fn into_json_index_expression(self) -> Self::Expression {
198 self
199 }
200 }
201}