Skip to content

Commit

Permalink
[db] Add convenience methods to DbValue #655 (#656)
Browse files Browse the repository at this point in the history
* add to_u64 and to_i64

* add to_f64

* Update db_value.rs

* Update db_f64.rs

* Update db_value.rs

* Update db_value.rs

* Update queries.md
  • Loading branch information
michaelvlach authored Aug 9, 2023
1 parent 50e1427 commit d540501
Show file tree
Hide file tree
Showing 6 changed files with 816 additions and 149 deletions.
34 changes: 26 additions & 8 deletions docs/queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,36 @@ The `DbKey` is an alias of `DbValue` and the value itself is an enum of valid ty
```Rust
pub enum DbValue {
Bytes(Vec<u8>),
Int(i64),
Uint(u64),
Float(DbFloat),
I64(i64),
U64(u64),
F64(DbF64),
String(String),
VecInt(Vec<i64>),
VecUint(Vec<u64>),
VecFloat(Vec<DbFloat>),
VecI64(Vec<i64>),
VecU64(Vec<u64>),
VecF64(Vec<DbF64>),
VecString(Vec<String>),
}
```

Note the `DbFloat` type (i.e. `pub struct DbFloat(f64)`) which is a convenient wrapper of `f64` to provide opinionated implementation of some of the operations that are not floating type friendly like comparisons. In `agdb` the float type is using [`total_cmp` standard library function](https://doc.rust-lang.org/std/primitive.f64.html#method.total_cmp). Please see its documentation for important details about possible limits or issues on certain platforms.
Note the `DbF64` type (i.e. `pub struct DbF64(f64)`) which is a convenient wrapper of `f64` to provide opinionated implementation of some of the operations that are not floating type friendly like comparisons. In `agdb` the float type is using [`total_cmp` standard library function](https://doc.rust-lang.org/std/primitive.f64.html#method.total_cmp). Please see its documentation for important details about possible limits or issues on certain platforms.

The enum variants can be conveniently accessed through methods named after each variant:

```Rust
fn bytes(&self) -> Result<&Vec<u8>, DbError>;
fn to_f64(&self) -> Result<DbF64, DbError>;
fn to_i64(&self) -> Result<i64, DbError>;
fn to_u64(&self) -> Result<u64, DbError>;
fn to_string(&self) -> String;
fn string(&self) -> Result<&String, DbError>;
fn vec_f64(&self) -> Result<&Vec<DbF64>, DbError>;
fn vec_i64(&self) -> Result<&Vec<i64>, DbError>;
fn vec_u64(&self) -> Result<&Vec<u64>, DbError>;
fn vec_string(&self) -> Result<&Vec<String>, DbError>;

```

The numerical variants (`I64`, `U64`, `DbF64`) will attempt loss-less conversions where possible. To avoid copies all other variants return `&` where conversions are not possible even if they could be done in theory. The special case is `to_string()` provided by the `Display` trait. It converts any values into string (it also copies the `String` variant) and performs possibly lossy conversion from `Bytes` to UTF-8 string.

# QueryError

Expand Down Expand Up @@ -461,7 +479,7 @@ Selects elements identified by `ids` [`QueryIds`](#queryids--queryid) or search
The result will contain:

- number of returned elements
- list of elements with only keys and default (empty `Int(0)` values)
- list of elements with only keys and default (empty `I64(0)` values)

### Select key count

Expand Down
2 changes: 1 addition & 1 deletion src/agdb/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub mod db_key;
pub mod db_key_value;
pub mod db_value;

mod db_float;
mod db_f64;
mod db_search_handlers;
mod db_value_index;

Expand Down
70 changes: 36 additions & 34 deletions src/agdb/db/db_float.rs → src/agdb/db/db_f64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,56 +11,57 @@ use std::hash::Hasher;
/// [docs](https://doc.rust-lang.org/std/primitive.f64.html#method.total_cmp)
/// to understand how it handles NaNs and other edge cases
/// of floating point numbers.
#[derive(Clone, Debug)]
pub struct DbFloat(f64);
#[derive(Clone, Copy, Debug)]
pub struct DbF64(f64);

impl DbFloat {
impl DbF64 {
#[allow(clippy::wrong_self_convention)]
pub fn to_f64(&self) -> f64 {
self.0
}
}

impl Eq for DbFloat {}
impl Eq for DbF64 {}

impl Hash for DbFloat {
impl Hash for DbF64 {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_bits().hash(state)
}
}

impl Ord for DbFloat {
impl Ord for DbF64 {
fn cmp(&self, other: &Self) -> Ordering {
self.0.total_cmp(&other.0)
}
}

impl PartialEq for DbFloat {
impl PartialEq for DbF64 {
fn eq(&self, other: &Self) -> bool {
self.0.total_cmp(&other.0) == Ordering::Equal
}
}

impl PartialOrd for DbFloat {
impl PartialOrd for DbF64 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.0.total_cmp(&other.0))
}
}

impl From<f32> for DbFloat {
impl From<f32> for DbF64 {
fn from(value: f32) -> Self {
DbFloat(value.into())
DbF64(value.into())
}
}

impl From<f64> for DbFloat {
impl From<f64> for DbF64 {
fn from(value: f64) -> Self {
DbFloat(value)
DbF64(value)
}
}

impl Serialize for DbFloat {
impl Serialize for DbF64 {
fn deserialize(bytes: &[u8]) -> Result<Self, DbError> {
Ok(DbFloat::from(f64::deserialize(bytes)?))
Ok(DbF64::from(f64::deserialize(bytes)?))
}

fn serialize(&self) -> Vec<u8> {
Expand All @@ -72,7 +73,7 @@ impl Serialize for DbFloat {
}
}

impl StableHash for DbFloat {
impl StableHash for DbF64 {
fn stable_hash(&self) -> u64 {
self.0.to_bits().stable_hash()
}
Expand All @@ -85,85 +86,86 @@ mod tests {
use std::collections::HashSet;

#[test]
#[allow(clippy::clone_on_copy)]
fn derived_from_clone() {
let float = DbFloat::from(1.0_f64);
let float = DbF64::from(1.0_f64);
let _other = float.clone();
let _other2 = float;
}
#[test]
fn derived_from_debug() {
format!("{:?}", DbFloat::from(1.0_f64));
format!("{:?}", DbF64::from(1.0_f64));
}

#[test]
fn derived_from_eq() {
let float = DbFloat::from(1.0_f64);
let other = DbFloat::from(1.0_f64);
let float = DbF64::from(1.0_f64);
let other = DbF64::from(1.0_f64);

assert_eq!(float, other);
}

#[test]
fn derived_from_hash() {
let mut set = HashSet::<DbFloat>::new();
set.insert(DbFloat::from(1.0_f64));
let mut set = HashSet::<DbF64>::new();
set.insert(DbF64::from(1.0_f64));
}

#[test]
fn derived_from_ord() {
assert_eq!(DbFloat::from(1.0).cmp(&DbFloat::from(1.0)), Ordering::Equal);
assert_eq!(DbF64::from(1.0).cmp(&DbF64::from(1.0)), Ordering::Equal);
}

#[test]
fn derived_from_partial_ord() {
let mut vec = vec![
DbFloat::from(1.1_f64),
DbFloat::from(1.3_f64),
DbFloat::from(-3.333_f64),
DbF64::from(1.1_f64),
DbF64::from(1.3_f64),
DbF64::from(-3.333_f64),
];
vec.sort();

assert_eq!(
vec,
vec![
DbFloat::from(-3.333_f64),
DbFloat::from(1.1_f64),
DbFloat::from(1.3_f64)
DbF64::from(-3.333_f64),
DbF64::from(1.1_f64),
DbF64::from(1.3_f64)
]
);
}

#[test]
fn from_f32() {
let _ = DbFloat::from(1.0_f32);
let _ = DbF64::from(1.0_f32);
}

#[test]
fn from_f64() {
let _ = DbFloat::from(1.0_f64);
let _ = DbF64::from(1.0_f64);
}

#[test]
fn serialize() {
let float = DbFloat::from(0.1_f64 + 0.2_f64);
let float = DbF64::from(0.1_f64 + 0.2_f64);
let bytes = float.serialize();

assert_eq!(bytes.len() as u64, float.serialized_size());

let actual = DbFloat::deserialize(&bytes).unwrap();
let actual = DbF64::deserialize(&bytes).unwrap();

assert_eq!(float, actual);
}

#[test]
fn stable_hash() {
let hash = DbFloat::from(1.0_f64).stable_hash();
let hash = DbF64::from(1.0_f64).stable_hash();

assert_ne!(hash, 0);
}

#[test]
fn to_f64() {
let _to_f64 = DbFloat::from(1.0_f64).to_f64();
let _to_f64 = DbF64::from(1.0_f64).to_f64();
}
}
12 changes: 6 additions & 6 deletions src/agdb/db/db_key_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,21 @@ mod tests {
format!(
"{:?}",
DbKeyValue {
key: DbKey::Int(0),
value: DbKey::Int(0)
key: DbKey::I64(0),
value: DbKey::I64(0)
}
);
}
#[test]
fn derived_from_partial_eq() {
assert_eq!(
DbKeyValue {
key: DbKey::Int(0),
value: DbKey::Int(0)
key: DbKey::I64(0),
value: DbKey::I64(0)
},
DbKeyValue {
key: DbKey::Int(0),
value: DbKey::Int(0)
key: DbKey::I64(0),
value: DbKey::I64(0)
}
);
}
Expand Down
Loading

0 comments on commit d540501

Please sign in to comment.