Skip to main content

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}