Skip to main content

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 new empty payload object with at least the specified capacity.
67    pub fn with_capacity(capacity: usize) -> Self {
68        Self(HashMap::with_capacity(capacity))
69    }
70
71    /// Insert a payload value at the given key, replacing any existing value
72    pub fn insert(&mut self, key: impl ToString, val: impl Into<Value>) {
73        self.0.insert(key.to_string(), val.into());
74    }
75
76    /// Deserializes the payload directly into `T`. This requires `T` to implement `serde::Deserialize`.
77    /// Returns an error if `T` and the payload have a different structure.
78    ///
79    /// ```rust
80    /// use qdrant_client::Payload;
81    /// use serde_json::json;
82    /// use serde::Deserialize;
83    ///
84    /// #[derive(Deserialize)]
85    /// struct MyData {
86    ///     value1: String,
87    ///     value2: u32,
88    /// }
89    ///
90    /// let payload: Payload = json!({
91    ///     "value1": "Qdrant",
92    ///     "value2": 42,
93    /// })
94    /// .try_into()
95    /// .unwrap();
96    ///
97    /// let parsed: MyData = payload.deserialize().unwrap();
98    /// assert_eq!(parsed.value1, "Qdrant");
99    /// assert_eq!(parsed.value2, 42);
100    /// ```
101    #[cfg(feature = "serde")]
102    pub fn deserialize<T: serde::de::DeserializeOwned>(self) -> Result<T, QdrantError> {
103        Ok(T::deserialize(
104            Struct { fields: self.0 }.into_deserializer(),
105        )?)
106    }
107}
108
109impl From<HashMap<String, Value>> for Payload {
110    #[inline]
111    fn from(payload: HashMap<String, Value>) -> Self {
112        Self(payload)
113    }
114}
115
116impl From<HashMap<&str, Value>> for Payload {
117    #[inline]
118    fn from(payload: HashMap<&str, Value>) -> Self {
119        Self(
120            payload
121                .into_iter()
122                .map(|(k, v)| (k.to_string(), v))
123                .collect(),
124        )
125    }
126}
127
128impl From<Payload> for HashMap<String, Value> {
129    #[inline]
130    fn from(payload: Payload) -> Self {
131        payload.0
132    }
133}
134
135#[cfg(feature = "serde")]
136impl From<Payload> for serde_json::Value {
137    #[inline]
138    fn from(value: Payload) -> serde_json::Value {
139        serde_json::Value::Object(value.into())
140    }
141}
142
143#[cfg(feature = "serde")]
144impl From<Payload> for serde_json::Map<String, serde_json::Value> {
145    #[inline]
146    fn from(value: Payload) -> serde_json::Map<String, serde_json::Value> {
147        value
148            .0
149            .into_iter()
150            .map(|(k, v)| (k, v.into()))
151            .collect::<serde_json::Map<String, serde_json::Value>>()
152    }
153}
154
155#[cfg(feature = "serde")]
156impl TryFrom<serde_json::Value> for Payload {
157    type Error = crate::QdrantError;
158
159    /// Convert JSON object into payload
160    ///
161    /// The JSON value must be a valid object. A JSON object of type
162    /// [`Map<String, Value>`](serde_json::Map) can be converted without errors using
163    /// [`Payload::from`].
164    ///
165    /// # Errors
166    ///
167    /// Returns an [`QdrantError::JsonToPayload`](crate::QdrantError::JsonToPayload) error if the
168    /// value is not an object.
169    #[inline]
170    fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
171        if let serde_json::Value::Object(object) = value {
172            Ok(object.into())
173        } else {
174            Err(crate::QdrantError::JsonToPayload(value))
175        }
176    }
177}
178
179#[cfg(feature = "serde")]
180impl From<serde_json::Map<String, serde_json::Value>> for Payload {
181    /// Convert JSON object into payload
182    ///
183    /// If you have a JSON object as generic value of type [`Value`](serde_json::Value), you can
184    /// convert it with [`Payload::try_from`].
185    #[inline]
186    fn from(object: serde_json::Map<String, serde_json::Value>) -> Self {
187        Payload::from(
188            object
189                .into_iter()
190                .map(|(k, v)| (k, v.into()))
191                .collect::<HashMap<String, Value>>(),
192        )
193    }
194}
195
196#[cfg(feature = "serde")]
197impl From<HashMap<String, serde_json::Value>> for Payload {
198    fn from(value: HashMap<String, serde_json::Value>) -> Self {
199        Payload::from(
200            value
201                .into_iter()
202                .map(|(k, v)| (k, v.into()))
203                .collect::<HashMap<String, Value>>(),
204        )
205    }
206}
207
208impl<K, const N: usize> From<[(K, Value); N]> for Payload
209where
210    K: Into<String>,
211{
212    fn from(values: [(K, Value); N]) -> Self {
213        let mut map = HashMap::with_capacity(N);
214        for (k, v) in values {
215            map.insert(k.into(), v);
216        }
217        Self(map)
218    }
219}
220
221impl From<GeoPoint> for Value {
222    fn from(point: GeoPoint) -> Self {
223        use crate::qdrant::value::Kind;
224
225        let map = HashMap::from([
226            ("lat".to_string(), point.lat.into()),
227            ("lon".to_string(), point.lon.into()),
228        ]);
229
230        Self {
231            kind: Some(Kind::StructValue(Struct { fields: map })),
232        }
233    }
234}
235
236#[cfg(feature = "serde")]
237#[cfg(test)]
238mod tests {
239    use serde_json::json;
240
241    use super::{Payload, *};
242
243    #[test]
244    fn json_payload_round_trip() {
245        let payload: Payload = vec![
246            ("some_string", "Bar".into()),
247            ("some_bool", true.into()),
248            ("some_int", 12.into()),
249            ("some_float", 2.3.into()),
250            ("some_seq", vec!["elem1", "elem2"].into()),
251            ("some_obj", vec![("key", "value")].into()),
252        ]
253        .into_iter()
254        .collect::<HashMap<_, Value>>()
255        .into();
256
257        // payload -> Json string
258        let json_value = serde_json::to_string(&payload).unwrap();
259
260        // Json string -> payload
261        let payload_back: Payload = serde_json::from_str(&json_value).unwrap();
262
263        // assert round trip
264        assert_eq!(payload, payload_back);
265    }
266
267    #[test]
268    fn payload_from_string() {
269        let json = r#"{
270            "some_string": "Bar",
271            "some_bool": true,
272            "some_int": 12,
273            "some_float": 2.3,
274            "some_seq": ["elem1", "elem2"],
275            "some_obj": {"key": "value"}
276            }"#;
277
278        // String -> payload
279        let parsed_payload: Payload = serde_json::from_str(json).unwrap();
280
281        let expected: Payload = vec![
282            ("some_string", "Bar".into()),
283            ("some_bool", true.into()),
284            ("some_int", 12.into()),
285            ("some_float", 2.3.into()),
286            ("some_seq", vec!["elem1", "elem2"].into()),
287            ("some_obj", vec![("key", "value")].into()),
288        ]
289        .into_iter()
290        .collect::<HashMap<_, Value>>()
291        .into();
292
293        // assert expected
294        assert_eq!(parsed_payload, expected);
295    }
296
297    #[test]
298    fn test_json_macro() {
299        let json_value = json!({
300            "some_string": "Bar",
301            "some_bool": true,
302            "some_int": 12,
303            "some_float": 2.3,
304            "some_seq": ["elem1", "elem2"],
305            "some_obj": {"key": "value"}
306        });
307
308        let payload: Payload = Payload::try_from(json_value).unwrap();
309
310        eprintln!("payload = {payload:#?}");
311    }
312}