diesel/expression_methods/json_expression_methods.rs
1use crate::expression::grouped::Grouped;
2use crate::expression::operators::RetrieveAsTextJson;
3use crate::expression::Expression;
4use crate::sql_types::SqlType;
5
6/// PostgreSQL specific methods present on JSON and JSONB expressions.
7#[cfg(any(feature = "postgres_backend", feature = "sqlite"))]
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::IntoSql;
131 use crate::sql_types::{Integer, Json, Jsonb, Nullable, Text};
132 use crate::Expression;
133
134 pub trait Sealed {}
135
136 #[diagnostic::on_unimplemented(
137 message = "`{Self}` is neither `diesel::sql_types::Text` nor `diesel::sql_types::Integer`",
138 note = "try to provide an expression that produces one of the expected sql types"
139 )]
140 pub trait TextOrInteger {}
141 impl TextOrInteger for Text {}
142 impl TextOrInteger for Integer {}
143
144 /// Marker trait used to implement `PgAnyJsonExpressionMethods` on the appropriate types.
145 #[diagnostic::on_unimplemented(
146 message = "`{Self}` is neither `diesel::sql_types::Json`, `diesel::sql_types::Jsonb`, `diesel::sql_types::Nullable<Json>` nor `diesel::sql_types::Nullable<Jsonb>`",
147 note = "try to provide an expression that produces one of the expected sql types"
148 )]
149 pub trait JsonOrNullableJsonOrJsonbOrNullableJsonb {}
150 impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Json {}
151 impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Nullable<Json> {}
152 impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Jsonb {}
153 impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Nullable<Jsonb> {}
154
155 impl Sealed for &'_ str {}
156 impl Sealed for String {}
157 impl Sealed for i32 {}
158 impl<T> Sealed for T
159 where
160 T: Expression,
161 T::SqlType: TextOrInteger,
162 {
163 }
164
165 impl<'a> JsonIndex for &'a str {
166 type Expression = crate::dsl::AsExprOf<&'a str, crate::sql_types::Text>;
167
168 fn into_json_index_expression(self) -> Self::Expression {
169 self.into_sql::<Text>()
170 }
171 }
172
173 impl JsonIndex for String {
174 type Expression = crate::dsl::AsExprOf<String, crate::sql_types::Text>;
175
176 fn into_json_index_expression(self) -> Self::Expression {
177 self.into_sql::<Text>()
178 }
179 }
180
181 impl JsonIndex for i32 {
182 type Expression = crate::dsl::AsExprOf<i32, crate::sql_types::Int4>;
183
184 fn into_json_index_expression(self) -> Self::Expression {
185 self.into_sql::<crate::sql_types::Int4>()
186 }
187 }
188
189 impl<T> JsonIndex for T
190 where
191 T: Expression,
192 T::SqlType: TextOrInteger,
193 {
194 type Expression = Self;
195
196 fn into_json_index_expression(self) -> Self::Expression {
197 self
198 }
199 }
200}