use crate::{Key, Item};
use bytes::Bytes;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortKeyCondition {
Equal,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
Between,
BeginsWith,
}
#[derive(Debug, Clone)]
pub struct QueryParams {
pub pk: Bytes,
pub sk_condition: Option<(SortKeyCondition, Bytes, Option<Bytes>)>,
pub forward: bool,
pub limit: Option<usize>,
pub start_key: Option<Key>,
pub index_name: Option<String>,
}
impl QueryParams {
pub fn new(pk: Bytes) -> Self {
Self {
pk,
sk_condition: None,
forward: true,
limit: None,
start_key: None,
index_name: None,
}
}
pub fn with_sk_condition(
mut self,
condition: SortKeyCondition,
value: Bytes,
value2: Option<Bytes>,
) -> Self {
self.sk_condition = Some((condition, value, value2));
self
}
pub fn with_direction(mut self, forward: bool) -> Self {
self.forward = forward;
self
}
pub fn with_limit(mut self, limit: usize) -> Self {
self.limit = Some(limit);
self
}
pub fn with_start_key(mut self, key: Key) -> Self {
self.start_key = Some(key);
self
}
pub fn with_index_name(mut self, index_name: impl Into<String>) -> Self {
self.index_name = Some(index_name.into());
self
}
pub fn matches_sk(&self, sk: &Option<Bytes>) -> bool {
match &self.sk_condition {
None => true, Some((condition, value, value2)) => {
let sk_bytes = match sk {
Some(b) => b,
None => return false, };
match condition {
SortKeyCondition::Equal => sk_bytes == value,
SortKeyCondition::LessThan => sk_bytes < value,
SortKeyCondition::LessThanOrEqual => sk_bytes <= value,
SortKeyCondition::GreaterThan => sk_bytes > value,
SortKeyCondition::GreaterThanOrEqual => sk_bytes >= value,
SortKeyCondition::Between => {
if let Some(v2) = value2 {
sk_bytes >= value && sk_bytes <= v2
} else {
false
}
}
SortKeyCondition::BeginsWith => sk_bytes.starts_with(value.as_ref()),
}
}
}
}
pub fn should_skip(&self, key: &Key) -> bool {
if let Some(start) = &self.start_key {
if self.forward {
key <= start
} else {
key >= start
}
} else {
false
}
}
}
#[derive(Debug, Clone)]
pub struct QueryResult {
pub items: Vec<Item>,
pub last_key: Option<Key>,
pub scanned_count: usize,
}
impl QueryResult {
pub fn new(items: Vec<Item>, last_key: Option<Key>, scanned_count: usize) -> Self {
Self {
items,
last_key,
scanned_count,
}
}
}
#[derive(Debug, Clone)]
pub struct ScanParams {
pub limit: Option<usize>,
pub start_key: Option<Key>,
pub segment: Option<usize>,
pub total_segments: Option<usize>,
}
impl ScanParams {
pub fn new() -> Self {
Self {
limit: None,
start_key: None,
segment: None,
total_segments: None,
}
}
pub fn with_limit(mut self, limit: usize) -> Self {
self.limit = Some(limit);
self
}
pub fn with_start_key(mut self, key: Key) -> Self {
self.start_key = Some(key);
self
}
pub fn with_segment(mut self, segment: usize, total_segments: usize) -> Self {
self.segment = Some(segment);
self.total_segments = Some(total_segments);
self
}
pub fn should_scan_stripe(&self, stripe_id: usize) -> bool {
match (self.segment, self.total_segments) {
(Some(seg), Some(total)) => {
stripe_id % total == seg
}
_ => true, }
}
pub fn should_skip(&self, key: &Key) -> bool {
if let Some(start) = &self.start_key {
key <= start
} else {
false
}
}
}
impl Default for ScanParams {
fn default() -> Self {
Self::new()
}
}
pub type ScanResult = QueryResult;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_query_params_sk_equal() {
let params = QueryParams::new(Bytes::from("pk1"))
.with_sk_condition(SortKeyCondition::Equal, Bytes::from("sk1"), None);
assert!(params.matches_sk(&Some(Bytes::from("sk1"))));
assert!(!params.matches_sk(&Some(Bytes::from("sk2"))));
assert!(!params.matches_sk(&None));
}
#[test]
fn test_query_params_sk_less_than() {
let params = QueryParams::new(Bytes::from("pk1"))
.with_sk_condition(SortKeyCondition::LessThan, Bytes::from("sk5"), None);
assert!(params.matches_sk(&Some(Bytes::from("sk1"))));
assert!(params.matches_sk(&Some(Bytes::from("sk4"))));
assert!(!params.matches_sk(&Some(Bytes::from("sk5"))));
assert!(!params.matches_sk(&Some(Bytes::from("sk6"))));
}
#[test]
fn test_query_params_sk_between() {
let params = QueryParams::new(Bytes::from("pk1")).with_sk_condition(
SortKeyCondition::Between,
Bytes::from("sk2"),
Some(Bytes::from("sk5")),
);
assert!(!params.matches_sk(&Some(Bytes::from("sk1"))));
assert!(params.matches_sk(&Some(Bytes::from("sk2"))));
assert!(params.matches_sk(&Some(Bytes::from("sk3"))));
assert!(params.matches_sk(&Some(Bytes::from("sk5"))));
assert!(!params.matches_sk(&Some(Bytes::from("sk6"))));
}
#[test]
fn test_query_params_sk_begins_with() {
let params = QueryParams::new(Bytes::from("pk1"))
.with_sk_condition(SortKeyCondition::BeginsWith, Bytes::from("user#"), None);
assert!(params.matches_sk(&Some(Bytes::from("user#123"))));
assert!(params.matches_sk(&Some(Bytes::from("user#456"))));
assert!(!params.matches_sk(&Some(Bytes::from("post#123"))));
assert!(!params.matches_sk(&Some(Bytes::from("user"))));
}
#[test]
fn test_query_params_no_condition() {
let params = QueryParams::new(Bytes::from("pk1"));
assert!(params.matches_sk(&Some(Bytes::from("sk1"))));
assert!(params.matches_sk(&Some(Bytes::from("anything"))));
assert!(params.matches_sk(&None));
}
#[test]
fn test_query_params_pagination_skip() {
let params = QueryParams::new(Bytes::from("pk1"))
.with_start_key(Key::with_sk(b"pk1".to_vec(), b"sk3".to_vec()))
.with_direction(true);
let key1 = Key::with_sk(b"pk1".to_vec(), b"sk1".to_vec());
let key2 = Key::with_sk(b"pk1".to_vec(), b"sk3".to_vec());
let key3 = Key::with_sk(b"pk1".to_vec(), b"sk5".to_vec());
assert!(params.should_skip(&key1)); assert!(params.should_skip(&key2)); assert!(!params.should_skip(&key3)); }
}