1use std::collections::BTreeMap;
5
6use serde::{Deserialize, Serialize};
7use serde_json::{json, Map, Value};
8
9use crate::client::Client;
10use crate::error::{Error, Result};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
14#[serde(rename_all = "lowercase")]
15pub enum VectorMetric {
16 #[default]
18 Cosine,
19 Dot,
21 L2,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
33#[serde(rename_all = "lowercase")]
34pub enum VectorQuantization {
35 #[default]
37 None,
38 Int8,
41}
42
43#[derive(Debug, Clone, PartialEq, Deserialize)]
45pub struct VectorItem {
46 pub id: String,
48 #[serde(default, deserialize_with = "crate::serde_null::or_default")]
50 pub vector: Vec<f32>,
51 #[serde(default, deserialize_with = "crate::serde_null::or_default")]
53 pub metadata: Map<String, Value>,
54}
55
56#[derive(Debug, Clone, PartialEq, Deserialize)]
63pub struct VectorHit {
64 pub id: String,
66 #[serde(default)]
68 pub score: f32,
69 #[serde(default, deserialize_with = "crate::serde_null::or_default")]
71 pub metadata: Map<String, Value>,
72 #[serde(default)]
74 pub vector: Option<Vec<f32>>,
75 #[serde(default)]
77 pub reason: Option<String>,
78}
79
80#[derive(Debug, Clone, PartialEq, Deserialize)]
82pub struct VectorSearchResult {
83 #[serde(default, deserialize_with = "crate::serde_null::or_default")]
85 pub results: Vec<VectorHit>,
86 #[serde(default)]
89 pub index: String,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
94pub struct VectorCollectionSummary {
95 pub name: String,
97 #[serde(default)]
99 pub dimension: usize,
100 #[serde(default)]
102 pub metric: VectorMetric,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
107pub struct VectorCollectionInfo {
108 pub collection: String,
110 #[serde(default)]
112 pub dimension: usize,
113 #[serde(default)]
115 pub metric: VectorMetric,
116 #[serde(default)]
118 pub count: i64,
119 #[serde(default)]
121 pub quantization: VectorQuantization,
122}
123
124#[derive(Debug, Clone, PartialEq, Deserialize)]
126pub struct VectorPage {
127 pub collection: String,
129 #[serde(default)]
131 pub count: usize,
132 #[serde(default, deserialize_with = "crate::serde_null::or_default")]
134 pub vectors: Vec<VectorItem>,
135 #[serde(default)]
137 pub truncated: bool,
138 #[serde(default)]
140 pub total: i64,
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
146#[serde(rename_all = "snake_case")]
147pub enum ScoreMetric {
148 Cosine,
150 Dot,
152 L2,
154 L1,
156 Linf,
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
162#[serde(rename_all = "snake_case")]
163pub enum FilterStrategy {
164 #[default]
166 Auto,
167 Graph,
169 Exact,
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
175#[serde(rename_all = "snake_case")]
176pub enum AnnIndex {
177 Hnsw,
179 Ivf,
181 IvfPq,
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
187#[serde(rename_all = "snake_case")]
188pub enum FusionMethod {
189 #[default]
191 Rrf,
192 Max,
194 Mean,
196}
197
198#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
200pub struct SparseVector {
201 #[serde(default)]
203 pub indices: Vec<u32>,
204 #[serde(default)]
206 pub values: Vec<f32>,
207}
208
209#[derive(Debug, Clone, Default, PartialEq)]
214pub struct VectorSearchOptions {
215 pub filter: Option<Value>,
217 pub min_score: Option<f32>,
219 pub include_vectors: bool,
221 pub offset: Option<usize>,
223 pub include_metadata: Option<bool>,
226 pub max_distance: Option<f32>,
228 pub ef_search: Option<usize>,
230 pub exact: bool,
232 pub score_metric: Option<ScoreMetric>,
234 pub filter_strategy: Option<FilterStrategy>,
236 pub using: Option<String>,
238 pub tenant: Option<String>,
240 pub namespace: Option<String>,
242 pub sparse: Option<SparseVector>,
244 pub hybrid_alpha: Option<f32>,
246 pub explain: bool,
248 pub score_expr: Option<String>,
250 pub explain_text: Option<String>,
252 pub ann: Option<AnnIndex>,
254 pub nprobe: Option<usize>,
256}
257
258impl VectorSearchOptions {
259 fn write(&self, body: &mut Map<String, Value>) {
260 fn put(body: &mut Map<String, Value>, key: &str, value: Option<impl Serialize>) {
261 if let Some(value) = value {
262 body.insert(key.to_string(), json!(value));
263 }
264 }
265 put(body, "filter", self.filter.as_ref());
266 put(body, "min_score", self.min_score);
267 put(
268 body,
269 "include_vectors",
270 self.include_vectors.then_some(true),
271 );
272 put(body, "offset", self.offset);
273 put(body, "include_metadata", self.include_metadata);
274 put(body, "max_distance", self.max_distance);
275 put(body, "ef_search", self.ef_search);
276 put(body, "exact", self.exact.then_some(true));
277 put(body, "score_metric", self.score_metric);
278 put(body, "filter_strategy", self.filter_strategy);
279 put(body, "using", self.using.as_ref());
280 put(body, "tenant", self.tenant.as_ref());
281 put(body, "namespace", self.namespace.as_ref());
282 put(body, "sparse", self.sparse.as_ref());
283 put(body, "hybrid_alpha", self.hybrid_alpha);
284 put(body, "explain", self.explain.then_some(true));
285 put(body, "score_expr", self.score_expr.as_ref());
286 put(body, "explain_text", self.explain_text.as_ref());
287 put(body, "ann", self.ann);
288 put(body, "nprobe", self.nprobe);
289 }
290}
291
292#[derive(Debug, Clone, PartialEq, Serialize)]
295#[serde(untagged)]
296pub enum VectorExample {
297 Id(String),
299 Vector(Vec<f32>),
301}
302
303impl VectorExample {
304 pub fn id(id: &str) -> Self {
306 VectorExample::Id(id.to_string())
307 }
308}
309
310#[derive(Debug, Clone, PartialEq, Serialize)]
312pub struct VectorUpsert {
313 pub id: String,
315 pub vector: Vec<f32>,
317 #[serde(skip_serializing_if = "Option::is_none")]
319 pub metadata: Option<Map<String, Value>>,
320}
321
322impl VectorUpsert {
323 pub fn new(id: &str, vector: impl Into<Vec<f32>>) -> Self {
325 Self {
326 id: id.to_string(),
327 vector: vector.into(),
328 metadata: None,
329 }
330 }
331
332 pub fn metadata(mut self, metadata: Map<String, Value>) -> Self {
334 self.metadata = Some(metadata);
335 self
336 }
337}
338
339#[derive(Debug, Clone, PartialEq, Serialize)]
341pub struct TextUpsert {
342 pub id: String,
344 pub text: String,
346 #[serde(skip_serializing_if = "Option::is_none")]
348 pub metadata: Option<Map<String, Value>>,
349}
350
351impl TextUpsert {
352 pub fn new(id: &str, text: &str) -> Self {
354 Self {
355 id: id.to_string(),
356 text: text.to_string(),
357 metadata: None,
358 }
359 }
360
361 pub fn metadata(mut self, metadata: Map<String, Value>) -> Self {
363 self.metadata = Some(metadata);
364 self
365 }
366}
367
368#[derive(Debug, Clone, PartialEq, Deserialize)]
370pub struct Embedding {
371 #[serde(default)]
374 pub embedder: String,
375 #[serde(default)]
377 pub dimension: usize,
378 #[serde(default)]
380 pub vectors: Vec<Vec<f32>>,
381 #[serde(default)]
383 pub sparse: Vec<SparseVector>,
384}
385
386#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
388pub struct VectorAlias {
389 pub alias: String,
391 #[serde(default)]
393 pub collection: String,
394}
395
396#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
398pub struct VectorPartition {
399 pub name: String,
401 #[serde(default)]
403 pub count: i64,
404}
405
406#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
409pub struct VectorPartitions {
410 #[serde(default)]
412 pub field: String,
413 #[serde(default)]
415 pub partitions: Vec<VectorPartition>,
416 #[serde(default)]
418 pub unpartitioned: i64,
419}
420
421#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
423pub struct VectorIndexRebuild {
424 #[serde(default)]
426 pub rebuilt: bool,
427 #[serde(default)]
429 pub vectors: i64,
430 #[serde(default)]
432 pub build_ms: i64,
433 #[serde(default)]
435 pub persisted: bool,
436 #[serde(default)]
438 pub tombstones_cleared: i64,
439}
440
441#[derive(Debug, Clone, PartialEq, Deserialize)]
446pub struct VectorIndexStats {
447 #[serde(default)]
449 pub collection: String,
450 #[serde(default)]
452 pub index: String,
453 #[serde(default)]
455 pub stored: i64,
456 #[serde(default)]
458 pub flat_scan_threshold: Option<i64>,
459 #[serde(default)]
461 pub layers: Option<i64>,
462 #[serde(default)]
464 pub nodes: Option<i64>,
465 #[serde(default)]
467 pub live: Option<i64>,
468 #[serde(default)]
470 pub tombstones: Option<i64>,
471 #[serde(default)]
473 pub reachable: Option<i64>,
474 #[serde(default)]
476 pub unreachable: Option<i64>,
477 #[serde(default)]
479 pub should_rebuild: Option<bool>,
480 #[serde(default)]
482 pub persisted: Option<bool>,
483 #[serde(default)]
485 pub entry: Option<String>,
486 #[serde(default)]
488 pub live_per_layer: Option<Vec<i64>>,
489 #[serde(default)]
491 pub degree: Option<Value>,
492 #[serde(default)]
494 pub memory: Option<Value>,
495 #[serde(default)]
497 pub parameters: Option<Value>,
498}
499
500#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
503pub struct RecallOptions {
504 pub sample: Option<usize>,
507 pub top_k: Option<usize>,
509 pub ef_search: Option<usize>,
511}
512
513#[derive(Debug, Clone, PartialEq, Deserialize)]
515pub struct VectorRecall {
516 #[serde(default)]
518 pub collection: String,
519 #[serde(default)]
521 pub index: String,
522 #[serde(default)]
525 pub measured: bool,
526 #[serde(default)]
528 pub recall: f64,
529 #[serde(default)]
531 pub min_recall: Option<f64>,
532 #[serde(default)]
534 pub queries: Option<i64>,
535 #[serde(default)]
537 pub top_k: Option<i64>,
538 #[serde(default)]
540 pub ef_search: Option<i64>,
541 #[serde(default)]
543 pub flat_fallbacks: Option<i64>,
544 #[serde(default)]
546 pub reason: Option<String>,
547}
548
549#[derive(Debug, Clone, PartialEq, Deserialize)]
551pub struct FusedHit {
552 pub id: String,
554 #[serde(default)]
557 pub score: f32,
558 #[serde(default)]
560 pub matched: usize,
561 #[serde(default, deserialize_with = "crate::serde_null::or_default")]
563 pub metadata: Map<String, Value>,
564}
565
566#[derive(Debug, Clone, PartialEq, Deserialize)]
568pub struct FusedSearchResult {
569 #[serde(default)]
571 pub fusion: FusionMethod,
572 #[serde(default)]
574 pub queries: usize,
575 #[serde(default)]
577 pub depth: usize,
578 #[serde(default, deserialize_with = "crate::serde_null::or_default")]
580 pub results: Vec<FusedHit>,
581}
582
583#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
586pub struct VectorConfigChange {
587 pub normalize: Option<bool>,
590 pub m: Option<usize>,
593 pub ef_construction: Option<usize>,
595 pub multi_tenant: Option<bool>,
598}
599
600#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
602pub struct NamedVector {
603 pub name: String,
605 #[serde(default)]
607 pub dimension: usize,
608 #[serde(default)]
610 pub metric: VectorMetric,
611}
612
613#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
615pub struct VectorConfig {
616 #[serde(default)]
618 pub collection: String,
619 #[serde(default)]
621 pub normalize: bool,
622 #[serde(default)]
624 pub m: usize,
625 #[serde(default)]
627 pub ef_construction: usize,
628 #[serde(default)]
630 pub multi_tenant: bool,
631 #[serde(default)]
633 pub named: Vec<NamedVector>,
634}
635
636fn op(body: Value) -> Value {
637 json!({"Vector": body})
638}
639
640impl Client {
641 pub fn vector_create_collection(
644 &mut self,
645 collection: &str,
646 dimension: usize,
647 metric: VectorMetric,
648 ) -> Result<()> {
649 self.vector_create_collection_quantized(
650 collection,
651 dimension,
652 metric,
653 VectorQuantization::None,
654 )
655 }
656
657 pub fn vector_create_collection_quantized(
659 &mut self,
660 collection: &str,
661 dimension: usize,
662 metric: VectorMetric,
663 quantization: VectorQuantization,
664 ) -> Result<()> {
665 self.send(op(json!({"CreateCollection": {
666 "collection": collection,
667 "dimension": dimension,
668 "metric": metric,
669 "quantization": quantization,
670 }})))
671 .map(|_| ())
672 }
673
674 pub fn vector_drop_collection(&mut self, collection: &str) -> Result<()> {
677 self.send(op(json!({"DropCollection": {"collection": collection}})))
678 .map(|_| ())
679 }
680
681 pub fn vector_list_collections(&mut self) -> Result<Vec<VectorCollectionSummary>> {
683 #[derive(Deserialize)]
684 struct Details {
685 #[serde(default)]
686 details: Vec<VectorCollectionSummary>,
687 }
688 let response = self.send(op(Value::String("ListCollections".into())))?;
689 let out: Details = response.decode("ListCollections")?;
690 Ok(out.details)
691 }
692
693 pub fn vector_describe_collection(&mut self, collection: &str) -> Result<VectorCollectionInfo> {
695 let response = self.send(op(
696 json!({"DescribeCollection": {"collection": collection}}),
697 ))?;
698 response.decode("DescribeCollection")
699 }
700
701 pub fn vector_upsert(
706 &mut self,
707 collection: &str,
708 id: &str,
709 vector: &[f32],
710 metadata: Option<&Map<String, Value>>,
711 ) -> Result<()> {
712 self.send(op(json!({"Upsert": {
713 "collection": collection,
714 "id": id,
715 "vector": vector,
716 "metadata": metadata,
717 }})))
718 .map(|_| ())
719 }
720
721 pub fn vector_get(&mut self, collection: &str, id: &str) -> Result<Option<VectorItem>> {
723 let response = self.send(op(json!({"Get": {"collection": collection, "id": id}})))?;
724 let data = response.expect("Json", "Get")?;
725 if data.is_null() {
726 return Ok(None);
727 }
728 response.decode("Get").map(Some)
729 }
730
731 pub fn vector_delete(&mut self, collection: &str, id: &str) -> Result<()> {
734 self.send(op(json!({"Delete": {"collection": collection, "id": id}})))
735 .map(|_| ())
736 }
737
738 pub fn vector_search(
743 &mut self,
744 collection: &str,
745 vector: &[f32],
746 top_k: usize,
747 ) -> Result<VectorSearchResult> {
748 self.vector_search_filtered(collection, vector, top_k, &BTreeMap::new())
749 }
750
751 pub fn vector_search_filtered(
757 &mut self,
758 collection: &str,
759 vector: &[f32],
760 top_k: usize,
761 filter: &BTreeMap<String, Value>,
762 ) -> Result<VectorSearchResult> {
763 let filter = if filter.is_empty() {
764 Value::Null
765 } else {
766 json!(filter)
767 };
768 let response = self.send(op(json!({"Search": {
769 "collection": collection,
770 "vector": vector,
771 "top_k": top_k,
772 "filter": filter,
773 }})))?;
774 response.decode("Search")
775 }
776
777 pub fn vector_list_vectors(
782 &mut self,
783 collection: &str,
784 limit: Option<usize>,
785 offset: Option<usize>,
786 ) -> Result<VectorPage> {
787 let mut body = Map::new();
788 body.insert("collection".into(), json!(collection));
789 if let Some(limit) = limit.filter(|l| *l > 0) {
790 body.insert("limit".into(), json!(limit));
791 }
792 if let Some(offset) = offset.filter(|o| *o > 0) {
793 body.insert("offset".into(), json!(offset));
794 }
795 let response = self.send(op(json!({"ListVectors": Value::Object(body)})))?;
796 response.decode("ListVectors")
797 }
798
799 pub fn vector_embed(&mut self, texts: &[&str], dimension: usize) -> Result<Embedding> {
802 let response = self.send(op(json!({"Embed": {
803 "texts": texts,
804 "dimension": dimension,
805 }})))?;
806 response.decode("Embed")
807 }
808
809 pub fn vector_upsert_text(&mut self, collection: &str, items: &[TextUpsert]) -> Result<i64> {
812 let response = self.send(op(json!({"UpsertText": {
813 "collection": collection,
814 "items": items,
815 }})))?;
816 Ok(response.decode::<Upserted>("UpsertText")?.upserted)
817 }
818
819 pub fn vector_set_alias(&mut self, alias: &str, collection: Option<&str>) -> Result<()> {
823 self.send(op(json!({"SetAlias": {
824 "alias": alias,
825 "collection": collection,
826 }})))
827 .map(|_| ())
828 }
829
830 pub fn vector_list_aliases(&mut self) -> Result<Vec<VectorAlias>> {
832 #[derive(Deserialize)]
833 struct Aliases {
834 #[serde(default)]
835 aliases: Vec<VectorAlias>,
836 }
837 let response = self.send(op(Value::String("ListAliases".into())))?;
838 let out: Aliases = response.decode("ListAliases")?;
839 Ok(out.aliases)
840 }
841
842 pub fn vector_add_named_vector(
845 &mut self,
846 collection: &str,
847 name: &str,
848 dimension: usize,
849 metric: VectorMetric,
850 ) -> Result<()> {
851 self.send(op(json!({"AddNamedVector": {
852 "collection": collection,
853 "name": name,
854 "dimension": dimension,
855 "metric": metric,
856 }})))
857 .map(|_| ())
858 }
859
860 pub fn vector_upsert_named(
863 &mut self,
864 collection: &str,
865 id: &str,
866 vectors: &BTreeMap<String, Vec<f32>>,
867 metadata: Option<&Map<String, Value>>,
868 ) -> Result<()> {
869 self.send(op(json!({"UpsertNamed": {
870 "collection": collection,
871 "id": id,
872 "vectors": vectors,
873 "metadata": metadata,
874 }})))
875 .map(|_| ())
876 }
877
878 pub fn vector_upsert_many(&mut self, collection: &str, items: &[VectorUpsert]) -> Result<i64> {
882 let response = self.send(op(json!({"UpsertMany": {
883 "collection": collection,
884 "items": items,
885 }})))?;
886 Ok(response.decode::<Upserted>("UpsertMany")?.upserted)
887 }
888
889 pub fn vector_delete_many(&mut self, collection: &str, ids: &[&str]) -> Result<i64> {
892 let response = self.send(op(json!({"DeleteMany": {
893 "collection": collection,
894 "ids": ids,
895 }})))?;
896 Ok(response.decode::<DeletedCount>("DeleteMany")?.deleted)
897 }
898
899 pub fn vector_search_batch(
904 &mut self,
905 collection: &str,
906 vectors: &[Vec<f32>],
907 top_k: usize,
908 options: &VectorSearchOptions,
909 ) -> Result<Vec<Vec<VectorHit>>> {
910 #[derive(Deserialize)]
911 struct Batch {
912 #[serde(default)]
913 results: Vec<Vec<VectorHit>>,
914 }
915 let mut body = Map::new();
916 body.insert("collection".into(), json!(collection));
917 body.insert("vectors".into(), json!(vectors));
918 body.insert("top_k".into(), json!(top_k));
919 options.write(&mut body);
920 let response = self.send(op(json!({"SearchBatch": Value::Object(body)})))?;
921 let out: Batch = response.decode("SearchBatch")?;
922 Ok(out.results)
923 }
924
925 pub fn vector_search_multi(
927 &mut self,
928 collection: &str,
929 vectors: &[Vec<f32>],
930 top_k: usize,
931 fusion: FusionMethod,
932 filter: Option<&Value>,
933 ) -> Result<FusedSearchResult> {
934 let mut body = Map::new();
935 body.insert("collection".into(), json!(collection));
936 body.insert("vectors".into(), json!(vectors));
937 body.insert("top_k".into(), json!(top_k));
938 body.insert("fusion".into(), json!(fusion));
939 if let Some(filter) = filter {
940 body.insert("filter".into(), filter.clone());
941 }
942 let response = self.send(op(json!({"SearchMulti": Value::Object(body)})))?;
943 response.decode("SearchMulti")
944 }
945
946 pub fn vector_search_by_examples(
952 &mut self,
953 collection: &str,
954 positive: &[VectorExample],
955 negative: &[VectorExample],
956 top_k: usize,
957 exclude_examples: bool,
958 options: &VectorSearchOptions,
959 ) -> Result<VectorSearchResult> {
960 let mut body = Map::new();
961 body.insert("collection".into(), json!(collection));
962 body.insert("positive".into(), json!(positive));
963 body.insert("negative".into(), json!(negative));
964 body.insert("top_k".into(), json!(top_k));
965 body.insert("exclude_examples".into(), json!(exclude_examples));
966 options.write(&mut body);
967 let response = self.send(op(json!({"SearchByExamples": Value::Object(body)})))?;
968 response.decode("SearchByExamples")
969 }
970
971 pub fn vector_delete_by_filter(&mut self, collection: &str, filter: &Value) -> Result<i64> {
976 let response = self.send(op(json!({"DeleteByFilter": {
977 "collection": collection,
978 "filter": filter,
979 }})))?;
980 Ok(response.decode::<DeletedCount>("DeleteByFilter")?.deleted)
981 }
982
983 pub fn vector_update_by_filter(
986 &mut self,
987 collection: &str,
988 filter: &Value,
989 set: &Map<String, Value>,
990 ) -> Result<i64> {
991 #[derive(Deserialize)]
992 struct Updated {
993 #[serde(default)]
994 updated: i64,
995 }
996 let response = self.send(op(json!({"UpdateByFilter": {
997 "collection": collection,
998 "filter": filter,
999 "set": set,
1000 }})))?;
1001 Ok(response.decode::<Updated>("UpdateByFilter")?.updated)
1002 }
1003
1004 pub fn vector_count(&mut self, collection: &str, filter: Option<&Value>) -> Result<i64> {
1007 #[derive(Deserialize)]
1008 struct Count {
1009 #[serde(default)]
1010 count: i64,
1011 }
1012 let mut body = Map::new();
1013 body.insert("collection".into(), json!(collection));
1014 if let Some(filter) = filter {
1015 body.insert("filter".into(), filter.clone());
1016 }
1017 let response = self.send(op(json!({"Count": Value::Object(body)})))?;
1018 Ok(response.decode::<Count>("Count")?.count)
1019 }
1020
1021 pub fn vector_list_partitions(
1025 &mut self,
1026 collection: &str,
1027 field: Option<&str>,
1028 ) -> Result<VectorPartitions> {
1029 let mut body = Map::new();
1030 body.insert("collection".into(), json!(collection));
1031 if let Some(field) = field {
1032 body.insert("field".into(), json!(field));
1033 }
1034 let response = self.send(op(json!({"ListPartitions": Value::Object(body)})))?;
1035 response.decode("ListPartitions")
1036 }
1037
1038 pub fn vector_rename_collection(&mut self, collection: &str, to: &str) -> Result<i64> {
1041 #[derive(Deserialize)]
1042 struct Moved {
1043 #[serde(default)]
1044 moved: i64,
1045 }
1046 let response = self.send(op(json!({"RenameCollection": {
1047 "collection": collection,
1048 "to": to,
1049 }})))?;
1050 Ok(response.decode::<Moved>("RenameCollection")?.moved)
1051 }
1052
1053 pub fn vector_copy_collection(&mut self, collection: &str, to: &str) -> Result<i64> {
1056 #[derive(Deserialize)]
1057 struct Copied {
1058 #[serde(default)]
1059 copied: i64,
1060 }
1061 let response = self.send(op(json!({"CopyCollection": {
1062 "collection": collection,
1063 "to": to,
1064 }})))?;
1065 Ok(response.decode::<Copied>("CopyCollection")?.copied)
1066 }
1067
1068 pub fn vector_rebuild_index(&mut self, collection: &str) -> Result<VectorIndexRebuild> {
1071 let response = self.send(op(json!({"RebuildIndex": {"collection": collection}})))?;
1072 response.decode("RebuildIndex")
1073 }
1074
1075 pub fn vector_index_stats(&mut self, collection: &str) -> Result<VectorIndexStats> {
1078 let response = self.send(op(json!({"IndexStats": {"collection": collection}})))?;
1079 response.decode("IndexStats")
1080 }
1081
1082 pub fn vector_measure_recall(
1086 &mut self,
1087 collection: &str,
1088 options: &RecallOptions,
1089 ) -> Result<VectorRecall> {
1090 let mut body = Map::new();
1091 body.insert("collection".into(), json!(collection));
1092 if let Some(sample) = options.sample {
1093 body.insert("sample".into(), json!(sample));
1094 }
1095 if let Some(top_k) = options.top_k {
1096 body.insert("top_k".into(), json!(top_k));
1097 }
1098 if let Some(ef_search) = options.ef_search {
1099 body.insert("ef_search".into(), json!(ef_search));
1100 }
1101 let response = self.send(op(json!({"MeasureRecall": Value::Object(body)})))?;
1102 response.decode("MeasureRecall")
1103 }
1104
1105 pub fn vector_set_collection_config(
1107 &mut self,
1108 collection: &str,
1109 change: &VectorConfigChange,
1110 ) -> Result<VectorConfig> {
1111 if *change == VectorConfigChange::default() {
1112 return Err(Error::invalid(
1113 "a configuration change must set at least one of `normalize`, `m`, `ef_construction` or `multi_tenant`",
1114 ));
1115 }
1116 let mut body = Map::new();
1117 body.insert("collection".into(), json!(collection));
1118 if let Some(normalize) = change.normalize {
1119 body.insert("normalize".into(), json!(normalize));
1120 }
1121 if let Some(m) = change.m {
1122 body.insert("m".into(), json!(m));
1123 }
1124 if let Some(ef_construction) = change.ef_construction {
1125 body.insert("ef_construction".into(), json!(ef_construction));
1126 }
1127 if let Some(multi_tenant) = change.multi_tenant {
1128 body.insert("multi_tenant".into(), json!(multi_tenant));
1129 }
1130 let response = self.send(op(json!({"SetCollectionConfig": Value::Object(body)})))?;
1131 response.decode("SetCollectionConfig")
1132 }
1133}
1134
1135#[derive(Deserialize)]
1136struct Upserted {
1137 #[serde(default)]
1138 upserted: i64,
1139}
1140
1141#[derive(Deserialize)]
1142struct DeletedCount {
1143 #[serde(default)]
1144 deleted: i64,
1145}
1146
1147#[cfg(test)]
1148mod tests {
1149 use super::*;
1150
1151 #[test]
1152 fn metrics_and_quantizations_use_the_servers_spelling() {
1153 assert_eq!(
1154 serde_json::to_value(VectorMetric::Cosine).unwrap(),
1155 "cosine"
1156 );
1157 assert_eq!(serde_json::to_value(VectorMetric::Dot).unwrap(), "dot");
1158 assert_eq!(serde_json::to_value(VectorMetric::L2).unwrap(), "l2");
1159 assert_eq!(
1160 serde_json::to_value(VectorQuantization::None).unwrap(),
1161 "none"
1162 );
1163 assert_eq!(
1164 serde_json::to_value(VectorQuantization::Int8).unwrap(),
1165 "int8"
1166 );
1167 }
1168
1169 #[test]
1170 fn metrics_round_trip_from_the_wire() {
1171 let metric: VectorMetric = serde_json::from_str("\"l2\"").unwrap();
1172 assert_eq!(metric, VectorMetric::L2);
1173 assert_eq!(VectorMetric::default(), VectorMetric::Cosine);
1174 assert_eq!(VectorQuantization::default(), VectorQuantization::None);
1175 }
1176
1177 #[test]
1178 fn a_hit_decodes_with_its_metadata() {
1179 let hit: VectorHit =
1180 serde_json::from_value(json!({"id": "a", "score": -0.02, "metadata": {"kind": "doc"}}))
1181 .unwrap();
1182 assert_eq!(hit.id, "a");
1183 assert_eq!(hit.metadata["kind"], "doc");
1184 }
1185}