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}