Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk/cosmos/azure_data_cosmos/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- RID-addressed databases skip the extra database read when resolving throughput offers, reusing the addressed RID directly. ([#4687](https://github.com/Azure/azure-sdk-for-rust/pull/4687))
- Reading and querying items by RID now works end-to-end, including a parent-database cross-check that rejects a container RID belonging to a different database. ([#4687](https://github.com/Azure/azure-sdk-for-rust/pull/4687))
- Added cross-partition `DISTINCT` query support. `SELECT DISTINCT` now deduplicates structurally equal values across every physical partition and page, rather than failing as an unsupported query feature. A `DISTINCT` query of the exact form `SELECT DISTINCT VALUE <path> … ORDER BY <same path>` (for example `SELECT DISTINCT VALUE c.city FROM c ORDER BY c.city`) is resumable from a continuation token; every other shape — including a list projection such as `SELECT DISTINCT c.city …` and any multi-column `ORDER BY` — is not, and requesting a token for it returns an error explaining how to rewrite the query. ([#5026](https://github.com/Azure/azure-sdk-for-rust/pull/5026))
- Added finite cross-partition `ORDER BY VectorDistance(...)` queries with `TOP` or `OFFSET`/`LIMIT`. Results are fully buffered before the first page and cannot be resumed from continuation tokens. Hybrid/full-text vector ranking remains unsupported. ([#5130](https://github.com/Azure/azure-sdk-for-rust/pull/5130))
- Added an SDK-generated `x-ms-client-id` header that remains stable for each `CosmosClient`. ([#4844](https://github.com/Azure/azure-sdk-for-rust/pull/4844))
- Added opt-in Cosmos binary JSON encoding for item operations (`create`/`read`/`replace`/`upsert`). Enable it via `CosmosClientBuilder::with_binary_encoding_options` (or the `AZURE_COSMOS_BINARY_ENCODING_ENABLED` environment-variable fallback). Off by default; when disabled, requests and responses are byte-for-byte unchanged. ([#4671](https://github.com/Azure/azure-sdk-for-rust/pull/4671))
- Added `FeedOptions::max_fan_out` (and `FeedOptions::with_max_fan_out`) to cap how many physical partitions a cross-partition query or change feed may fan out to. Applies to `ContainerClient::query_items` and `ContainerClient::query_change_feed`. The cap is enforced only at initial query setup; a partition that splits mid-execution and pushes the fan-out higher does not abort the operation. ([#4855](https://github.com/Azure/azure-sdk-for-rust/pull/4855))
Expand Down
48 changes: 42 additions & 6 deletions sdk/cosmos/azure_data_cosmos/src/clients/container_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,15 +851,13 @@ impl ContainerClient {
))
}

/// Executes a single-partition query against items in the container.
/// Executes a query against items in the container.
///
/// The resulting document will be deserialized into the type provided as `T`.
/// If you want to deserialize the document to a direct representation of the JSON returned, use [`serde_json::Value`] as the target type.
///
/// We recommend using ["turbofish" syntax](https://doc.rust-lang.org/book/appendix-02-operators.html#:~:text=turbofish) (`query_items::<SomeTargetType>(...)`) to specify the target type, as it makes type inference easier.
///
/// **NOTE:** Currently, the Azure Cosmos DB SDK for Rust only supports single-partition querying. Cross-partition queries may be supported in the future.
///
/// # Arguments
///
/// * `query` - The query to execute.
Expand All @@ -868,8 +866,13 @@ impl ContainerClient {
///
/// # Cross Partition Queries
///
/// Cross-partition queries are significantly limited in the current version of the Cosmos DB SDK.
/// They are run on the gateway and limited to simple projections (`SELECT`) and filtering (`WHERE`).
/// Cross-partition vector ordering supports pure `ORDER BY VectorDistance(...)` queries with
/// a finite `TOP N` or `OFFSET x LIMIT y` window. The SDK buffers that result window before
/// returning the first page, so use narrow projections and choose a result window appropriate
/// for the memory available to the application.
///
/// The buffered result can be iterated in pages, but it does not support continuation tokens
/// for resuming in another process. Hybrid/full-text vector ranking remains unsupported.
/// For more details, see [the Cosmos DB documentation page on cross-partition queries](https://learn.microsoft.com/en-us/rest/api/cosmos-db/querying-cosmosdb-resources-using-the-rest-api#queries-that-cannot-be-served-by-gateway).
Comment thread
simorenoh marked this conversation as resolved.
Outdated
///
/// # Examples
Expand Down Expand Up @@ -912,6 +915,38 @@ impl ContainerClient {
/// # }
/// ```
///
/// A raw SQL vector search can bind the query vector as a parameter. Vector ordering must not
/// specify `ASC` or `DESC`, and must use `TOP N` or `OFFSET`/`LIMIT` when querying across
/// partitions:
///
/// ```rust,no_run
/// # async fn doc() -> Result<(), Box<dyn std::error::Error>> {
/// use azure_data_cosmos::{feed::FeedScope, Query};
/// use futures::TryStreamExt;
/// # let container_client: azure_data_cosmos::clients::ContainerClient = panic!("this is a non-running example");
/// #[derive(serde::Deserialize)]
/// struct VectorMatch {
/// id: String,
/// score: f64,
/// }
///
/// let query_vector = vec![0.1_f32, 0.2, 0.3];
/// let query = Query::from(
/// "SELECT TOP 5 c.id, VectorDistance(c.embedding, @vector, false) AS score \
/// FROM c ORDER BY VectorDistance(c.embedding, @vector, false)",
/// )
/// .with_parameter("@vector", &query_vector)?;
/// let mut matches = container_client
/// .query_items::<VectorMatch>(query, FeedScope::full_container(), None)
/// .await?;
///
/// while let Some(item) = matches.try_next().await? {
/// println!("{}: {}", item.id, item.score);
/// }
/// # Ok(())
/// # }
/// ```
///
/// See [`PartitionKey`](crate::PartitionKey) for more information on how to specify a partition key, and [`Query`] for more information on how to specify a query.
pub async fn query_items<T: DeserializeOwned + Send + 'static>(
&self,
Expand All @@ -920,6 +955,7 @@ impl ContainerClient {
options: Option<QueryOptions>,
) -> crate::Result<QueryItemIterator<T>> {
let options = options.unwrap_or_default();
let plan_options = options.to_plan_options();
let query = query.into();

let container_ref = self.container_ref.clone();
Expand Down Expand Up @@ -950,7 +986,7 @@ impl ContainerClient {
initial_operation,
&options.operation,
options.feed.continuation_token.as_ref(),
&options.feed.to_plan_options(),
&plan_options,
)
.await?;
Ok(QueryItemIterator::new(
Expand Down
38 changes: 38 additions & 0 deletions sdk/cosmos/azure_data_cosmos/src/feed/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,21 @@ impl FeedScope {
/// .with_parameter("@customer_info", CustomerInfo { id: 42, name: "Contoso".into() }).unwrap();
/// # assert_eq!(serde_json::to_string(&query).unwrap(), "{\"query\":\"\\n SELECT * FROM c\\n WHERE c.id = @customer_info.id\\n AND c.name = @customer_info.name\",\"parameters\":[{\"name\":\"@customer_info\",\"value\":{\"id\":42,\"name\":\"Contoso\"}}]}");
/// ```
///
/// Vectors can be bound as array parameters rather than interpolated into the
/// query text:
///
/// ```rust
/// # use azure_data_cosmos::Query;
/// let query_vector = vec![0.1_f32, 0.2, 0.3];
/// let query = Query::from(
/// "SELECT TOP 5 c.id, VectorDistance(c.embedding, @vector, false) AS score \
/// FROM c ORDER BY VectorDistance(c.embedding, @vector, false)",
/// )
/// .with_parameter("@vector", &query_vector).unwrap();
/// # let serialized = serde_json::to_value(&query).unwrap();
/// # assert_eq!(serialized["parameters"][0]["value"], serde_json::to_value(&query_vector).unwrap());
/// ```
#[derive(Clone, Debug, Serialize)]
pub struct Query {
/// The query text itself.
Expand Down Expand Up @@ -242,6 +257,29 @@ mod tests {
Ok(())
}

#[test]
pub fn serialize_query_with_float_vector_parameter() -> Result<(), Box<dyn Error>> {
let query_vector = vec![0.25_f32, -1.5, 2.0];
let query = Query::from(
"SELECT TOP 3 * FROM c ORDER BY VectorDistance(c.embedding, @vector, false)",
)
.with_parameter("@vector", query_vector)?;

assert_eq!(
serde_json::to_value(query)?,
serde_json::json!({
"query": "SELECT TOP 3 * FROM c ORDER BY VectorDistance(c.embedding, @vector, false)",
"parameters": [
{
"name": "@vector",
"value": [0.25, -1.5, 2.0]
}
]
})
);
Ok(())
}

#[test]
pub fn with_text_replaces_query_text() {
let query = Query::from("SELECT * FROM c").with_text("SELECT c.id FROM c".to_string());
Expand Down
4 changes: 4 additions & 0 deletions sdk/cosmos/azure_data_cosmos/src/options/feed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ impl QueryOptions {
self.feed = self.feed.with_continuation_token(continuation_token);
self
}

pub(crate) fn to_plan_options(&self) -> PlanOptions {
self.feed.to_plan_options()
}
}

#[cfg(test)]
Expand Down
Loading
Loading