Skip to content

Commit

Permalink
feat: Path Query Bincode Serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
QuantumExplorer committed Aug 21, 2024
1 parent 713ad96 commit 27067ef
Show file tree
Hide file tree
Showing 4 changed files with 549 additions and 4 deletions.
269 changes: 266 additions & 3 deletions grovedb/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::{
fmt,
};

use bincode::{Decode, Encode};
#[cfg(any(feature = "full", feature = "verify"))]
use grovedb_merk::proofs::query::query_item::QueryItem;
use grovedb_merk::proofs::query::{Key, SubqueryBranch};
Expand All @@ -21,14 +22,13 @@ use crate::query_result_type::PathKey;
use crate::Error;

#[cfg(any(feature = "full", feature = "verify"))]
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Encode, Decode)]
/// Path query
///
/// Represents a path to a specific GroveDB tree and a corresponding query to
/// apply to the given tree.
pub struct PathQuery {
/// Path
// TODO: Make generic over path type
pub path: Vec<Vec<u8>>,
/// Query
pub query: SizedQuery,
Expand All @@ -49,7 +49,7 @@ impl fmt::Display for PathQuery {
}

#[cfg(any(feature = "full", feature = "verify"))]
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Encode, Decode)]
/// Holds a query to apply to a tree and an optional limit/offset value.
/// Limit and offset values affect the size of the result set.
pub struct SizedQuery {
Expand Down Expand Up @@ -585,6 +585,7 @@ impl<'a> SinglePathSubquery<'a> {
mod tests {
use std::{borrow::Cow, ops::RangeFull};

use bincode::{config::standard, decode_from_slice, encode_to_vec};
use grovedb_merk::proofs::{
query::{query_item::QueryItem, SubqueryBranch},
Query,
Expand Down Expand Up @@ -1753,4 +1754,266 @@ mod tests {

assert_eq!(query.max_depth(), None);
}

#[test]
fn test_simple_path_query_serialization() {
let path_query = PathQuery {
path: vec![b"root".to_vec(), b"subtree".to_vec()],
query: SizedQuery {
query: Query {
items: vec![QueryItem::Key(b"key1".to_vec())],
default_subquery_branch: SubqueryBranch::default(),
conditional_subquery_branches: None,
left_to_right: true,
},
limit: None,
offset: None,
},
};

let encoded = encode_to_vec(&path_query, standard()).unwrap();
let decoded: PathQuery = decode_from_slice(&encoded, standard()).unwrap().0;

assert_eq!(path_query, decoded);
}

#[test]
fn test_range_query_serialization() {
let path_query = PathQuery {
path: vec![b"root".to_vec()],
query: SizedQuery {
query: Query {
items: vec![QueryItem::Range(b"a".to_vec()..b"z".to_vec())],
default_subquery_branch: SubqueryBranch::default(),
conditional_subquery_branches: None,
left_to_right: false,
},
limit: Some(10),
offset: Some(2),
},
};

let encoded = encode_to_vec(&path_query, standard()).unwrap();
let decoded: PathQuery = decode_from_slice(&encoded, standard()).unwrap().0;

assert_eq!(path_query, decoded);
}

#[test]
fn test_range_inclusive_query_serialization() {
let path_query = PathQuery {
path: vec![b"root".to_vec()],
query: SizedQuery {
query: Query {
items: vec![QueryItem::RangeInclusive(b"a".to_vec()..=b"z".to_vec())],
default_subquery_branch: SubqueryBranch::default(),
conditional_subquery_branches: None,
left_to_right: true,
},
limit: Some(5),
offset: None,
},
};

let encoded = encode_to_vec(&path_query, standard()).unwrap();
let decoded: PathQuery = decode_from_slice(&encoded, standard()).unwrap().0;

assert_eq!(path_query, decoded);
}

#[test]
fn test_conditional_subquery_serialization() {
let mut conditional_branches = IndexMap::new();
conditional_branches.insert(
QueryItem::Key(b"key1".to_vec()),
SubqueryBranch {
subquery_path: Some(vec![b"conditional_path".to_vec()]),
subquery: Some(Box::new(Query::default())),
},
);

let path_query = PathQuery {
path: vec![b"root".to_vec()],
query: SizedQuery {
query: Query {
items: vec![QueryItem::Key(b"key1".to_vec())],
default_subquery_branch: SubqueryBranch::default(),
conditional_subquery_branches: Some(conditional_branches),
left_to_right: true,
},
limit: None,
offset: None,
},
};

let encoded = encode_to_vec(&path_query, standard()).unwrap();
let decoded: PathQuery = decode_from_slice(&encoded, standard()).unwrap().0;

assert_eq!(path_query, decoded);
}

#[test]
fn test_empty_path_query_serialization() {
let path_query = PathQuery {
path: vec![],
query: SizedQuery {
query: Query::default(),
limit: None,
offset: None,
},
};

let encoded = encode_to_vec(&path_query, standard()).unwrap();
let decoded: PathQuery = decode_from_slice(&encoded, standard()).unwrap().0;

assert_eq!(path_query, decoded);
}

#[test]
fn test_path_query_with_multiple_keys() {
let path_query = PathQuery {
path: vec![b"root".to_vec()],
query: SizedQuery {
query: Query {
items: vec![
QueryItem::Key(b"key1".to_vec()),
QueryItem::Key(b"key2".to_vec()),
QueryItem::Key(b"key3".to_vec()),
],
default_subquery_branch: SubqueryBranch::default(),
conditional_subquery_branches: None,
left_to_right: true,
},
limit: None,
offset: None,
},
};

let encoded = encode_to_vec(&path_query, standard()).unwrap();
let decoded: PathQuery = decode_from_slice(&encoded, standard()).unwrap().0;

assert_eq!(path_query, decoded);
}

#[test]
fn test_path_query_with_full_range() {
let path_query = PathQuery {
path: vec![b"root".to_vec()],
query: SizedQuery {
query: Query {
items: vec![QueryItem::RangeFull(RangeFull)],
default_subquery_branch: SubqueryBranch::default(),
conditional_subquery_branches: None,
left_to_right: false,
},
limit: Some(100),
offset: Some(10),
},
};

let encoded = encode_to_vec(&path_query, standard()).unwrap();
let decoded: PathQuery = decode_from_slice(&encoded, standard()).unwrap().0;

assert_eq!(path_query, decoded);
}

#[test]
fn test_path_query_with_complex_conditions() {
let mut conditional_branches = IndexMap::new();
conditional_branches.insert(
QueryItem::Key(b"key1".to_vec()),
SubqueryBranch {
subquery_path: Some(vec![b"conditional_path1".to_vec()]),
subquery: Some(Box::new(Query {
items: vec![QueryItem::Range(b"a".to_vec()..b"m".to_vec())],
default_subquery_branch: SubqueryBranch::default(),
conditional_subquery_branches: None,
left_to_right: true,
})),
},
);
conditional_branches.insert(
QueryItem::Range(b"n".to_vec()..b"z".to_vec()),
SubqueryBranch {
subquery_path: Some(vec![b"conditional_path2".to_vec()]),
subquery: Some(Box::new(Query {
items: vec![QueryItem::Key(b"key2".to_vec())],
default_subquery_branch: SubqueryBranch::default(),
conditional_subquery_branches: None,
left_to_right: false,
})),
},
);

let path_query = PathQuery {
path: vec![b"root".to_vec()],
query: SizedQuery {
query: Query {
items: vec![QueryItem::Key(b"key3".to_vec())],
default_subquery_branch: SubqueryBranch::default(),
conditional_subquery_branches: Some(conditional_branches),
left_to_right: true,
},
limit: Some(50),
offset: Some(5),
},
};

let encoded = encode_to_vec(&path_query, standard()).unwrap();
let decoded: PathQuery = decode_from_slice(&encoded, standard()).unwrap().0;

assert_eq!(path_query, decoded);
}

#[test]
fn test_path_query_with_subquery_path() {
let path_query = PathQuery {
path: vec![b"root".to_vec()],
query: SizedQuery {
query: Query {
items: vec![QueryItem::Key(b"key1".to_vec())],
default_subquery_branch: SubqueryBranch {
subquery_path: Some(vec![b"subtree_path".to_vec()]),
subquery: Some(Box::new(Query {
items: vec![QueryItem::Key(b"key2".to_vec())],
default_subquery_branch: SubqueryBranch::default(),
conditional_subquery_branches: None,
left_to_right: true,
})),
},
conditional_subquery_branches: None,
left_to_right: true,
},
limit: None,
offset: None,
},
};

let encoded = encode_to_vec(&path_query, standard()).unwrap();
let decoded: PathQuery = decode_from_slice(&encoded, standard()).unwrap().0;

assert_eq!(path_query, decoded);
}

#[test]
fn test_path_query_with_empty_query_items() {
let path_query = PathQuery {
path: vec![b"root".to_vec()],
query: SizedQuery {
query: Query {
items: vec![], // No items in the query
default_subquery_branch: SubqueryBranch::default(),
conditional_subquery_branches: None,
left_to_right: true,
},
limit: Some(20),
offset: None,
},
};

let encoded = encode_to_vec(&path_query, standard()).unwrap();
let decoded: PathQuery = decode_from_slice(&encoded, standard()).unwrap().0;

assert_eq!(path_query, decoded);
}
}
1 change: 1 addition & 0 deletions merk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ documentation = "https://docs.rs/grovedb-merk"

[dependencies]
thiserror = "1.0.58"
bincode = { version = "2.0.0-rc.3" }
grovedb-storage = { version = "1.0.0", path = "../storage", optional = true }
failure = "0.1.8"
integer-encoding = "4.0.0"
Expand Down
Loading

0 comments on commit 27067ef

Please sign in to comment.