I'm trying to use a BTreeMap as an index in an in-memory database, with multiple keys. Something like this:
let mut map = BTreeMap::new();
map.insert(("a".to_string(), "x".to_string()), "ax".to_string());
map.insert(("a".to_string(), "y".to_string()), "ay".to_string());
Now my question is, what would be the best way to actually query this? Say for instance I'd like to get ("a", *), i.e. all entries with "a" as the first key and anything as second.
I tried something like this:
use std::{collections::{BTreeMap}, cmp::Ordering};
use std::ops::Bound::{Included, Unbounded};
#[derive(Clone, Debug, Hash)]
pub enum StringKey {
Exact(String),
Any,
}
impl PartialOrd for StringKey {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(StringKey::Exact(a), StringKey::Exact(b)) => Some(a.cmp(b)),
(StringKey::Exact(_), StringKey::Any) => Some(Ordering::Equal),
(StringKey::Any, StringKey::Exact(_)) => Some(Ordering::Equal),
(StringKey::Any, StringKey::Any) => Some(Ordering::Equal),
}
}
}
impl Ord for StringKey {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
impl PartialEq for StringKey {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Exact(a), Self::Exact(b)) => a == b,
(Self::Exact(_), Self::Any) => true,
(Self::Any, Self::Exact(_)) => true,
(Self::Any, Self::Any) => true,
}
}
}
impl Eq for StringKey {
}
fn main() {
let mut map = BTreeMap::new();
map.insert((StringKey::Exact("a".to_string()), StringKey::Exact("x".to_string())), "ax".to_string());
map.insert((StringKey::Exact("a".to_string()), StringKey::Exact("y".to_string())), "ay".to_string());
map.insert((StringKey::Exact("b".to_string()), StringKey::Exact("x".to_string())), "bx".to_string());
let query = (StringKey::Exact("a".to_string()), StringKey::Any);
// Would be easier to do (Included(query), Included(query)), but that only returns one item
let iter = map.range((Included(query.clone()), Unbounded));
for (key, value) in iter {
if key > &query {
return;
}
println!("{:?} {:?}", key, value);
}
}
Which prints
(Exact("a"), Exact("x")) "ax"
(Exact("a"), Exact("y")) "ay"
So that works, but the problem is just that I'm violating the Ord/Eq rules here (things like StringKey::Exact("a") == StringKey::Any == StringKey::Exact("b")), which doesn't seem like the right way to go about it. It's also a problem since I'd like to store the StringKey in HashMaps in other parts of the code but that doesn't really work when Eq is implemented like this.
So, again; is there a better way to do this?
Thankful for any help!