qdrant_client/
payload.rs

1use std::collections::HashMap;
2
3#[cfg(feature = "serde")]
4use serde::de::IntoDeserializer;
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8use crate::qdrant::{GeoPoint, Struct, Value};
9#[cfg(feature = "serde")]
10use crate::QdrantError;
11
12/// Point payload
13///
14/// A JSON-like object that can be attached to points. With payloads you can store any kind of
15/// information along with your points. Qdrant provides comprehensive ways to filter on payload
16/// values during vector search.
17///
18/// Payload documentation: <https://qdrant.tech/documentation/concepts/payload/>
19///
20/// # Serde
21///
22/// <small><em>Requires `serde` feature</em></small>
23///
24/// [Serde JSON](serde_json) types can be converted to and from [`Payload`]. Note that a valid
25/// payload must be a JSON object, and not another JSON type.
26///
27/// Convert a JSON [`Value`](serde_json::Value) to and from [`Payload`]:
28///
29/// ```rust
30///# use qdrant_client::Payload;
31/// use serde_json::{Value, json};
32///
33/// let value = json!({
34///     "city": "Berlin",
35/// });
36///
37/// let payload = Payload::try_from(value).expect("not a JSON object");
38/// let value = Value::from(payload);
39/// ```
40///
41/// If the above value is not a JSON object, a [`QdrantError::JsonToPayload`](crate::QdrantError::JsonToPayload) error is returned.
42///
43/// Convert a JSON object ([`Map<String, Value>`](serde_json::Map)) to and from from [`Payload`]:
44///
45/// ```rust
46///# use qdrant_client::Payload;
47/// use serde_json::{Map, Value};
48///
49/// let mut object = Map::new();
50/// object.insert("city".to_string(), "Berlin".into());
51///
52/// let payload = Payload::from(object);
53/// let object = Map::from(payload);
54/// ```
55#[derive(Clone, PartialEq, Debug, Default)]
56#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
57#[cfg_attr(feature = "serde", serde(transparent))]
58pub struct Payload(pub(crate) HashMap<String, Value>);
59
60impl Payload {
61    /// Construct a new empty payload object
62    pub fn new() -> Self {
63        Self(HashMap::new())
64    }
65
66    /// Construct a payload object from the given hash map
67    #[deprecated(since = "1.10.0", note = "use `Payload::from` instead")]
68    pub fn new_from_hashmap(payload: HashMap<String, Value>) -> Self {
69        Self(payload)
70    }
71
72    /// Insert a payload value at the given key, replacing any existing value
73    pub fn insert(&mut self, key: impl ToString, val: impl Into<Value>) {
74        self.0.insert(key.to_string(), val.into());
75    }
76
77    /// Deserializes the payload directly into `T`. This requires `T` to implement `serde::Deserialize`.
78    /// Returns an error if `T` and the payload have a different structure.
79    ///
80    /// ```rust
81    /// use qdrant_client::Payload;
82    /// use serde_json::json;
83    /// use serde::Deserialize;
84    ///
85    /// #[derive(Deserialize)]
86    /// struct MyData {
87    ///     value1: String,
88    ///     value2: u32,
89    /// }
90    ///
91    /// let payload: Payload = json!({
92    ///     "value1": "Qdrant",
93    ///     "value2": 42,
94    /// })
95    /// .try_into()
96    /// .unwrap();
97    ///
98    /// let parsed: MyData = payload.deserialize().unwrap();
99    /// assert_eq!(parsed.value1, "Qdrant");
100    /// assert_eq!(parsed.value2, 42);
101    /// ```
102    #[cfg(feature = "serde")]
103    pub fn deserialize<T: serde::de::DeserializeOwned>(self) -> Result<T, QdrantError> {
104        Ok(T::deserialize(
105            Struct { fields: self.0 }.into_deserializer(),
106        )?)
107    }
108}
109
110impl From<HashMap<String, Value>> for Payload {
111    #[inline]
112    fn from(payload: HashMap<String, Value>) -> Self {
113        Self(payload)
114    }
115}
116
117impl From<HashMap<&str, Value>> for Payload {
118    #[inline]
119    fn from(payload: HashMap<&str, Value>) -> Self {
120        Self(
121            payload
122                .into_iter()
123                .map(|(k, v)| (k.to_string(), v))
124                .collect(),
125        )
126    }
127}
128
129impl From<Payload> for HashMap<String, Value> {
130    #[inline]
131    fn from(payload: Payload) -> Self {
132        payload.0
133    }
134}
135
136#[cfg(feature = "serde")]
137impl From<Payload> for serde_json::Value {
138    #[inline]
139    fn from(value: Payload) -> serde_json::Value {
140        serde_json::Value::Object(value.into())
141    }
142}
143
144#[cfg(feature = "serde")]
145impl From<Payload> for serde_json::Map<String, serde_json::Value> {
146    #[inline]
147    fn from(value: Payload) -> serde_json::Map<String, serde_json::Value> {
148        value
149            .0
150            .into_iter()
151            .map(|(k, v)| (k, v.into()))
152            .collect::<serde_json::Map<String, serde_json::Value>>()
153    }
154}
155
156#[cfg(feature = "serde")]
157impl TryFrom<serde_json::Value> for Payload {
158    type Error = crate::QdrantError;
159
160    /// Convert JSON object into payload
161    ///
162    /// The JSON value must be a valid object. A JSON object of type
163    /// [`Map<String, Value>`](serde_json::Map) can be converted without errors using
164    /// [`Payload::from`].
165    ///
166    /// # Errors
167    ///
168    /// Returns an [`QdrantError::JsonToPayload`](crate::QdrantError::JsonToPayload) error if the
169    /// value is not an object.
170    #[inline]
171    fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
172        if let serde_json::Value::Object(object) = value {
173            Ok(object.into())
174        } else {
175            Err(crate::QdrantError::JsonToPayload(value))
176        }
177    }
178}
179
180#[cfg(feature = "serde")]
181impl From<serde_json::Map<String, serde_json::Value>> for Payload {
182    /// Convert JSON object into payload
183    ///
184    /// If you have a JSON object as generic value of type [`Value`](serde_json::Value), you can
185    /// convert it with [`Payload::try_from`].
186    #[inline]
187    fn from(object: serde_json::Map<String, serde_json::Value>) -> Self {
188        Payload::from(
189            object
190                .into_iter()
191                .map(|(k, v)| (k, v.into()))
192                .collect::<HashMap<String, Value>>(),
193        )
194    }
195}
196
197#[cfg(feature = "serde")]
198impl From<HashMap<String, serde_json::Value>> for Payload {
199    fn from(value: HashMap<String, serde_json::Value>) -> Self {
200        Payload::from(
201            value
202                .into_iter()
203                .map(|(k, v)| (k, v.into()))
204                .collect::<HashMap<String, Value>>(),
205        )
206    }
207}
208
209impl<K, const N: usize> From<[(K, Value); N]> for Payload
210where
211    K: Into<String>,
212{
213    fn from(values: [(K, Value); N]) -> Self {
214        let mut map = HashMap::with_capacity(N);
215        for (k, v) in values {
216            map.insert(k.into(), v);
217        }
218        Self(map)
219    }
220}
221
222impl From<GeoPoint> for Value {
223    fn from(point: GeoPoint) -> Self {
224        use crate::qdrant::value::Kind;
225
226        let map = HashMap::from([
227            ("lat".to_string(), point.lat.into()),
228            ("lon".to_string(), point.lon.into()),
229        ]);
230
231        Self {
232            kind: Some(Kind::StructValue(Struct { fields: map })),
233        }
234    }
235}
236
237#[cfg(feature = "serde")]
238#[cfg(test)]
239mod tests {
240    use serde_json::json;
241
242    use super::*;
243    use crate::client::Payload;
244
245    #[test]
246    fn json_payload_round_trip() {
247        let payload: Payload = vec![
248            ("some_string", "Bar".into()),
249            ("some_bool", true.into()),
250            ("some_int", 12.into()),
251            ("some_float", 2.3.into()),
252            ("some_seq", vec!["elem1", "elem2"].into()),
253            ("some_obj", vec![("key", "value")].into()),
254        ]
255        .into_iter()
256        .collect::<HashMap<_, Value>>()
257        .into();
258
259        // payload -> Json string
260        let json_value = serde_json::to_string(&payload).unwrap();
261
262        // Json string -> payload
263        let payload_back: Payload = serde_json::from_str(&json_value).unwrap();
264
265        // assert round trip
266        assert_eq!(payload, payload_back);
267    }
268
269    #[test]
270    fn payload_from_string() {
271        let json = r#"{
272            "some_string": "Bar",
273            "some_bool": true,
274            "some_int": 12,
275            "some_float": 2.3,
276            "some_seq": ["elem1", "elem2"],
277            "some_obj": {"key": "value"}
278            }"#;
279
280        // String -> payload
281        let parsed_payload: Payload = serde_json::from_str(json).unwrap();
282
283        let expected: Payload = vec![
284            ("some_string", "Bar".into()),
285            ("some_bool", true.into()),
286            ("some_int", 12.into()),
287            ("some_float", 2.3.into()),
288            ("some_seq", vec!["elem1", "elem2"].into()),
289            ("some_obj", vec![("key", "value")].into()),
290        ]
291        .into_iter()
292        .collect::<HashMap<_, Value>>()
293        .into();
294
295        // assert expected
296        assert_eq!(parsed_payload, expected);
297    }
298
299    #[test]
300    fn test_json_macro() {
301        let json_value = json!({
302            "some_string": "Bar",
303            "some_bool": true,
304            "some_int": 12,
305            "some_float": 2.3,
306            "some_seq": ["elem1", "elem2"],
307            "some_obj": {"key": "value"}
308        });
309
310        let payload: Payload = Payload::try_from(json_value).unwrap();
311
312        eprintln!("payload = {payload:#?}");
313    }
314}