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    /// # 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}