qdrant_client/grpc_conversions/
extensions.rs1use std::fmt::{Display, Formatter};
2use std::hash::{Hash, Hasher};
3
4#[cfg(feature = "uuid")]
5use uuid::Uuid;
6
7use crate::client::Payload;
8#[allow(deprecated)]
9use crate::error::NotA;
10use crate::prelude::{PointStruct, Value};
11#[cfg(feature = "uuid")]
12use crate::qdrant::point_id::PointIdOptions;
13use crate::qdrant::value::Kind;
14use crate::qdrant::{
15 HardwareUsage, InferenceUsage, ListValue, ModelUsage, PointId, RetrievedPoint, ScoredPoint,
16 Struct, Usage, Vectors,
17};
18
19static NULL_VALUE: Value = Value {
21 kind: Some(Kind::NullValue(0)),
22};
23
24impl PointStruct {
25 pub fn new(
26 id: impl Into<PointId>,
27 vectors: impl Into<Vectors>,
28 payload: impl Into<Payload>,
29 ) -> Self {
30 Self {
31 id: Some(id.into()),
32 payload: payload.into().into(),
33 vectors: Some(vectors.into()),
34 }
35 }
36}
37
38impl RetrievedPoint {
39 pub fn get(&self, key: &str) -> &Value {
50 self.try_get(key).unwrap_or(&NULL_VALUE)
51 }
52
53 pub fn try_get(&self, key: &str) -> Option<&Value> {
64 self.payload.get(key)
65 }
66}
67
68impl ScoredPoint {
69 pub fn get(&self, key: &str) -> &Value {
80 self.try_get(key).unwrap_or(&NULL_VALUE)
81 }
82
83 pub fn try_get(&self, key: &str) -> Option<&Value> {
94 self.payload.get(key)
95 }
96}
97
98macro_rules! extract {
99 ($kind:ident, $check:ident) => {
100 #[doc = stringify!([$kind])]
102 pub fn $check(&self) -> bool {
103 matches!(self.kind, Some($kind(_)))
104 }
105 };
106 ($kind:ident, $check:ident, $extract:ident, $ty:ty) => {
107 extract!($kind, $check);
108
109 #[doc = stringify!([$ty])]
111 #[doc = stringify!([$kind].)]
114 pub fn $extract(&self) -> Option<$ty> {
115 if let Some($kind(v)) = self.kind {
116 Some(v)
117 } else {
118 None
119 }
120 }
121 };
122 ($kind:ident, $check:ident, $extract:ident, ref $ty:ty) => {
123 extract!($kind, $check);
124
125 #[doc = stringify!([$ty])]
127 #[doc = stringify!([$kind].)]
130 pub fn $extract(&self) -> Option<&$ty> {
131 if let Some($kind(v)) = &self.kind {
132 Some(v)
133 } else {
134 None
135 }
136 }
137 };
138}
139
140mod value_extract_impl {
143 use crate::qdrant::value::Kind::*;
144 use crate::qdrant::{Struct, Value};
145 impl Value {
146 extract!(NullValue, is_null);
147 extract!(BoolValue, is_bool, as_bool, bool);
148 extract!(IntegerValue, is_integer, as_integer, i64);
149 extract!(DoubleValue, is_double, as_double, f64);
150 extract!(StringValue, is_str, as_str, ref String);
151 extract!(ListValue, is_list, as_list, ref [Value]);
152 extract!(StructValue, is_struct, as_struct, ref Struct);
153 }
154}
155
156impl Value {
157 #[cfg(feature = "serde")]
158 pub fn into_json(self) -> serde_json::Value {
178 use serde_json::Value as JsonValue;
179 match self.kind {
180 Some(Kind::BoolValue(b)) => JsonValue::Bool(b),
181 Some(Kind::IntegerValue(i)) => JsonValue::from(i),
182 Some(Kind::DoubleValue(d)) => JsonValue::from(d),
183 Some(Kind::StringValue(s)) => JsonValue::String(s),
184 Some(Kind::ListValue(vs)) => vs.into_iter().map(Value::into_json).collect(),
185 Some(Kind::StructValue(s)) => s
186 .fields
187 .into_iter()
188 .map(|(k, v)| (k, v.into_json()))
189 .collect(),
190 Some(Kind::NullValue(_)) | None => JsonValue::Null,
191 }
192 }
193}
194
195#[cfg(feature = "serde")]
196impl From<Value> for serde_json::Value {
197 fn from(value: Value) -> Self {
198 value.into_json()
199 }
200}
201
202impl Display for Value {
203 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
204 match &self.kind {
205 Some(Kind::BoolValue(b)) => write!(f, "{b}"),
206 Some(Kind::IntegerValue(i)) => write!(f, "{i}"),
207 Some(Kind::DoubleValue(v)) => write!(f, "{v}"),
208 Some(Kind::StringValue(s)) => write!(f, "{s:?}"),
209 Some(Kind::ListValue(vs)) => {
210 let mut i = vs.values.iter();
211 write!(f, "[")?;
212 if let Some(first) = i.next() {
213 write!(f, "{first}")?;
214 for v in i {
215 write!(f, ",{v}")?;
216 }
217 }
218 write!(f, "]")
219 }
220 Some(Kind::StructValue(s)) => {
221 let mut i = s.fields.iter();
222 write!(f, "{{")?;
223 if let Some((key, value)) = i.next() {
224 write!(f, "{key:?}:{value}")?;
225 for (key, value) in i {
226 write!(f, ",{key:?}:{value}")?;
227 }
228 }
229 write!(f, "}}")
230 }
231 _ => write!(f, "null"),
232 }
233 }
234}
235
236impl Value {
237 pub fn try_list_iter(&self) -> Option<impl Iterator<Item = &Value>> {
241 if let Some(Kind::ListValue(values)) = &self.kind {
242 Some(values.iter())
243 } else {
244 None
245 }
246 }
247
248 #[deprecated(since = "1.10.0", note = "use `try_list_iter` instead")]
250 #[allow(deprecated)]
251 pub fn iter_list(&self) -> Result<impl Iterator<Item = &Value>, NotA<ListValue>> {
252 if let Some(Kind::ListValue(values)) = &self.kind {
253 Ok(values.iter())
254 } else {
255 Err(NotA::default())
256 }
257 }
258
259 pub fn get_value(&self, key: &str) -> Option<&Value> {
263 if let Some(Kind::StructValue(Struct { fields })) = &self.kind {
264 Some(fields.get(key)?)
265 } else {
266 None
267 }
268 }
269
270 #[deprecated(since = "1.10.0", note = "use `get_value` instead")]
272 #[allow(deprecated)]
273 pub fn get_struct(&self, key: &str) -> Result<&Value, NotA<Struct>> {
274 if let Some(Kind::StructValue(Struct { fields })) = &self.kind {
275 Ok(fields.get(key).unwrap_or(&NULL_VALUE))
276 } else {
277 Err(NotA::default())
278 }
279 }
280}
281
282impl std::ops::Deref for ListValue {
283 type Target = [Value];
284
285 fn deref(&self) -> &[Value] {
286 &self.values
287 }
288}
289
290impl IntoIterator for ListValue {
291 type Item = Value;
292
293 type IntoIter = std::vec::IntoIter<Value>;
294
295 fn into_iter(self) -> Self::IntoIter {
296 self.values.into_iter()
297 }
298}
299
300impl ListValue {
301 pub fn iter(&self) -> std::slice::Iter<'_, Value> {
302 self.values.iter()
303 }
304}
305
306#[cfg(feature = "uuid")]
307impl From<Uuid> for PointId {
308 fn from(uuid: Uuid) -> Self {
309 Self {
310 point_id_options: Some(PointIdOptions::from(uuid)),
311 }
312 }
313}
314
315#[cfg(feature = "uuid")]
316impl From<Uuid> for PointIdOptions {
317 fn from(uuid: Uuid) -> Self {
318 PointIdOptions::Uuid(uuid.to_string())
319 }
320}
321
322impl Hash for PointId {
323 fn hash<H: Hasher>(&self, state: &mut H) {
324 use crate::qdrant::point_id::PointIdOptions::{Num, Uuid};
325 match &self.point_id_options {
326 Some(Num(u)) => state.write_u64(*u),
327 Some(Uuid(s)) => s.hash(state),
328 None => {}
329 }
330 }
331}
332
333impl Hash for ScoredPoint {
334 fn hash<H: Hasher>(&self, state: &mut H) {
335 self.id.hash(state)
336 }
337}
338
339impl Hash for RetrievedPoint {
340 fn hash<H: Hasher>(&self, state: &mut H) {
341 self.id.hash(state)
342 }
343}
344
345impl Usage {
346 pub(crate) fn aggregate_opts(this: Option<Self>, other: Option<Self>) -> Option<Self> {
347 match (this, other) {
348 (Some(this), Some(other)) => Some(this.aggregate(other)),
349 (Some(this), None) => Some(this),
350 (None, Some(other)) => Some(other),
351 (None, None) => None,
352 }
353 }
354
355 pub(crate) fn aggregate(self, other: Self) -> Self {
356 Self {
357 hardware: HardwareUsage::aggregate_opts(self.hardware, other.hardware),
358 inference: InferenceUsage::aggregate_opts(self.inference, other.inference),
359 }
360 }
361}
362
363impl HardwareUsage {
364 pub(crate) fn aggregate_opts(this: Option<Self>, other: Option<Self>) -> Option<Self> {
365 match (this, other) {
366 (Some(this), Some(other)) => Some(this.aggregate(other)),
367 (Some(this), None) => Some(this),
368 (None, Some(other)) => Some(other),
369 (None, None) => None,
370 }
371 }
372
373 pub(crate) fn aggregate(self, other: Self) -> Self {
374 let Self {
375 cpu,
376 payload_io_read,
377 payload_io_write,
378 payload_index_io_read,
379 payload_index_io_write,
380 vector_io_read,
381 vector_io_write,
382 } = other;
383
384 Self {
385 cpu: self.cpu + cpu,
386 payload_io_read: self.payload_io_read + payload_io_read,
387 payload_io_write: self.payload_io_write + payload_io_write,
388 payload_index_io_read: self.payload_index_io_read + payload_index_io_read,
389 payload_index_io_write: self.payload_index_io_write + payload_index_io_write,
390 vector_io_read: self.vector_io_read + vector_io_read,
391 vector_io_write: self.vector_io_write + vector_io_write,
392 }
393 }
394}
395
396impl InferenceUsage {
397 pub(crate) fn aggregate_opts(this: Option<Self>, other: Option<Self>) -> Option<Self> {
398 match (this, other) {
399 (Some(this), Some(other)) => Some(this.aggregate(other)),
400 (Some(this), None) => Some(this),
401 (None, Some(other)) => Some(other),
402 (None, None) => None,
403 }
404 }
405
406 pub(crate) fn aggregate(self, other: Self) -> Self {
407 let mut models = self.models;
408 for (model_name, other_usage) in other.models {
409 models
410 .entry(model_name)
411 .and_modify(|usage| {
412 *usage = usage.aggregate(other_usage);
413 })
414 .or_insert(other_usage);
415 }
416
417 Self { models }
418 }
419}
420
421impl ModelUsage {
422 pub(crate) fn aggregate(self, other: Self) -> Self {
423 Self {
424 tokens: self.tokens + other.tokens,
425 }
426 }
427}
428
429#[cfg(test)]
430mod tests {
431 use std::collections::HashMap;
432
433 use super::*;
434
435 #[test]
436 fn test_inference_usage_aggregation() {
437 let mut models1 = HashMap::new();
438 models1.insert("model_a".to_string(), ModelUsage { tokens: 100 });
439 models1.insert("model_b".to_string(), ModelUsage { tokens: 200 });
440
441 let mut models2 = HashMap::new();
442 models2.insert("model_a".to_string(), ModelUsage { tokens: 50 });
443 models2.insert("model_c".to_string(), ModelUsage { tokens: 300 });
444
445 let usage1 = InferenceUsage { models: models1 };
446 let usage2 = InferenceUsage { models: models2 };
447
448 let aggregated = usage1.aggregate(usage2);
449
450 assert_eq!(aggregated.models.get("model_a").unwrap().tokens, 150);
452
453 assert_eq!(aggregated.models.get("model_b").unwrap().tokens, 200);
455
456 assert_eq!(aggregated.models.get("model_c").unwrap().tokens, 300);
458
459 assert_eq!(aggregated.models.len(), 3);
461 }
462}