| name | server-querying-rust |
| summary | SQL++ queries with the Couchbase Rust SDK โ scope.query, cluster.query, positional and named parameters, scan consistency, deserializing rows into structs, DML (INSERT/UPDATE/DELETE/UPSERT), MutationState RYOW |
| description | SQL++ queries with the Couchbase Rust SDK โ scope.query, cluster.query, positional and named parameters, scan consistency, deserializing rows into structs, DML (INSERT/UPDATE/DELETE/UPSERT), MutationState RYOW |
| compatibility | Rust SDK 1.x+. Requires tokio async runtime. |
| metadata | {"last_verified":"2026-05","min_server_version":"7.0","handoff":[{"condition":"user asks about connection setup or SDK configuration","skill":"server-connection-rust"},{"condition":"user asks about slow queries or index recommendations","skill":"server-query-optimizer"},{"condition":"user asks about CAS, bulk ops, sub-document, or SDK patterns","skill":"sdk-patterns-rust"},{"condition":"user asks about full-text search or vector search","skill":"search-rust"},{"condition":"user is on Server 8.x and asks about columnar or OLAP queries","skill":"columnar-analytics"}]} |
SQL++ Querying โ Rust
SQL++ syntax reference: shared/server/sql-syntax.md โ SELECT, JOINs, MERGE, window functions, built-in functions.
Basic Query
Prefer scope.query โ it sets query_context automatically so collection names don't need to be fully qualified.
use couchbase::options::query_options::QueryOptions;
let scope = cluster.bucket("travel-sample").scope("inventory");
let mut result = scope.query("SELECT * FROM `airline` LIMIT 10", None).await?;
let mut rows = result.rows();
while let Some(row) = rows.next().await {
let row: serde_json::Value = row?;
println!("{row}");
}
Parameterized Queries
Always use parameters โ never interpolate user input into query strings.
let mut result = scope.query(
"SELECT name, country FROM `airline` WHERE country = $1 LIMIT $2",
QueryOptions::new()
.add_positional_parameter("United States")?
.add_positional_parameter(20)?,
).await?;
let mut result = scope.query(
"SELECT name, country FROM `airline` WHERE country = $country LIMIT $limit",
QueryOptions::new()
.add_named_parameter("country", "United States")?
.add_named_parameter("limit", 20)?,
).await?;
Deserialize into Structs
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Airline {
name: String,
country: String,
#[serde(rename = "iata")]
iata_code: Option<String>,
}
let mut result = scope.query(
"SELECT name, country, iata FROM `airline` WHERE country = $1",
QueryOptions::new().add_positional_parameter("France")?,
).await?;
let mut rows = result.rows();
while let Some(row) = rows.next().await {
let airline: Airline = row?;
println!("{airline:?}");
}
Scan Consistency
use couchbase::query::ScanConsistency;
use couchbase::kv::MutationState;
let mut result = scope.query("SELECT COUNT(*) FROM `airline`", None).await?;
let mut result = scope.query(
"SELECT COUNT(*) FROM `airline`",
QueryOptions::new().scan_consistency(ScanConsistency::RequestPlus),
).await?;
let upsert_result = collection.upsert("airline::new", doc, None).await?;
let mutation_state = MutationState::from(upsert_result.mutation_token().unwrap().clone());
let mut result = scope.query(
"SELECT * FROM `airline` WHERE META().id = 'airline::new'",
QueryOptions::new().scan_consistency(ScanConsistency::AtPlus(mutation_state)),
).await?;
DML โ INSERT, UPDATE, DELETE, UPSERT
Boundary note: This section covers SDK execution of DML statements. For SQL++ DML syntax (INSERT, UPDATE, DELETE, UPSERT, MERGE), see sqlpp-language.
scope.query(
"INSERT INTO `airline` (KEY, VALUE) VALUES ($key, $doc)",
QueryOptions::new()
.add_named_parameter("key", "airline::9999")?
.add_named_parameter("doc", serde_json::json!({"name": "New Air", "country": "US"}))?,
).await?;
scope.query(
"UPDATE `airline` SET country = $country WHERE META().id = $id",
QueryOptions::new()
.add_named_parameter("country", "CA")?
.add_named_parameter("id", "airline::9999")?,
).await?;
scope.query(
"DELETE FROM `airline` WHERE META().id = $id",
QueryOptions::new().add_named_parameter("id", "airline::9999")?,
).await?;
Cluster-Level Query
Use cluster.query when querying across buckets or using fully qualified keyspace names:
let mut result = cluster.query(
"SELECT * FROM `travel-sample`.`inventory`.`airline` LIMIT 5",
None,
).await?;
Prepared Statements
Mark a query as prepared to avoid re-parsing on repeated execution:
let mut result = scope.query(
"SELECT name FROM `airline` WHERE country = $1",
QueryOptions::new()
.adhoc(false)
.add_positional_parameter("US")?,
).await?;
Collecting All Rows
use futures::StreamExt;
let mut result = scope.query("SELECT name FROM `airline` LIMIT 100", None).await?;
let mut rows = result.rows();
let mut airlines: Vec<serde_json::Value> = Vec::new();
while let Some(row) = rows.next().await {
airlines.push(row?);
}
Transactions
Rust SDK 1.x does not support distributed ACID transactions. There is no cluster.transactions() API.
For atomic multi-document updates, use optimistic locking with CAS โ read a document, modify it, then replace using the CAS value from the read. If another writer modified the document concurrently, the replace fails with a CasMismatch error and you retry.
See sdk-patterns-rust for the full CAS pattern.