use crate::convert::*;
use crate::error::Result;
use bytes::Bytes;
use kstone_core::Item;
use kstone_proto::{self as proto, keystone_db_client::KeystoneDbClient};
use tonic::transport::Channel;
pub struct RemoteQuery {
partition_key: Vec<u8>,
sort_key_condition: Option<proto::SortKeyCondition>,
limit: Option<u32>,
exclusive_start_key: Option<proto::LastKey>,
scan_forward: Option<bool>,
index_name: Option<String>,
}
impl RemoteQuery {
pub fn new(pk: &[u8]) -> Self {
Self {
partition_key: pk.to_vec(),
sort_key_condition: None,
limit: None,
exclusive_start_key: None,
scan_forward: None,
index_name: None,
}
}
pub fn sk_eq(mut self, sk: &[u8]) -> Self {
self.sort_key_condition = Some(proto::SortKeyCondition {
condition: Some(proto::sort_key_condition::Condition::EqualTo(
value_to_proto_bytes(sk),
)),
});
self
}
pub fn sk_lt(mut self, sk: &[u8]) -> Self {
self.sort_key_condition = Some(proto::SortKeyCondition {
condition: Some(proto::sort_key_condition::Condition::LessThan(
value_to_proto_bytes(sk),
)),
});
self
}
pub fn sk_lte(mut self, sk: &[u8]) -> Self {
self.sort_key_condition = Some(proto::SortKeyCondition {
condition: Some(proto::sort_key_condition::Condition::LessThanOrEqual(
value_to_proto_bytes(sk),
)),
});
self
}
pub fn sk_gt(mut self, sk: &[u8]) -> Self {
self.sort_key_condition = Some(proto::SortKeyCondition {
condition: Some(proto::sort_key_condition::Condition::GreaterThan(
value_to_proto_bytes(sk),
)),
});
self
}
pub fn sk_gte(mut self, sk: &[u8]) -> Self {
self.sort_key_condition = Some(proto::SortKeyCondition {
condition: Some(proto::sort_key_condition::Condition::GreaterThanOrEqual(
value_to_proto_bytes(sk),
)),
});
self
}
pub fn sk_between(mut self, sk1: &[u8], sk2: &[u8]) -> Self {
self.sort_key_condition = Some(proto::SortKeyCondition {
condition: Some(proto::sort_key_condition::Condition::Between(
proto::BetweenCondition {
lower: Some(value_to_proto_bytes(sk1)),
upper: Some(value_to_proto_bytes(sk2)),
},
)),
});
self
}
pub fn sk_begins_with(mut self, prefix: &[u8]) -> Self {
self.sort_key_condition = Some(proto::SortKeyCondition {
condition: Some(proto::sort_key_condition::Condition::BeginsWith(
value_to_proto_bytes(prefix),
)),
});
self
}
pub fn forward(mut self, forward: bool) -> Self {
self.scan_forward = Some(forward);
self
}
pub fn limit(mut self, limit: usize) -> Self {
self.limit = Some(limit as u32);
self
}
pub fn start_after(mut self, pk: &[u8], sk: Option<&[u8]>) -> Self {
self.exclusive_start_key = Some(proto::LastKey {
partition_key: pk.to_vec(),
sort_key: sk.map(|s| s.to_vec()),
});
self
}
pub fn index(mut self, index_name: impl Into<String>) -> Self {
self.index_name = Some(index_name.into());
self
}
pub async fn execute(
self,
client: &mut KeystoneDbClient<Channel>,
) -> Result<RemoteQueryResponse> {
let request = proto::QueryRequest {
partition_key: self.partition_key,
sort_key_condition: self.sort_key_condition,
filter_expression: None,
expression_values: std::collections::HashMap::new(),
index_name: self.index_name,
limit: self.limit,
exclusive_start_key: self.exclusive_start_key,
scan_forward: self.scan_forward,
};
let response = client
.query(request)
.await?
.into_inner();
let items: Vec<Item> = response
.items
.into_iter()
.map(|proto_item| proto_item_to_ks(proto_item).expect("Invalid item from server"))
.collect();
let last_key = response.last_evaluated_key.map(|key| {
let (pk, sk) = proto_last_key_to_ks(key);
(pk, sk)
});
Ok(RemoteQueryResponse {
items,
count: response.count as usize,
scanned_count: response.scanned_count as usize,
last_key,
})
}
}
pub struct RemoteQueryResponse {
pub items: Vec<Item>,
pub count: usize,
pub last_key: Option<(Bytes, Option<Bytes>)>,
pub scanned_count: usize,
}
fn value_to_proto_bytes(bytes: &[u8]) -> proto::Value {
proto::Value {
value: Some(proto::value::Value::BinaryValue(bytes.to_vec())),
}
}