qdrant_client/
payload.rs

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