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 /// # // Reading the query result requires reading memory allocated by the
30 /// # // native sqlite library, which miri cannot track provenance for.
31 /// # #[cfg(miri)]
32 /// # fn main() {}
33 /// # #[cfg(not(miri))]
34 /// # fn main() {
35 /// # run_test().unwrap();
36 /// # }
37 ///
38 /// # #[cfg(feature = "serde_json")]
39 /// # fn run_test() -> QueryResult<()> {
40 /// # use self::contacts::dsl::*;
41 /// # let conn = &mut establish_connection();
42 /// # diesel::sql_query("DROP TABLE IF EXISTS contacts").execute(conn).unwrap();
43 /// # diesel::sql_query("CREATE TABLE contacts (
44 /// # id SERIAL PRIMARY KEY,
45 /// # name VARCHAR NOT NULL,
46 /// # address JSONB NOT NULL
47 /// # )").execute(conn)
48 /// # .unwrap();
49 /// #
50 /// let santas_address: serde_json::Value = serde_json::json!({
51 /// "street": "Article Circle Expressway 1",
52 /// "city": "North Pole",
53 /// "postcode": "99705",
54 /// "state": "Alaska"
55 /// });
56 /// diesel::insert_into(contacts)
57 /// .values((name.eq("Claus"), address.eq(&santas_address)))
58 /// .execute(conn)?;
59 ///
60 /// let santas_postcode = contacts.select(address.retrieve_as_text("postcode")).get_result::<String>(conn)?;
61 /// assert_eq!(santas_postcode, "99705");
62 ///
63 ///
64 /// let robert_downey_jr_addresses: serde_json::Value = serde_json::json!([
65 /// {
66 /// "street": "Somewhere In La 251",
67 /// "city": "Los Angeles",
68 /// "postcode": "12231223",
69 /// "state": "California"
70 /// },
71 /// {
72 /// "street": "Somewhere In Ny 251",
73 /// "city": "New York",
74 /// "postcode": "3213212",
75 /// "state": "New York"
76 /// }
77 /// ]);
78 ///
79 /// diesel::insert_into(contacts)
80 /// .values((name.eq("Robert Downey Jr."), address.eq(&robert_downey_jr_addresses)))
81 /// .execute(conn)?;
82 ///
83 /// let roberts_second_address_in_db = contacts
84 /// .filter(name.eq("Robert Downey Jr."))
85 /// .select(address.retrieve_as_text(1))
86 /// .get_result::<String>(conn)?;
87 ///
88 /// let roberts_second_address = serde_json::json!{{
89 /// "city": "New York",
90 /// "state": "New York",
91 /// "street": "Somewhere In Ny 251",
92 /// "postcode": "3213212"
93 /// }};
94 /// assert_eq!(roberts_second_address, serde_json::from_str::<serde_json::Value>(&roberts_second_address_in_db).unwrap());
95 /// # Ok(())
96 /// # }
97 /// # #[cfg(not(feature = "serde_json"))]
98 /// # fn run_test() -> QueryResult<()> {
99 /// # Ok(())
100 /// # }
101 /// ```
102 fn retrieve_as_text<T>(
103 self,
104 other: T,
105 ) -> crate::expression::helper_types::RetrieveAsText<Self, T>
106 where
107 T: JsonIndex,
108 <T::Expression as Expression>::SqlType: SqlType,
109 {
110 Grouped(RetrieveAsTextJson::new(
111 self,
112 other.into_json_index_expression(),
113 ))
114 }
115}
116
117/// A marker trait indicating which types can be used as index into a json field
118pub trait JsonIndex: self::private::Sealed {
119 #[doc(hidden)]
120 type Expression: Expression;
121
122 #[doc(hidden)]
123 fn into_json_index_expression(self) -> Self::Expression;
124}
125
126impl<T> AnyJsonExpressionMethods for T
127where
128 T: Expression,
129 T::SqlType: private::JsonOrNullableJsonOrJsonbOrNullableJsonb,
130{
131}
132
133pub(crate) mod private {
134 use super::JsonIndex;
135 use crate::Expression;
136 use crate::expression::IntoSql;
137 use crate::sql_types::{Integer, Json, Jsonb, Nullable, Text};
138 use alloc::string::String;
139
140 pub trait Sealed {}
141
142 #[diagnostic::on_unimplemented(
143 message = "`{Self}` is neither `diesel::sql_types::Text` nor `diesel::sql_types::Integer`",
144 note = "try to provide an expression that produces one of the expected sql types"
145 )]
146 pub trait TextOrInteger {}
147 impl TextOrInteger for Text {}
148 impl TextOrInteger for Integer {}
149
150 /// Marker trait used to implement `PgAnyJsonExpressionMethods` on the appropriate types.
151 #[diagnostic::on_unimplemented(
152 message = "`{Self}` is neither `diesel::sql_types::Json`, `diesel::sql_types::Jsonb`, `diesel::sql_types::Nullable<Json>` nor `diesel::sql_types::Nullable<Jsonb>`",
153 note = "try to provide an expression that produces one of the expected sql types"
154 )]
155 pub trait JsonOrNullableJsonOrJsonbOrNullableJsonb {}
156 impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Json {}
157 impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Nullable<Json> {}
158 impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Jsonb {}
159 impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Nullable<Jsonb> {}
160
161 impl Sealed for &'_ str {}
162 impl Sealed for String {}
163 impl Sealed for i32 {}
164 impl<T> Sealed for T
165 where
166 T: Expression,
167 T::SqlType: TextOrInteger,
168 {
169 }
170
171 impl<'a> JsonIndex for &'a str {
172 type Expression = crate::dsl::AsExprOf<&'a str, crate::sql_types::Text>;
173
174 fn into_json_index_expression(self) -> Self::Expression {
175 self.into_sql::<Text>()
176 }
177 }
178
179 impl JsonIndex for String {
180 type Expression = crate::dsl::AsExprOf<String, crate::sql_types::Text>;
181
182 fn into_json_index_expression(self) -> Self::Expression {
183 self.into_sql::<Text>()
184 }
185 }
186
187 impl JsonIndex for i32 {
188 type Expression = crate::dsl::AsExprOf<i32, crate::sql_types::Int4>;
189
190 fn into_json_index_expression(self) -> Self::Expression {
191 self.into_sql::<crate::sql_types::Int4>()
192 }
193 }
194
195 impl<T> JsonIndex for T
196 where
197 T: Expression,
198 T::SqlType: TextOrInteger,
199 {
200 type Expression = Self;
201
202 fn into_json_index_expression(self) -> Self::Expression {
203 self
204 }
205 }
206}