Skip to content

Commit

Permalink
support multi dimensional filter options to be filtered in group
Browse files Browse the repository at this point in the history
closes #15
  • Loading branch information
glendc committed May 23, 2024
1 parent 1defbbe commit 7a48b71
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 13 deletions.
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,48 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

# 0.5.0 (2024-05-23)

New Features:

- [[`#15`](https://github.com/plabayo/venndb/issues/15)]: support multi dimensional filter options to be filtered in group

Example:

```rust
use venndb::{Any, VennDB};

#[derive(Debug, VennDB)]
pub struct Value {
#[venndb(filter)]
pub foo: String,
pub bar: u32,
}

let db = ValueDB::from_iter([
Value {
foo: "a".to_owned(),
bar: 8,
},
Value {
foo: "b".to_owned(),
bar: 12,
},
Value {
foo: "c".to_owned(),
bar: 16,
},
].into_Iter()).unwrap();

let mut query = db.query();
query.foo(MyString("a".to_owned()));
query.foo(MyString("c".to_owned()));
let values: Vec<_> = query.execute().unwrap().iter().collect();
assert_eq!(values.len(), 2);
assert_eq!(values[0].bar, 8);
assert_eq!(values[0].bar, 16);
```

# 0.4.0 (2024-04-19)

Breaking Changes:
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ repository = "https://github.com/plabayo/venndb"
keywords = ["database", "db", "memory", "bits"]
categories = ["database"]
authors = ["Glen De Cauwsemaecker <[email protected]>"]
version = "0.4.0"
version = "0.5.0"
rust-version = "1.75.0"

[package.metadata.docs.rs]
Expand All @@ -23,7 +23,7 @@ rustdoc-args = ["--cfg", "docsrs"]
bitvec = "1.0.1"
hashbrown = "0.14.3"
rand = "0.8.5"
venndb-macros = { version = "0.4.0", path = "venndb-macros" }
venndb-macros = { version = "0.5.0", path = "venndb-macros" }

[dev-dependencies]
divan = "0.1.14"
Expand Down
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,47 @@ with a value of `foo` being more likely then other strings.
As such the only correct answer when filtering for _any_ value,
is to return rows that have _any_ value.

> ❓ How can I query on multiple variants of a "filter map" property?
Just call the _query_ method multiple times. It will allow you to match
rows that have either of these values.

Example

```rust,ignore
use venndb::{Any, VennDB};
#[derive(Debug, VennDB)]
pub struct Value {
#[venndb(filter)]
pub foo: String,
pub bar: u32,
}
let db = ValueDB::from_iter([
Value {
foo: "a".to_owned(),
bar: 8,
},
Value {
foo: "b".to_owned(),
bar: 12,
},
Value {
foo: "c".to_owned(),
bar: 16,
},
].into_Iter()).unwrap();
let mut query = db.query();
query.foo(MyString("a".to_owned()));
query.foo(MyString("c".to_owned()));
let values: Vec<_> = query.execute().unwrap().iter().collect();
assert_eq!(values.len(), 2);
assert_eq!(values[0].bar, 8);
assert_eq!(values[0].bar, 16);
```

## Example

Here follows an example demonstrating all the features of `VennDB`.
Expand Down Expand Up @@ -755,7 +796,7 @@ Query (e.g. `EmployeeInMemDBQuery`)
| `EmployeeInMemDBQuery::reset(&mut self) -> &mut Self` | reset the query, bringing it back to the clean state it has on creation |
| `EmployeeInMemDBQuery::execute(&self) -> Option<EmployeeInMemDBQueryResult<'a>>` | return the result of the query using the set filters. It will be `None` in case no rows matched the defined filters. Or put otherwise, the result will contain at least one row when `Some(_)` is returned. |
| `EmployeeInMemDBQuery::is_manager(&mut self, value: bool) -> &mut Self` | a filter setter for a `bool` filter. One such method per `bool` filter (that isn't `skip`ped) will be available. E.g. if you have ` foo` filter then there will be a `EmployeeInMemDBQuery:foo` method. For _bool_ filters that are optional (`Option<bool>`) this method is also generated just the same. |
| `EmployeeInMemDBQuery::department(&mut self, value: impl ::std::convert::Into<Department>) -> &mut Self` | a filter (map) setter for a non-`bool` filter. One such method per non-`bool` filter will be available. You can also `skip` these, but that's of course a bit pointless. The type will be equal to the actual field type. And the name will once again be equal to the original field name. Filter maps that have a `Option<T>` type have exactly the same signature. |
| `EmployeeInMemDBQuery::department(&mut self, value: impl ::std::convert::Into<Department>) -> &mut Self` | a filter (map) setter for a non-`bool` filter. One such method per non-`bool` filter will be available. You can also `skip` these, but that's of course a bit pointless. The type will be equal to the actual field type. And the name will once again be equal to the original field name. Filter maps that have a `Option<T>` type have exactly the same signature. Duering query you can call this method multiple times in case you wish to allow multiple variants. |

Query Result (e.g. `EmployeeInMemDBQueryResult`)

Expand Down
2 changes: 1 addition & 1 deletion venndb-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repository = "https://github.com/plabayo/venndb"
keywords = ["database", "db", "memory", "bits"]
categories = ["database", "db"]
authors = ["Glen De Cauwsemaecker <[email protected]>"]
version = "0.4.0"
version = "0.5.0"
rust-version = "1.75.0"

[package.metadata.docs.rs]
Expand Down
47 changes: 39 additions & 8 deletions venndb-macros/src/generate_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ fn generate_query_struct(
let name = field.name();
let ty = field.ty();
Some(quote! {
#name: Option<#ty>,
#name: Vec<#ty>,
})
}
FieldInfo::Key(_) => None,
Expand All @@ -646,7 +646,7 @@ fn generate_query_struct(
FieldInfo::FilterMap(field) => {
let name = field.name();
Some(quote! {
#name: None,
#name: Vec::new(),
})
}
FieldInfo::Key(_) => None,
Expand Down Expand Up @@ -724,7 +724,7 @@ fn generate_query_struct_impl(
Some(quote! {
#[doc=#doc]
#vis fn #name(&mut self, value: impl::std::convert::Into<#ty>) -> &mut Self {
self.#name = Some(value.into());
self.#name.push(value.into());
self
}
})
Expand All @@ -745,7 +745,7 @@ fn generate_query_struct_impl(
FieldInfo::FilterMap(field) => {
let name = field.name();
Some(quote! {
self.#name = None;
self.#name.clear();
})
}
FieldInfo::Key(_) => None,
Expand Down Expand Up @@ -775,7 +775,8 @@ fn generate_query_struct_impl(
let name = field.name();
let filter_map_name: Ident = field.filter_map_name();
let filter_vec_name: Ident = field.filter_vec_name();
let value_filter = match field.filter_any_name() {
// used if only one value is set, making it more efficient
let value_filter_one = match field.filter_any_name() {
Some(filter_any_vec) => quote! {
if ::venndb::Any::is_any(&value) {
filter &= &self.db.#filter_any_vec;
Expand All @@ -793,13 +794,43 @@ fn generate_query_struct_impl(
};
},
};
// used if multiple values are set, requires an extra alloc
let value_filter_multi = match field.filter_any_name() {
Some(filter_any_vec) => quote! {
if ::venndb::Any::is_any(&value) {
inter_filter |= &self.db.#filter_any_vec;
} else {
match self.db.#filter_map_name.get(value) {
Some(index) => inter_filter |= &self.db.#filter_vec_name[*index],
None => inter_filter |= &self.db.#filter_any_vec,
};
}
},
None => quote! {
match self.db.#filter_map_name.get(value) {
Some(index) => inter_filter |= &self.db.#filter_vec_name[*index],
None => return None,
};
},
};
// apply the filter
Some(quote! {
// Filter by the filterm ap below, only if it is defined as Some(_).
// If there is no filter matched to the given value then the search is over,
// and we early return None.

if let Some(value) = &self.#name {
#value_filter
match &self.#name.len() {
0 => (),
1 => {
let value = &self.#name[self.#name.len() - 1];
#value_filter_one
}
_ => {
let mut inter_filter = ::venndb::__internal::BitVec::repeat(false, self.db.rows.len());
for value in &self.#name {
#value_filter_multi
}
filter &= inter_filter;
}
}
})
}
Expand Down
85 changes: 85 additions & 0 deletions venndb-usage/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1351,3 +1351,88 @@ mod tests_v0_4 {
);
}
}

#[cfg(test)]
mod tests_v0_5 {
use super::*;

#[test]
fn test_employee_db_multi_filter_map_value() {
let db = EmployeeDB::from_rows(vec![
Employee {
id: 1,
name: "Alice".to_string(),
is_manager: true,
is_admin: false,
is_active: true,
department: Department::Engineering,
},
Employee {
id: 2,
name: "Bob".to_string(),
is_manager: false,
is_admin: false,
is_active: true,
department: Department::HR,
},
Employee {
id: 3,
name: "Charlie".to_string(),
is_manager: true,
is_admin: true,
is_active: true,
department: Department::Sales,
},
])
.unwrap();

let mut query = db.query();
query.department(Department::Engineering);
query.department(Department::HR);

let results = query.execute().unwrap().iter().collect::<Vec<_>>();
assert_eq!(results.len(), 2);
assert_eq!(results[0].id, 1);
assert_eq!(results[1].id, 2);
}

#[test]
fn test_employee_db_multi_filter_map_value_with_any() {
let db = EmployeeDB::from_rows(vec![
Employee {
id: 1,
name: "Alice".to_string(),
is_manager: true,
is_admin: false,
is_active: true,
department: Department::Engineering,
},
Employee {
id: 2,
name: "Bob".to_string(),
is_manager: false,
is_admin: false,
is_active: true,
department: Department::HR,
},
Employee {
id: 3,
name: "Charlie".to_string(),
is_manager: true,
is_admin: true,
is_active: true,
department: Department::Any,
},
])
.unwrap();

let mut query = db.query();
query.department(Department::Engineering);
query.department(Department::Any);

let results = query.execute().unwrap().iter().collect::<Vec<_>>();
assert_eq!(results.len(), 2);
assert_eq!(results[0].id, 1);
assert_eq!(results[1].id, 3);
}
}
3 changes: 2 additions & 1 deletion venndb-usage/tests/compiles/derive_struct_all_the_things.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ fn main() {
query
.is_manager(true)
.is_admin(true)
.department(Department::Engineering);
.department(Department::Engineering)
.department(Department::Sales);
assert!(query.execute().is_none());
}

0 comments on commit 7a48b71

Please sign in to comment.