diff --git a/src/qdrant_client/collection.rs b/src/qdrant_client/collection.rs index 97e5fe7..a31c323 100644 --- a/src/qdrant_client/collection.rs +++ b/src/qdrant_client/collection.rs @@ -5,19 +5,18 @@ use tonic::transport::Channel; use tonic::Status; use crate::auth::TokenInterceptor; -use crate::prelude::{CreateCollection, DeleteCollection}; use crate::qdrant::collections_client::CollectionsClient; use crate::qdrant::{ alias_operations, AliasOperations, ChangeAliases, CollectionClusterInfoRequest, CollectionClusterInfoResponse, CollectionExistsRequest, CollectionOperationResponse, - CreateAlias, DeleteAlias, GetCollectionInfoRequest, GetCollectionInfoResponse, - ListAliasesRequest, ListAliasesResponse, ListCollectionAliasesRequest, ListCollectionsRequest, - ListCollectionsResponse, RenameAlias, UpdateCollection, UpdateCollectionClusterSetupRequest, - UpdateCollectionClusterSetupResponse, + CreateAlias, CreateCollection, DeleteAlias, DeleteCollection, GetCollectionInfoRequest, + GetCollectionInfoResponse, ListAliasesRequest, ListAliasesResponse, + ListCollectionAliasesRequest, ListCollectionsRequest, ListCollectionsResponse, RenameAlias, + UpdateCollection, UpdateCollectionClusterSetupRequest, UpdateCollectionClusterSetupResponse, }; use crate::qdrant_client::{Qdrant, QdrantResult}; -/// Collection operations. +/// # Collection operations /// /// Create, update and delete collections, manage collection aliases and collection cluster /// configuration. @@ -48,31 +47,6 @@ impl Qdrant { Ok(result) } - /// Delete an existing collection. - /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - ///# async fn delete_collection(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client.delete_collection("my_collection").await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn delete_collection( - &self, - request: impl Into, - ) -> QdrantResult { - let delete_collection = &request.into(); - - self.with_collections_client(|mut collection_api| async move { - let result = collection_api.delete(delete_collection.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - /// Create a new collection. /// /// ```no_run @@ -105,6 +79,30 @@ impl Qdrant { .await } + /// Get collection info. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + ///# async fn collection_info(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// client.collection_info("my_collection").await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn collection_info( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + self.with_collections_client(|mut collection_api| async move { + let result = collection_api.get(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } + /// List all collections. /// /// ```no_run @@ -192,25 +190,26 @@ impl Qdrant { .await } - /// Get collection info. + /// Delete an existing collection. /// /// ```no_run ///# use qdrant_client::{Qdrant, QdrantError}; - ///# async fn collection_info(client: &Qdrant) + ///# async fn delete_collection(client: &Qdrant) ///# -> Result<(), QdrantError> { - /// client.collection_info("my_collection").await?; + /// client.delete_collection("my_collection").await?; ///# Ok(()) ///# } /// ``` /// - /// Documentation: - pub async fn collection_info( + /// Documentation: + pub async fn delete_collection( &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); + request: impl Into, + ) -> QdrantResult { + let delete_collection = &request.into(); + self.with_collections_client(|mut collection_api| async move { - let result = collection_api.get(request.clone()).await?; + let result = collection_api.delete(delete_collection.clone()).await?; Ok(result.into_inner()) }) .await @@ -239,27 +238,53 @@ impl Qdrant { self.update_aliases(request.into()).await } - /// Delete existing collection name alias. + /// List collection name aliases for all collections. /// /// ```no_run ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::CreateAliasBuilder; + ///# async fn list_aliases(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// client.list_aliases().await?; + ///# Ok(()) + ///# } + /// ``` /// - ///# async fn delete_alias(client: &Qdrant) + /// This only lists collection name aliases. To list collection names, use + /// [`list_collections`](Self::list_collections). + /// + /// Documentation: + pub async fn list_aliases(&self) -> QdrantResult { + self.with_collections_client(|mut collection_api| async move { + let result = collection_api.list_aliases(ListAliasesRequest {}).await?; + Ok(result.into_inner()) + }) + .await + } + + /// List collection name aliases for a specific collection. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + ///# async fn list_collection_aliases(client: &Qdrant) ///# -> Result<(), QdrantError> { - /// client - /// .delete_alias("my_alias") - /// .await?; + /// client.list_collection_aliases("my_collection").await?; ///# Ok(()) ///# } /// ``` /// - /// Documentation: - pub async fn delete_alias( + /// Documentation: + pub async fn list_collection_aliases( &self, - request: impl Into, - ) -> QdrantResult { - self.update_aliases(request.into()).await + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + self.with_collections_client(|mut collection_api| async move { + let result = collection_api + .list_collection_aliases(request.clone()) + .await?; + Ok(result.into_inner()) + }) + .await } /// Rename existing collection name alias. @@ -310,53 +335,27 @@ impl Qdrant { .await } - /// List collection name aliases for a specific collection. + /// Delete existing collection name alias. /// /// ```no_run ///# use qdrant_client::{Qdrant, QdrantError}; - ///# async fn list_collection_aliases(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client.list_collection_aliases("my_collection").await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn list_collection_aliases( - &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); - self.with_collections_client(|mut collection_api| async move { - let result = collection_api - .list_collection_aliases(request.clone()) - .await?; - Ok(result.into_inner()) - }) - .await - } - - /// List collection name aliases for all collections. + /// use qdrant_client::qdrant::CreateAliasBuilder; /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - ///# async fn list_aliases(client: &Qdrant) + ///# async fn delete_alias(client: &Qdrant) ///# -> Result<(), QdrantError> { - /// client.list_aliases().await?; + /// client + /// .delete_alias("my_alias") + /// .await?; ///# Ok(()) ///# } /// ``` /// - /// This only lists collection name aliases. To list collection names, use - /// [`list_collections`](Self::list_collections). - /// - /// Documentation: - pub async fn list_aliases(&self) -> QdrantResult { - self.with_collections_client(|mut collection_api| async move { - let result = collection_api.list_aliases(ListAliasesRequest {}).await?; - Ok(result.into_inner()) - }) - .await + /// Documentation: + pub async fn delete_alias( + &self, + request: impl Into, + ) -> QdrantResult { + self.update_aliases(request.into()).await } /// List cluster info of a collection. @@ -436,9 +435,8 @@ mod tests { use super::*; use crate::payload::Payload; - use crate::prelude::Distance; use crate::qdrant::{ - CountPointsBuilder, CreateCollectionBuilder, PointStruct, SearchPointsBuilder, + CountPointsBuilder, CreateCollectionBuilder, Distance, PointStruct, SearchPointsBuilder, UpsertPointsBuilder, VectorParamsBuilder, }; diff --git a/src/qdrant_client/index.rs b/src/qdrant_client/index.rs new file mode 100644 index 0000000..659fe73 --- /dev/null +++ b/src/qdrant_client/index.rs @@ -0,0 +1,80 @@ +use crate::qdrant::{ + CreateFieldIndexCollection, DeleteFieldIndexCollection, PointsOperationResponse, +}; +use crate::qdrant_client::{Qdrant, QdrantResult}; + +/// # Index operations +/// +/// Manage field and payload indices in collections. +/// +/// Documentation: +impl Qdrant { + /// Create payload index in a collection. + /// + /// ```no_run + ///# use std::collections::HashMap; + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::{CreateFieldIndexCollectionBuilder, FieldType}; + /// + ///# async fn create_field_index(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// client + /// .create_field_index( + /// CreateFieldIndexCollectionBuilder::new( + /// "my_collection", + /// "city", + /// FieldType::Keyword, + /// ), + /// ) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn create_field_index( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut client| async move { + let result = client.create_field_index(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } + + /// Delete payload index from a collection. + /// + /// ```no_run + ///# use std::collections::HashMap; + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::DeleteFieldIndexCollectionBuilder; + /// + ///# async fn create_field_index(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// client + /// .delete_field_index(DeleteFieldIndexCollectionBuilder::new( + /// "my_collection", + /// "city", + /// )) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn delete_field_index( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut client| async move { + let result = client.delete_field_index(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } +} diff --git a/src/qdrant_client/mod.rs b/src/qdrant_client/mod.rs index bacb0e2..f0b55c6 100644 --- a/src/qdrant_client/mod.rs +++ b/src/qdrant_client/mod.rs @@ -3,8 +3,11 @@ mod collection; pub mod config; mod conversions; pub mod error; +mod index; +mod payload; mod points; mod query; +mod search; mod sharding_keys; mod snapshot; @@ -60,12 +63,22 @@ pub type QdrantBuilder = QdrantConfig; /// /// # Operations /// +/// Categories: +/// +/// - [Collection operations](Self#collection-operations) - manage collections, aliases and cluster configuration +/// - [Point operations](Self#point-operations) - manage points and vectors +/// - [Payload operations](Self#payload-operations) - manage point payloads +/// - [Search operations](Self#search-operations) - search and explore points +/// - [Query operations](Self#query-operations) - query points using universal search +/// - [Index operations](Self#index-operations) - manage field and payload indices +/// - [Snapshot operations](Self#snapshot-operations) - manage instance or collection snapshots +/// - [Shard key operations](Self#sharding-key-operations) - manage shard keys +/// /// Common operations include: /// -/// - [`create_collection`](Qdrant::create_collection) - Create a new collection -/// - [`upsert_points`](Qdrant::upsert_points) - Insert or update points -/// - [`search_points`](Qdrant::search_points) - Search points with similarity search -/// - [All operations](Qdrant#implementations) +/// - [`create_collection`](Self::create_collection) - create a new collection +/// - [`upsert_points`](Self::upsert_points) - insert or update points +/// - [`search_points`](Self::search_points) - search points with similarity search pub struct Qdrant { /// Client configuration pub config: QdrantConfig, @@ -74,6 +87,8 @@ pub struct Qdrant { channel: ChannelPool, } +/// # Construct and connect +/// /// Methods to construct a new Qdrant client. impl Qdrant { /// Create a new Qdrant client. diff --git a/src/qdrant_client/payload.rs b/src/qdrant_client/payload.rs new file mode 100644 index 0000000..0beb3ab --- /dev/null +++ b/src/qdrant_client/payload.rs @@ -0,0 +1,176 @@ +use crate::qdrant::{ + ClearPayloadPoints, DeletePayloadPoints, PointsOperationResponse, SetPayloadPoints, +}; +use crate::qdrant_client::{Qdrant, QdrantResult}; + +/// # Payload operations +/// +/// Manage point payloads. +/// +/// Documentation: +impl Qdrant { + /// Set payload of points. + /// + /// Sets only the given payload values on a point, leaving other existing payloads in place. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::Payload; + /// use qdrant_client::qdrant::{PointsIdsList, SetPayloadPointsBuilder}; + /// use serde_json::json; + /// + ///# async fn set_payload(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// let payload: Payload = json!({ + /// "property1": "string", + /// "property2": "string", + /// }) + /// .try_into() + /// .unwrap(); + /// + /// client + /// .set_payload( + /// SetPayloadPointsBuilder::new("my_collection", payload) + /// .points_selector(PointsIdsList { + /// ids: vec![0.into(), 3.into(), 10.into()], + /// }) + /// .wait(true), + /// ) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn set_payload( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut points_api| async move { + let result = points_api.set_payload(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } + + /// Overwrite payload of points. + /// + /// Sets the given payload values on a point, completely replacing existing payload. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::Payload; + /// use qdrant_client::qdrant::{ + /// points_selector::PointsSelectorOneOf, PointsIdsList, SetPayloadPointsBuilder, + /// }; + /// use serde_json::json; + /// + ///# async fn overwrite_payload(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// let payload: Payload = json!({ + /// "property1": "string", + /// "property2": "string", + /// }) + /// .try_into() + /// .unwrap(); + /// + /// client + /// .overwrite_payload( + /// SetPayloadPointsBuilder::new("my_collection", payload) + /// .points_selector(PointsSelectorOneOf::Points(PointsIdsList { + /// ids: vec![0.into(), 3.into(), 10.into()], + /// })) + /// .wait(true), + /// ) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn overwrite_payload( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut points_api| async move { + let result = points_api.overwrite_payload(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } + + /// Delete specified payload keys of points. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::{DeletePayloadPointsBuilder, PointsIdsList}; + /// + ///# async fn delete_payload(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// client + /// .delete_payload( + /// DeletePayloadPointsBuilder::new( + /// "my_collection", + /// vec!["color".to_string(), "price".to_string()], + /// ) + /// .points_selector(PointsIdsList { + /// ids: vec![0.into(), 3.into(), 100.into()], + /// }) + /// .wait(true), + /// ) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn delete_payload( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut points_api| async move { + let result = points_api.delete_payload(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } + + /// Clear all payload of points. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::{ClearPayloadPointsBuilder, PointsIdsList}; + /// + ///# async fn clear_payload(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// client + /// .clear_payload(ClearPayloadPointsBuilder::new("my_collection").points( + /// PointsIdsList { + /// ids: vec![0.into(), 3.into(), 100.into()], + /// }, + /// )) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn clear_payload( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut points_api| async move { + let result = points_api.clear_payload(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } +} diff --git a/src/qdrant_client/points.rs b/src/qdrant_client/points.rs index 143d5e2..1a3d47e 100644 --- a/src/qdrant_client/points.rs +++ b/src/qdrant_client/points.rs @@ -5,23 +5,19 @@ use tonic::transport::Channel; use tonic::Status; use crate::auth::TokenInterceptor; -use crate::prelude::SearchPoints; use crate::qdrant::points_client::PointsClient; use crate::qdrant::{ - ClearPayloadPoints, CountPoints, CountResponse, CreateFieldIndexCollection, - DeleteFieldIndexCollection, DeletePayloadPoints, DeletePointVectors, DeletePoints, - DiscoverBatchPoints, DiscoverBatchResponse, DiscoverPoints, DiscoverResponse, GetPoints, - GetResponse, PointsOperationResponse, RecommendBatchPoints, RecommendBatchResponse, - RecommendGroupsResponse, RecommendPointGroups, RecommendPoints, RecommendResponse, - ScrollPoints, ScrollResponse, SearchBatchPoints, SearchBatchResponse, SearchGroupsResponse, - SearchPointGroups, SearchResponse, SetPayloadPoints, UpdateBatchPoints, UpdateBatchResponse, + CountPoints, CountResponse, DeletePointVectors, DeletePoints, GetPoints, GetResponse, + PointsOperationResponse, ScrollPoints, ScrollResponse, UpdateBatchPoints, UpdateBatchResponse, UpdatePointVectors, UpsertPoints, }; use crate::qdrant_client::{Qdrant, QdrantResult}; -/// Point operations. +/// # Point operations /// -/// Manage points, vectors and payloads. Search and explore them. +/// Manage points and vectors. +/// +/// Documentation: impl Qdrant { pub(crate) async fn with_points_client>>( &self, @@ -47,117 +43,6 @@ impl Qdrant { Ok(result) } - /// Search points in a collection. - /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{Condition, Filter, SearchParamsBuilder, SearchPointsBuilder}; - /// - ///# async fn search_points(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client - /// .search_points( - /// SearchPointsBuilder::new("my_collection", vec![0.2, 0.1, 0.9, 0.7], 3) - /// .filter(Filter::must([Condition::matches( - /// "city", - /// "London".to_string(), - /// )])) - /// .params(SearchParamsBuilder::default().hnsw_ef(128).exact(false)), - /// ) - /// .await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn search_points( - &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut points_api| async move { - let result = points_api.search(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Batch multiple points searches in a collection. - /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{Condition, Filter, SearchBatchPointsBuilder, SearchPointsBuilder,}; - /// - ///# async fn search_batch_points(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// let filter = Filter::must([Condition::matches("city", "London".to_string())]); - /// - /// let searches = vec![ - /// SearchPointsBuilder::new("my_collection", vec![0.2, 0.1, 0.9, 0.7], 3) - /// .filter(filter.clone()) - /// .build(), - /// SearchPointsBuilder::new("my_collection", vec![0.5, 0.3, 0.2, 0.3], 3) - /// .filter(filter) - /// .build(), - /// ]; - /// - /// client - /// .search_batch_points(SearchBatchPointsBuilder::new("my_collection", searches)) - /// .await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn search_batch_points( - &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut points_api| async move { - let result = points_api.search_batch(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Search points in a collection and group results by a payload field. - /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::SearchPointGroupsBuilder; - /// - ///# async fn search_points(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client - /// .search_groups(SearchPointGroupsBuilder::new( - /// "my_collection", // Collection name - /// vec![1.1], // Search vector - /// 4, // Search limit - /// "document_id", // Group by field - /// 2, // Group size - /// )) - /// .await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn search_groups( - &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut points_api| async move { - let result = points_api.search_groups(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - /// Insert or update points in a collection. /// /// If points with the specified IDs already exist, they will be overwritten. @@ -266,204 +151,195 @@ impl Qdrant { .await } - /// Set payload of points. + /// Retrieve specific points from a collection. /// - /// Sets only the given payload values on a point, leaving other existing payloads in place. + /// Use [`with_vectors`](crate::qdrant::GetPointsBuilder::with_vectors) and + /// [`with_payload`](crate::qdrant::GetPointsBuilder::with_payload) to specify whether to + /// include or exclude vector and payload data in the response. By default they are excluded to + /// save bandwidth. /// /// ```no_run ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::Payload; - /// use qdrant_client::qdrant::{PointsIdsList, SetPayloadPointsBuilder}; - /// use serde_json::json; + /// use qdrant_client::qdrant::GetPointsBuilder; /// - ///# async fn set_payload(client: &Qdrant) + ///# async fn get_points(client: &Qdrant) ///# -> Result<(), QdrantError> { - /// let payload: Payload = json!({ - /// "property1": "string", - /// "property2": "string", - /// }) - /// .try_into() - /// .unwrap(); - /// /// client - /// .set_payload( - /// SetPayloadPointsBuilder::new("my_collection", payload) - /// .points_selector(PointsIdsList { - /// ids: vec![0.into(), 3.into(), 10.into()], - /// }) - /// .wait(true), + /// .get_points( + /// GetPointsBuilder::new( + /// "my_collection", + /// vec![0.into(), 30.into(), 100.into()], + /// ) + /// .with_vectors(true) + /// .with_payload(true) /// ) /// .await?; ///# Ok(()) ///# } /// ``` /// - /// Documentation: - pub async fn set_payload( - &self, - request: impl Into, - ) -> QdrantResult { + /// Documentation: + pub async fn get_points(&self, request: impl Into) -> QdrantResult { let request = &request.into(); self.with_points_client(|mut points_api| async move { - let result = points_api.set_payload(request.clone()).await?; + let result = points_api.get(request.clone()).await?; Ok(result.into_inner()) }) .await } - /// Overwrite payload of points. + /// Scroll points in a collection. /// - /// Sets the given payload values on a point, completely replacing existing payload. + /// Use [`with_vectors`](crate::qdrant::ScrollPointsBuilder::with_vectors) and + /// [`with_payload`](crate::qdrant::ScrollPointsBuilder::with_payload) to specify whether to + /// include or exclude vector and payload data in the response. By default they are excluded to + /// save bandwidth. /// /// ```no_run ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::Payload; - /// use qdrant_client::qdrant::{ - /// points_selector::PointsSelectorOneOf, PointsIdsList, SetPayloadPointsBuilder, - /// }; - /// use serde_json::json; + /// use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder}; /// - ///# async fn overwrite_payload(client: &Qdrant) + ///# async fn scroll(client: &Qdrant) ///# -> Result<(), QdrantError> { - /// let payload: Payload = json!({ - /// "property1": "string", - /// "property2": "string", - /// }) - /// .try_into() - /// .unwrap(); - /// /// client - /// .overwrite_payload( - /// SetPayloadPointsBuilder::new("my_collection", payload) - /// .points_selector(PointsSelectorOneOf::Points(PointsIdsList { - /// ids: vec![0.into(), 3.into(), 10.into()], - /// })) - /// .wait(true), + /// .scroll( + /// ScrollPointsBuilder::new("my_collection") + /// .filter(Filter::must([Condition::matches( + /// "color", + /// "red".to_string(), + /// )])) + /// .limit(1) + /// .with_payload(true) + /// .with_vectors(true), /// ) /// .await?; ///# Ok(()) ///# } /// ``` /// - /// Documentation: - pub async fn overwrite_payload( - &self, - request: impl Into, - ) -> QdrantResult { + /// Documentation: + pub async fn scroll(&self, request: impl Into) -> QdrantResult { let request = &request.into(); self.with_points_client(|mut points_api| async move { - let result = points_api.overwrite_payload(request.clone()).await?; + let result = points_api.scroll(request.clone()).await?; Ok(result.into_inner()) }) .await } - /// Delete specified payload keys of points. - /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{DeletePayloadPointsBuilder, PointsIdsList}; - /// - ///# async fn delete_payload(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client - /// .delete_payload( - /// DeletePayloadPointsBuilder::new( - /// "my_collection", - /// vec!["color".to_string(), "price".to_string()], - /// ) - /// .points_selector(PointsIdsList { - /// ids: vec![0.into(), 3.into(), 100.into()], - /// }) - /// .wait(true), - /// ) - /// .await?; - ///# Ok(()) - ///# } - /// ``` + /// Count points in a collection. /// - /// Documentation: - pub async fn delete_payload( - &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut points_api| async move { - let result = points_api.delete_payload(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Clear all payload of points. + /// Use [`exact`](crate::qdrant::CountPointsBuilder::exact) to specify whether to use exact + /// counting. Exact counting is more accurate but slower. /// /// ```no_run ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{ClearPayloadPointsBuilder, PointsIdsList}; + /// use qdrant_client::qdrant::{Condition, CountPointsBuilder, Filter}; /// - ///# async fn clear_payload(client: &Qdrant) + ///# async fn count(client: &Qdrant) ///# -> Result<(), QdrantError> { /// client - /// .clear_payload(ClearPayloadPointsBuilder::new("my_collection").points( - /// PointsIdsList { - /// ids: vec![0.into(), 3.into(), 100.into()], - /// }, - /// )) + /// .count( + /// CountPointsBuilder::new("collection_name") + /// .filter(Filter::must([Condition::matches( + /// "color", + /// "red".to_string(), + /// )])) + /// .exact(true), + /// ) /// .await?; ///# Ok(()) ///# } /// ``` /// - /// Documentation: - pub async fn clear_payload( - &self, - request: impl Into, - ) -> QdrantResult { + /// Documentation: + pub async fn count(&self, request: impl Into) -> QdrantResult { let request = &request.into(); self.with_points_client(|mut points_api| async move { - let result = points_api.clear_payload(request.clone()).await?; + let result = points_api.count(request.clone()).await?; Ok(result.into_inner()) }) .await } - /// Retrieve specific points from a collection. + /// Batch point updates in a collection. /// - /// Use [`with_vectors`](crate::qdrant::GetPointsBuilder::with_vectors) and - /// [`with_payload`](crate::qdrant::GetPointsBuilder::with_payload) to specify whether to - /// include or exclude vector and payload data in the response. By default they are excluded to - /// save bandwidth. + /// Execute a batch of point [updates](crate::qdrant::points_update_operation::Operation) in a single operation. /// /// ```no_run + ///# use std::collections::HashMap; ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::GetPointsBuilder; + /// use qdrant_client::Payload; + /// use qdrant_client::qdrant::{ + /// points_selector::PointsSelectorOneOf, + /// points_update_operation::{ + /// Operation, OverwritePayload, PointStructList, UpdateVectors, + /// }, + /// PointStruct, PointVectors, PointsIdsList, PointsSelector, PointsUpdateOperation, + /// UpdateBatchPointsBuilder, + /// }; + /// use serde_json::json; /// - ///# async fn get_points(client: &Qdrant) + ///# async fn update_batch_points(client: &Qdrant) ///# -> Result<(), QdrantError> { /// client - /// .get_points( - /// GetPointsBuilder::new( + /// .update_points_batch( + /// UpdateBatchPointsBuilder::new( /// "my_collection", - /// vec![0.into(), 30.into(), 100.into()], + /// vec![ + /// PointsUpdateOperation { + /// operation: Some(Operation::Upsert(PointStructList { + /// points: vec![PointStruct::new( + /// 1, + /// vec![1.0, 2.0, 3.0, 4.0], + /// Payload::try_from(json!({})).unwrap(), + /// )], + /// ..Default::default() + /// })), + /// }, + /// PointsUpdateOperation { + /// operation: Some(Operation::UpdateVectors(UpdateVectors { + /// points: vec![PointVectors { + /// id: Some(1.into()), + /// vectors: Some(vec![1.0, 2.0, 3.0, 4.0].into()), + /// }], + /// ..Default::default() + /// })), + /// }, + /// PointsUpdateOperation { + /// operation: Some(Operation::OverwritePayload(OverwritePayload { + /// points_selector: Some(PointsSelector { + /// points_selector_one_of: Some(PointsSelectorOneOf::Points( + /// PointsIdsList { + /// ids: vec![1.into()], + /// }, + /// )), + /// }), + /// payload: HashMap::from([("test_payload".to_string(), 1.into())]), + /// ..Default::default() + /// })), + /// }, + /// ], /// ) - /// .with_vectors(true) - /// .with_payload(true) + /// .wait(true), /// ) /// .await?; ///# Ok(()) ///# } /// ``` /// - /// Documentation: - pub async fn get_points(&self, request: impl Into) -> QdrantResult { + /// Documentation: + pub async fn update_points_batch( + &self, + request: impl Into, + ) -> QdrantResult { let request = &request.into(); self.with_points_client(|mut points_api| async move { - let result = points_api.get(request.clone()).await?; + let result = points_api.update_batch(request.clone()).await?; Ok(result.into_inner()) }) .await @@ -528,58 +404,17 @@ impl Qdrant { .await } - /// Delete vectors from points. + /// Update vectors on points. /// - /// Removes specified vectors from points in a collection, leaving existing vectors on these - /// points with a different name in place. + /// Updates the given vectors on points in a collection, leaving existing vectors on these points + /// with a different name in place. /// /// ```no_run + ///# use std::collections::HashMap; ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{DeletePointVectorsBuilder, PointsIdsList, VectorsSelector}; + /// use qdrant_client::qdrant::{PointVectors, UpdatePointVectorsBuilder}; /// - ///# async fn delete_vectors(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client - /// .delete_vectors( - /// DeletePointVectorsBuilder::new("my_collection") - /// .points_selector(PointsIdsList { - /// ids: vec![0.into(), 3.into(), 10.into()], - /// }) - /// .vectors(VectorsSelector { - /// names: vec!["text".into(), "image".into()], - /// }) - /// .wait(true), - /// ) - /// .await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn delete_vectors( - &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut points_api| async move { - let result = points_api.delete_vectors(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Update vectors on points. - /// - /// Updates the given vectors on points in a collection, leaving existing vectors on these points - /// with a different name in place. - /// - /// ```no_run - ///# use std::collections::HashMap; - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{PointVectors, UpdatePointVectorsBuilder}; - /// - ///# async fn update_vectors(client: &Qdrant) + ///# async fn update_vectors(client: &Qdrant) ///# -> Result<(), QdrantError> { /// client /// .update_vectors( @@ -626,470 +461,42 @@ impl Qdrant { .await } - /// Scroll points in a collection. - /// - /// Use [`with_vectors`](crate::qdrant::ScrollPointsBuilder::with_vectors) and - /// [`with_payload`](crate::qdrant::ScrollPointsBuilder::with_payload) to specify whether to - /// include or exclude vector and payload data in the response. By default they are excluded to - /// save bandwidth. - /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder}; - /// - ///# async fn scroll(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client - /// .scroll( - /// ScrollPointsBuilder::new("my_collection") - /// .filter(Filter::must([Condition::matches( - /// "color", - /// "red".to_string(), - /// )])) - /// .limit(1) - /// .with_payload(true) - /// .with_vectors(true), - /// ) - /// .await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn scroll(&self, request: impl Into) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut points_api| async move { - let result = points_api.scroll(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Recommend points in a collection. - /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{Condition, Filter, RecommendPointsBuilder, RecommendStrategy}; - /// - ///# async fn recommend(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client - /// .recommend( - /// RecommendPointsBuilder::new("my_collection", 3) - /// .add_positive(100) - /// .add_positive(200) - /// .add_positive(vec![100.0, 231.0]) - /// .add_negative(718) - /// .add_negative(vec![0.2, 0.3, 0.4, 0.5]) - /// .strategy(RecommendStrategy::AverageVector) - /// .filter(Filter::must([Condition::matches( - /// "city", - /// "London".to_string(), - /// )])), - /// ) - /// .await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn recommend( - &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut points_api| async move { - let result = points_api.recommend(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Batch multiple points recommendations in a collection. - /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{Condition, Filter, RecommendBatchPointsBuilder, RecommendPointsBuilder}; - /// - ///# async fn recommend_batch(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// let filter = Filter::must([Condition::matches("city", "London".to_string())]); - /// - /// let recommend_queries = vec![ - /// RecommendPointsBuilder::new("my_collection", 3) - /// .add_positive(100) - /// .add_positive(231) - /// .add_negative(718) - /// .filter(filter.clone()) - /// .build(), - /// RecommendPointsBuilder::new("my_collection", 3) - /// .add_positive(200) - /// .add_positive(67) - /// .add_negative(300) - /// .filter(filter.clone()) - /// .build(), - /// ]; - /// - /// client - /// .recommend_batch(RecommendBatchPointsBuilder::new( - /// "my_collection", - /// recommend_queries, - /// )) - /// .await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn recommend_batch( - &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut points_api| async move { - let result = points_api.recommend_batch(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Recommend points in a collection and group results by a payload field. - /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{RecommendPointGroupsBuilder, RecommendStrategy}; - /// - ///# async fn recommend_groups(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client - /// .recommend_groups( - /// RecommendPointGroupsBuilder::new( - /// "my_collection", // Collection name - /// "document_id", // Group by field - /// 2, // Group size - /// 3, // Search limit - /// ) - /// .add_positive(100) - /// .add_positive(200) - /// .add_negative(718) - /// .strategy(RecommendStrategy::AverageVector), - /// ) - /// .await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn recommend_groups( - &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut points_api| async move { - let result = points_api.recommend_groups(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Discover points in a collection. - /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{ - /// target_vector::Target, vector_example::Example, ContextExamplePairBuilder, - /// DiscoverPointsBuilder, VectorExample, - /// }; - /// - ///# async fn discover(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client - /// .discover( - /// DiscoverPointsBuilder::new( - /// "my_collection", // Collection name - /// vec![ - /// ContextExamplePairBuilder::default() - /// .positive(Example::Id(100.into())) - /// .negative(Example::Id(718.into())) - /// .build(), - /// ContextExamplePairBuilder::default() - /// .positive(Example::Id(200.into())) - /// .negative(Example::Id(300.into())) - /// .build(), - /// ], - /// 10, // Search limit - /// ) - /// .target(Target::Single(VectorExample { - /// example: Some(Example::Vector(vec![0.2, 0.1, 0.9, 0.7].into())), - /// })), - /// ) - /// .await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn discover( - &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut points_api| async move { - let result = points_api.discover(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Batch multiple point discoveries in a collection. - /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{ - /// vector_example::Example, ContextExamplePairBuilder, DiscoverBatchPointsBuilder, - /// DiscoverPointsBuilder, - /// }; - /// - ///# async fn discover_batch(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// let discover_points = DiscoverBatchPointsBuilder::new( - /// "my_collection", - /// vec![ - /// DiscoverPointsBuilder::new( - /// "my_collection", - /// vec![ - /// ContextExamplePairBuilder::default() - /// .positive(Example::Id(100.into())) - /// .negative(Example::Id(718.into())) - /// .build(), - /// ContextExamplePairBuilder::default() - /// .positive(Example::Id(200.into())) - /// .negative(Example::Id(300.into())) - /// .build(), - /// ], - /// 10, - /// ) - /// .build(), - /// DiscoverPointsBuilder::new( - /// "my_collection", - /// vec![ - /// ContextExamplePairBuilder::default() - /// .positive(Example::Id(342.into())) - /// .negative(Example::Id(213.into())) - /// .build(), - /// ContextExamplePairBuilder::default() - /// .positive(Example::Id(100.into())) - /// .negative(Example::Id(200.into())) - /// .build(), - /// ], - /// 10, - /// ) - /// .build(), - /// ], - /// ); - /// - /// client.discover_batch(&discover_points.build()).await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn discover_batch( - &self, - request: &DiscoverBatchPoints, - ) -> QdrantResult { - self.with_points_client(|mut points_api| async move { - let result = points_api.discover_batch(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Count points in a collection. - /// - /// Use [`exact`](crate::qdrant::CountPointsBuilder::exact) to specify whether to use exact - /// counting. Exact counting is more accurate but slower. - /// - /// ```no_run - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{Condition, CountPointsBuilder, Filter}; - /// - ///# async fn count(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client - /// .count( - /// CountPointsBuilder::new("collection_name") - /// .filter(Filter::must([Condition::matches( - /// "color", - /// "red".to_string(), - /// )])) - /// .exact(true), - /// ) - /// .await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn count(&self, request: impl Into) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut points_api| async move { - let result = points_api.count(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Batch point updates in a collection. - /// - /// Execute a batch of point [updates](crate::qdrant::points_update_operation::Operation) in a single operation. - /// - /// ```no_run - ///# use std::collections::HashMap; - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::Payload; - /// use qdrant_client::qdrant::{ - /// points_selector::PointsSelectorOneOf, - /// points_update_operation::{ - /// Operation, OverwritePayload, PointStructList, UpdateVectors, - /// }, - /// PointStruct, PointVectors, PointsIdsList, PointsSelector, PointsUpdateOperation, - /// UpdateBatchPointsBuilder, - /// }; - /// use serde_json::json; - /// - ///# async fn update_batch_points(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client - /// .update_points_batch( - /// UpdateBatchPointsBuilder::new( - /// "my_collection", - /// vec![ - /// PointsUpdateOperation { - /// operation: Some(Operation::Upsert(PointStructList { - /// points: vec![PointStruct::new( - /// 1, - /// vec![1.0, 2.0, 3.0, 4.0], - /// Payload::try_from(json!({})).unwrap(), - /// )], - /// ..Default::default() - /// })), - /// }, - /// PointsUpdateOperation { - /// operation: Some(Operation::UpdateVectors(UpdateVectors { - /// points: vec![PointVectors { - /// id: Some(1.into()), - /// vectors: Some(vec![1.0, 2.0, 3.0, 4.0].into()), - /// }], - /// ..Default::default() - /// })), - /// }, - /// PointsUpdateOperation { - /// operation: Some(Operation::OverwritePayload(OverwritePayload { - /// points_selector: Some(PointsSelector { - /// points_selector_one_of: Some(PointsSelectorOneOf::Points( - /// PointsIdsList { - /// ids: vec![1.into()], - /// }, - /// )), - /// }), - /// payload: HashMap::from([("test_payload".to_string(), 1.into())]), - /// ..Default::default() - /// })), - /// }, - /// ], - /// ) - /// .wait(true), - /// ) - /// .await?; - ///# Ok(()) - ///# } - /// ``` + /// Delete vectors from points. /// - /// Documentation: - pub async fn update_points_batch( - &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut points_api| async move { - let result = points_api.update_batch(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Create payload index in a collection. + /// Removes specified vectors from points in a collection, leaving existing vectors on these + /// points with a different name in place. /// /// ```no_run - ///# use std::collections::HashMap; ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::{CreateFieldIndexCollectionBuilder, FieldType}; + /// use qdrant_client::qdrant::{DeletePointVectorsBuilder, PointsIdsList, VectorsSelector}; /// - ///# async fn create_field_index(client: &Qdrant) + ///# async fn delete_vectors(client: &Qdrant) ///# -> Result<(), QdrantError> { /// client - /// .create_field_index( - /// CreateFieldIndexCollectionBuilder::new( - /// "my_collection", - /// "city", - /// FieldType::Keyword, - /// ), + /// .delete_vectors( + /// DeletePointVectorsBuilder::new("my_collection") + /// .points_selector(PointsIdsList { + /// ids: vec![0.into(), 3.into(), 10.into()], + /// }) + /// .vectors(VectorsSelector { + /// names: vec!["text".into(), "image".into()], + /// }) + /// .wait(true), /// ) /// .await?; ///# Ok(()) ///# } /// ``` /// - /// Documentation: - pub async fn create_field_index( - &self, - request: impl Into, - ) -> QdrantResult { - let request = &request.into(); - - self.with_points_client(|mut client| async move { - let result = client.create_field_index(request.clone()).await?; - Ok(result.into_inner()) - }) - .await - } - - /// Delete payload index from a collection. - /// - /// ```no_run - ///# use std::collections::HashMap; - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::DeleteFieldIndexCollectionBuilder; - /// - ///# async fn create_field_index(client: &Qdrant) - ///# -> Result<(), QdrantError> { - /// client - /// .delete_field_index(DeleteFieldIndexCollectionBuilder::new( - /// "my_collection", - /// "city", - /// )) - /// .await?; - ///# Ok(()) - ///# } - /// ``` - /// - /// Documentation: - pub async fn delete_field_index( + /// Documentation: + pub async fn delete_vectors( &self, - request: impl Into, + request: impl Into, ) -> QdrantResult { let request = &request.into(); - self.with_points_client(|mut client| async move { - let result = client.delete_field_index(request.clone()).await?; + self.with_points_client(|mut points_api| async move { + let result = points_api.delete_vectors(request.clone()).await?; Ok(result.into_inner()) }) .await diff --git a/src/qdrant_client/query.rs b/src/qdrant_client/query.rs index 7a58859..01794ad 100644 --- a/src/qdrant_client/query.rs +++ b/src/qdrant_client/query.rs @@ -2,7 +2,7 @@ use super::QdrantResult; use crate::qdrant::{QueryBatchPoints, QueryBatchResponse, QueryPoints, QueryResponse}; use crate::qdrant_client::Qdrant; -/// Point query operations. +/// # Query operations /// /// Query points using the universal search API. /// diff --git a/src/qdrant_client/search.rs b/src/qdrant_client/search.rs new file mode 100644 index 0000000..a5d587e --- /dev/null +++ b/src/qdrant_client/search.rs @@ -0,0 +1,369 @@ +use crate::qdrant::{ + DiscoverBatchPoints, DiscoverBatchResponse, DiscoverPoints, DiscoverResponse, + RecommendBatchPoints, RecommendBatchResponse, RecommendGroupsResponse, RecommendPointGroups, + RecommendPoints, RecommendResponse, SearchBatchPoints, SearchBatchResponse, + SearchGroupsResponse, SearchPointGroups, SearchPoints, SearchResponse, +}; +use crate::qdrant_client::{Qdrant, QdrantResult}; + +/// # Search operations +/// +/// Search and explore points. +/// +/// Documentation: +impl Qdrant { + /// Search points in a collection. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::{Condition, Filter, SearchParamsBuilder, SearchPointsBuilder}; + /// + ///# async fn search_points(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// client + /// .search_points( + /// SearchPointsBuilder::new("my_collection", vec![0.2, 0.1, 0.9, 0.7], 3) + /// .filter(Filter::must([Condition::matches( + /// "city", + /// "London".to_string(), + /// )])) + /// .params(SearchParamsBuilder::default().hnsw_ef(128).exact(false)), + /// ) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn search_points( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut points_api| async move { + let result = points_api.search(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } + + /// Batch multiple points searches in a collection. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::{Condition, Filter, SearchBatchPointsBuilder, SearchPointsBuilder,}; + /// + ///# async fn search_batch_points(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// let filter = Filter::must([Condition::matches("city", "London".to_string())]); + /// + /// let searches = vec![ + /// SearchPointsBuilder::new("my_collection", vec![0.2, 0.1, 0.9, 0.7], 3) + /// .filter(filter.clone()) + /// .build(), + /// SearchPointsBuilder::new("my_collection", vec![0.5, 0.3, 0.2, 0.3], 3) + /// .filter(filter) + /// .build(), + /// ]; + /// + /// client + /// .search_batch_points(SearchBatchPointsBuilder::new("my_collection", searches)) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn search_batch_points( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut points_api| async move { + let result = points_api.search_batch(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } + + /// Search points in a collection and group results by a payload field. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::SearchPointGroupsBuilder; + /// + ///# async fn search_points(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// client + /// .search_groups(SearchPointGroupsBuilder::new( + /// "my_collection", // Collection name + /// vec![1.1], // Search vector + /// 4, // Search limit + /// "document_id", // Group by field + /// 2, // Group size + /// )) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn search_groups( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut points_api| async move { + let result = points_api.search_groups(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } + + /// Recommend points in a collection. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::{Condition, Filter, RecommendPointsBuilder, RecommendStrategy}; + /// + ///# async fn recommend(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// client + /// .recommend( + /// RecommendPointsBuilder::new("my_collection", 3) + /// .add_positive(100) + /// .add_positive(200) + /// .add_positive(vec![100.0, 231.0]) + /// .add_negative(718) + /// .add_negative(vec![0.2, 0.3, 0.4, 0.5]) + /// .strategy(RecommendStrategy::AverageVector) + /// .filter(Filter::must([Condition::matches( + /// "city", + /// "London".to_string(), + /// )])), + /// ) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn recommend( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut points_api| async move { + let result = points_api.recommend(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } + + /// Batch multiple points recommendations in a collection. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::{Condition, Filter, RecommendBatchPointsBuilder, RecommendPointsBuilder}; + /// + ///# async fn recommend_batch(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// let filter = Filter::must([Condition::matches("city", "London".to_string())]); + /// + /// let recommend_queries = vec![ + /// RecommendPointsBuilder::new("my_collection", 3) + /// .add_positive(100) + /// .add_positive(231) + /// .add_negative(718) + /// .filter(filter.clone()) + /// .build(), + /// RecommendPointsBuilder::new("my_collection", 3) + /// .add_positive(200) + /// .add_positive(67) + /// .add_negative(300) + /// .filter(filter.clone()) + /// .build(), + /// ]; + /// + /// client + /// .recommend_batch(RecommendBatchPointsBuilder::new( + /// "my_collection", + /// recommend_queries, + /// )) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn recommend_batch( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut points_api| async move { + let result = points_api.recommend_batch(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } + + /// Recommend points in a collection and group results by a payload field. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::{RecommendPointGroupsBuilder, RecommendStrategy}; + /// + ///# async fn recommend_groups(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// client + /// .recommend_groups( + /// RecommendPointGroupsBuilder::new( + /// "my_collection", // Collection name + /// "document_id", // Group by field + /// 2, // Group size + /// 3, // Search limit + /// ) + /// .add_positive(100) + /// .add_positive(200) + /// .add_negative(718) + /// .strategy(RecommendStrategy::AverageVector), + /// ) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn recommend_groups( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut points_api| async move { + let result = points_api.recommend_groups(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } + + /// Discover points in a collection. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::{ + /// target_vector::Target, vector_example::Example, ContextExamplePairBuilder, + /// DiscoverPointsBuilder, VectorExample, + /// }; + /// + ///# async fn discover(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// client + /// .discover( + /// DiscoverPointsBuilder::new( + /// "my_collection", // Collection name + /// vec![ + /// ContextExamplePairBuilder::default() + /// .positive(Example::Id(100.into())) + /// .negative(Example::Id(718.into())) + /// .build(), + /// ContextExamplePairBuilder::default() + /// .positive(Example::Id(200.into())) + /// .negative(Example::Id(300.into())) + /// .build(), + /// ], + /// 10, // Search limit + /// ) + /// .target(Target::Single(VectorExample { + /// example: Some(Example::Vector(vec![0.2, 0.1, 0.9, 0.7].into())), + /// })), + /// ) + /// .await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn discover( + &self, + request: impl Into, + ) -> QdrantResult { + let request = &request.into(); + + self.with_points_client(|mut points_api| async move { + let result = points_api.discover(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } + + /// Batch multiple point discoveries in a collection. + /// + /// ```no_run + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::{ + /// vector_example::Example, ContextExamplePairBuilder, DiscoverBatchPointsBuilder, + /// DiscoverPointsBuilder, + /// }; + /// + ///# async fn discover_batch(client: &Qdrant) + ///# -> Result<(), QdrantError> { + /// let discover_points = DiscoverBatchPointsBuilder::new( + /// "my_collection", + /// vec![ + /// DiscoverPointsBuilder::new( + /// "my_collection", + /// vec![ + /// ContextExamplePairBuilder::default() + /// .positive(Example::Id(100.into())) + /// .negative(Example::Id(718.into())) + /// .build(), + /// ContextExamplePairBuilder::default() + /// .positive(Example::Id(200.into())) + /// .negative(Example::Id(300.into())) + /// .build(), + /// ], + /// 10, + /// ) + /// .build(), + /// DiscoverPointsBuilder::new( + /// "my_collection", + /// vec![ + /// ContextExamplePairBuilder::default() + /// .positive(Example::Id(342.into())) + /// .negative(Example::Id(213.into())) + /// .build(), + /// ContextExamplePairBuilder::default() + /// .positive(Example::Id(100.into())) + /// .negative(Example::Id(200.into())) + /// .build(), + /// ], + /// 10, + /// ) + /// .build(), + /// ], + /// ); + /// + /// client.discover_batch(&discover_points.build()).await?; + ///# Ok(()) + ///# } + /// ``` + /// + /// Documentation: + pub async fn discover_batch( + &self, + request: &DiscoverBatchPoints, + ) -> QdrantResult { + self.with_points_client(|mut points_api| async move { + let result = points_api.discover_batch(request.clone()).await?; + Ok(result.into_inner()) + }) + .await + } +} diff --git a/src/qdrant_client/sharding_keys.rs b/src/qdrant_client/sharding_keys.rs index 847cc16..259ecc4 100644 --- a/src/qdrant_client/sharding_keys.rs +++ b/src/qdrant_client/sharding_keys.rs @@ -3,7 +3,7 @@ use crate::qdrant::{ }; use crate::qdrant_client::{Qdrant, QdrantResult}; -/// Sharding key operations. +/// # Sharding key operations /// /// Create or delete shard keys for collections. /// diff --git a/src/qdrant_client/snapshot.rs b/src/qdrant_client/snapshot.rs index 6894a21..05b32a8 100644 --- a/src/qdrant_client/snapshot.rs +++ b/src/qdrant_client/snapshot.rs @@ -13,7 +13,7 @@ use crate::qdrant::{ }; use crate::qdrant_client::{Qdrant, QdrantResult}; -/// Snapshot operations. +/// # Snapshot operations /// /// Create, recover and manage snapshots for collections or a full Qdrant instance. /// @@ -99,6 +99,86 @@ impl Qdrant { .await } + /// Download a collection snapshot on this node. + /// + /// ```no_run + ///# use std::fs::File; + ///# use qdrant_client::{Qdrant, QdrantError}; + /// use qdrant_client::qdrant::SnapshotDownloadBuilder; + /// + ///# async fn download_snapshot(client: &Qdrant) + ///# -> Result { + /// client.download_snapshot( + /// SnapshotDownloadBuilder::new("./target_path.snapshot", "my_collection") + /// .snapshot_name("snapshot_name") + /// .rest_api_uri("http://localhost:6333") + /// ).await?; + /// + /// let snapshot_file = File::open("./target_path.snapshot")?; + ///# Ok(snapshot_file) + ///# } + /// ``` + /// + /// Note: Snapshots are node-local. They only contain data of a single node. In distributed + /// mode you must create a snapshot on each node separately. Each node has their own list of + /// snapshots. + /// + /// Documentation: + #[cfg(feature = "download_snapshots")] + pub async fn download_snapshot( + &self, + download: impl Into, + ) -> QdrantResult<()> { + use std::io::Write; + + use futures_util::StreamExt; + + use crate::qdrant_client::error::QdrantError; + + let options = download.into(); + + let snapshot_name = match &options.snapshot_name { + Some(sn) => sn.to_string(), + _ => match self + .list_snapshots(options.collection_name.clone()) + .await? + .snapshot_descriptions + .first() + { + Some(sn) => sn.name.clone(), + _ => { + return Err(QdrantError::NoSnapshotFound( + options.collection_name.clone(), + )) + } + }, + }; + + let mut stream = reqwest::get(format!( + "{}/collections/{}/snapshots/{snapshot_name}", + options + .rest_api_uri + .as_ref() + .map(|uri| uri.to_string()) + .unwrap_or_else(|| String::from("http://localhost:6333")), + options.collection_name, + )) + .await? + .bytes_stream(); + + let _ = std::fs::remove_file(&options.out_path); + let mut file = std::fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(&options.out_path)?; + + while let Some(chunk) = stream.next().await { + let _written = file.write(&chunk?)?; + } + + Ok(()) + } + /// Delete a collection snapshot on this node. /// /// ```no_run @@ -201,84 +281,4 @@ impl Qdrant { }) .await } - - /// Download a collection snapshot on this node. - /// - /// ```no_run - ///# use std::fs::File; - ///# use qdrant_client::{Qdrant, QdrantError}; - /// use qdrant_client::qdrant::SnapshotDownloadBuilder; - /// - ///# async fn download_snapshot(client: &Qdrant) - ///# -> Result { - /// client.download_snapshot( - /// SnapshotDownloadBuilder::new("./target_path.snapshot", "my_collection") - /// .snapshot_name("snapshot_name") - /// .rest_api_uri("http://localhost:6333") - /// ).await?; - /// - /// let snapshot_file = File::open("./target_path.snapshot")?; - ///# Ok(snapshot_file) - ///# } - /// ``` - /// - /// Note: Snapshots are node-local. They only contain data of a single node. In distributed - /// mode you must create a snapshot on each node separately. Each node has their own list of - /// snapshots. - /// - /// Documentation: - #[cfg(feature = "download_snapshots")] - pub async fn download_snapshot( - &self, - download: impl Into, - ) -> QdrantResult<()> { - use std::io::Write; - - use futures_util::StreamExt; - - use crate::qdrant_client::error::QdrantError; - - let options = download.into(); - - let snapshot_name = match &options.snapshot_name { - Some(sn) => sn.to_string(), - _ => match self - .list_snapshots(options.collection_name.clone()) - .await? - .snapshot_descriptions - .first() - { - Some(sn) => sn.name.clone(), - _ => { - return Err(QdrantError::NoSnapshotFound( - options.collection_name.clone(), - )) - } - }, - }; - - let mut stream = reqwest::get(format!( - "{}/collections/{}/snapshots/{snapshot_name}", - options - .rest_api_uri - .as_ref() - .map(|uri| uri.to_string()) - .unwrap_or_else(|| String::from("http://localhost:6333")), - options.collection_name, - )) - .await? - .bytes_stream(); - - let _ = std::fs::remove_file(&options.out_path); - let mut file = std::fs::OpenOptions::new() - .write(true) - .create_new(true) - .open(&options.out_path)?; - - while let Some(chunk) = stream.next().await { - let _written = file.write(&chunk?)?; - } - - Ok(()) - } }