use foundationdb::tuple::{unpack, Subspace, TuplePack};
use foundationdb::{Database, FdbResult, RangeOption, Transaction};
use std::fmt::{Display, Formatter};
#[derive(Default)]
struct User {
id: String,
name: String,
zipcode: String,
}
impl Display for User {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"id => '{}', zipcode => '{}', name => '{}'",
self.id, self.zipcode, self.name
)
}
}
async fn clear_subspaces(db: &Database, subspaces: &Vec<Subspace>) {
let transaction = db.create_trx().expect("Failed to create transaction");
for subspace in subspaces {
transaction.clear_subspace_range(subspace);
}
transaction
.commit()
.await
.expect("Unable to commit transaction");
}
async fn create_user(
transaction: &Transaction,
id: &str,
zipcode: &str,
name: &str,
user_subspace: &Subspace,
zipcode_index_subspace: &Subspace,
) {
let key = user_subspace.pack(&(id, zipcode));
let index = zipcode_index_subspace.pack(&(zipcode, id));
transaction.set(&key, &name.pack_to_vec());
transaction.set(&index, &[]);
}
async fn get_user(
user_subspace: &Subspace,
transaction: &Transaction,
user_id: &str,
zipcode: &str,
) -> FdbResult<User> {
let key = user_subspace.pack(&(user_id, zipcode));
let user = transaction
.get(&key, false)
.await?
.expect("Could not found a row");
let name: String = unpack(&user.to_vec()).expect("Could not unpack");
Ok(User {
id: user_id.to_string(),
zipcode: zipcode.to_string(),
name,
})
}
async fn search_user_by_zipcode(
db: &Database,
user_subspace: &Subspace,
zipcode_index: &Subspace,
zipcode: &str,
) -> Option<Vec<User>> {
let transaction = db.create_trx().expect("Unable to create transaction");
let begin = zipcode_index.pack(&(zipcode));
let mut end = zipcode_index.pack(&(zipcode));
end.pop();
end.push(255_u8);
let range = RangeOption::from((begin, end));
let result_get_index = &transaction.get_range(&range, 1, false).await;
let mut users = vec![];
match result_get_index {
Ok(results) => {
for result in results {
let (zipcode, id): (String, String) = zipcode_index
.unpack(result.key())
.expect("Unable to unpack value from index");
let result_user = get_user(user_subspace, &transaction, &id, &zipcode).await;
if let Ok(user) = result_user {
users.push(user);
}
}
Some(users)
}
Err(_err) => None,
}
}
#[tokio::main]
async fn main() {
let _guard = unsafe { foundationdb::boot() };
let db = Database::new_compat(None)
.await
.expect("failed to get database");
let user_subspace = Subspace::from_bytes("user");
let zipcode_index_subspace = Subspace::from_bytes("zipcode_index");
clear_subspaces(
&db,
&vec![user_subspace.clone(), zipcode_index_subspace.clone()],
)
.await;
populate_users(&db, &user_subspace, &zipcode_index_subspace)
.await
.expect("Unable to populate database");
let users = search_user_by_zipcode(&db, &user_subspace, &zipcode_index_subspace, "205").await;
if let Some(users) = users {
users.iter().for_each(|user| println!("{}", user));
}
}
async fn populate_users(
db: &Database,
user_subspace: &Subspace,
zipcode_index_subspace: &Subspace,
) -> FdbResult<()> {
let transaction = db.create_trx().expect("Failed to create transaction");
create_user(
&transaction,
"001",
"20500",
"Barack",
user_subspace,
zipcode_index_subspace,
)
.await;
create_user(
&transaction,
"002",
"20500",
"Michelle",
user_subspace,
zipcode_index_subspace,
)
.await;
create_user(
&transaction,
"003",
"20500",
"Sasha",
user_subspace,
zipcode_index_subspace,
)
.await;
create_user(
&transaction,
"004",
"20500",
"Malia",
user_subspace,
zipcode_index_subspace,
)
.await;
create_user(
&transaction,
"005",
"20500",
"Bo",
user_subspace,
zipcode_index_subspace,
)
.await;
create_user(
&transaction,
"101",
"SW1A 1AA",
"Elizabeth",
user_subspace,
zipcode_index_subspace,
)
.await;
create_user(
&transaction,
"102",
"SW1A 1AA",
"Philip",
user_subspace,
zipcode_index_subspace,
)
.await;
transaction.commit().await?;
Ok(())
}