Skip to content

Commit d97b0d1

Browse files
authored
[Cosmos] Add APIs to perform single-partition queries against a container (#1814)
* add container_client and ContainerClient::read * rudimentary initial query API * re-do partition keys to hide serde_json * switch to a `NullValue` marker shared by query and partition key, and update docs * add tracing_subscriber with env filter to all cosmos examples * fix doctests * remove Send bound from WASM * Add 'Sync' bound to from_response_body, but exclude bounds from wasm32 * copyright headers, copyright headers everywhere * change Pageable back to an owning type * pr feedback * refactor partition_key parameter to partition_key_strategy * refmt and move helper back to azure_data_cosmos * fix docs * remove unnecessary doctest from internal utility * fix cosmos_query sample * simplify doctests that don't run using panic!()
1 parent 9d5c324 commit d97b0d1

24 files changed

+1253
-179
lines changed

.vscode/cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"asyncoperation",
2424
"azsdk",
2525
"azurecli",
26+
"Contoso",
2627
"cplusplus",
2728
"datalake",
2829
"datetime",

eng/dict/rust-custom.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ newtype
33
repr
44
rustc
55
rustls
6+
turbofish

sdk/cosmos/.dict.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
colls
2+
documentdb
23
pkranges
34
sprocs
45
udfs

sdk/cosmos/azure_data_cosmos/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ typespec_client_core = { workspace = true, features = ["derive"] }
2222
tracing.workspace = true
2323
url.workspace = true
2424
serde.workspace = true
25+
serde_json.workspace = true
2526

2627
[dev-dependencies]
2728
tokio.workspace = true
28-
serde_json.workspace = true
2929
azure_identity.workspace = true
3030
clap.workspace = true
3131
time.workspace = true
32+
futures.workspace = true
33+
tracing-subscriber = { workspace = true, features = [ "env-filter", "fmt" ] }
3234

3335
[lints]
3436
workspace = true

sdk/cosmos/azure_data_cosmos/examples/cosmos_connect.rs renamed to sdk/cosmos/azure_data_cosmos/examples/cosmos_metadata.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
use azure_data_cosmos::{clients::DatabaseClientMethods, CosmosClient, CosmosClientMethods};
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
use azure_data_cosmos::{
5+
clients::{ContainerClientMethods, DatabaseClientMethods},
6+
CosmosClient, CosmosClientMethods,
7+
};
28
use clap::Parser;
39
use std::sync::Arc;
410

@@ -11,6 +17,10 @@ pub struct Args {
1117
/// The database to fetch information for.
1218
database: String,
1319

20+
/// Optionally, the container to fetch information for.
21+
#[clap(long, short)]
22+
container: Option<String>,
23+
1424
/// An authentication key to use when connecting to the Cosmos DB account. If omitted, the connection will use Entra ID.
1525
#[clap(long)]
1626
#[cfg(feature = "key_auth")]
@@ -19,13 +29,28 @@ pub struct Args {
1929

2030
#[tokio::main]
2131
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
32+
let _ = tracing_subscriber::fmt()
33+
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
34+
.init();
35+
2236
let args = Args::parse();
2337

2438
let client = create_client(&args);
2539

2640
let db_client = client.database_client(&args.database);
27-
let response = db_client.read(None).await?.deserialize_body().await?;
28-
println!("{:?}", response);
41+
if let Some(container_name) = args.container {
42+
let container_client = db_client.container_client(container_name);
43+
let response = container_client
44+
.read(None)
45+
.await?
46+
.deserialize_body()
47+
.await?;
48+
println!("{:?}", response);
49+
return Ok(());
50+
} else {
51+
let response = db_client.read(None).await?.deserialize_body().await?;
52+
println!("{:?}", response);
53+
}
2954
Ok(())
3055
}
3156

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
use azure_data_cosmos::{
5+
clients::{ContainerClientMethods, DatabaseClientMethods},
6+
CosmosClient, CosmosClientMethods, PartitionKey,
7+
};
8+
use azure_identity::DefaultAzureCredential;
9+
use clap::Parser;
10+
use futures::StreamExt;
11+
use std::sync::Arc;
12+
13+
/// An example to show querying a Cosmos DB container.
14+
#[derive(Parser)]
15+
pub struct Args {
16+
/// The Cosmos DB endpoint to connect to.
17+
endpoint: String,
18+
19+
/// The database to query.
20+
database: String,
21+
22+
/// The container to query.
23+
container: String,
24+
25+
/// The query to execute.
26+
#[clap(long, short)]
27+
query: String,
28+
29+
/// The partition key to use when querying the container. Currently this only supports a single string partition key.
30+
#[clap(long, short)]
31+
partition_key: String,
32+
33+
/// An authentication key to use when connecting to the Cosmos DB account. If omitted, the connection will use Entra ID.
34+
#[clap(long)]
35+
#[cfg(feature = "key_auth")]
36+
key: Option<String>,
37+
}
38+
39+
#[tokio::main]
40+
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
41+
let _ = tracing_subscriber::fmt()
42+
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
43+
.init();
44+
45+
let args = Args::parse();
46+
47+
let client = create_client(&args);
48+
49+
let db_client = client.database_client(&args.database);
50+
let container_client = db_client.container_client(&args.container);
51+
52+
let pk = PartitionKey::from(args.partition_key);
53+
let mut items_pager =
54+
container_client.query_items::<serde_json::Value>(&args.query, pk, None)?;
55+
56+
while let Some(page) = items_pager.next().await {
57+
let response = page?;
58+
println!("Results Page");
59+
println!(" Query Metrics: {:?}", response.query_metrics);
60+
println!(" Index Metrics: {:?}", response.index_metrics);
61+
println!(" Items:");
62+
for item in response.items {
63+
println!(" * {:?}", item);
64+
}
65+
}
66+
Ok(())
67+
}
68+
69+
#[cfg(feature = "key_auth")]
70+
fn create_client(args: &Args) -> CosmosClient {
71+
if let Some(key) = args.key.as_ref() {
72+
CosmosClient::with_key(&args.endpoint, key.clone(), None).unwrap()
73+
} else {
74+
let cred = DefaultAzureCredential::new().map(Arc::new).unwrap();
75+
CosmosClient::new(&args.endpoint, cred, None).unwrap()
76+
}
77+
}
78+
79+
#[cfg(not(feature = "key_auth"))]
80+
fn create_client(args: &Args) -> CosmosClient {
81+
let cred = DefaultAzureCredential::new().map(Arc::new).unwrap();
82+
CosmosClient::new(&args.endpoint, cred, None).unwrap()
83+
}

0 commit comments

Comments
 (0)