So here is a real-world example. I used to do this: Notice I get to use SQL to update all the relays in the table.
pub async fn clear_all_relay_list_usage_bits() -> Result<(), Error> {
// Keep only bits which are NOT part of relay lists
let sql = format!(
"UPDATE relay SET usage_bits = usage_bits & {}",
!(Self::INBOX | Self::OUTBOX)
);
spawn_blocking(move || {
let db = GLOBALS.db.blocking_lock();
let mut stmt = db.prepare(&sql)?;
rtry!(stmt.execute(()));
Ok::<(), Error>(())
})
.await??;
Ok(())
}
Now I have it like this
pub fn clear_all_relay_list_usage_bits() -> Result<(), Error> {
GLOBALS.storage.modify_all_relays(|relay| {
relay.usage_bits &= Self::INBOX | Self::OUTBOX;
})
}
pub fn modify_all_relays(&self, mut modify: M) -> Result<(), Error>
where
M: FnMut(&mut DbRelay),
{
let mut txn = self.env.begin_rw_txn()?;
let mut cursor = txn.open_rw_cursor(self.relays)?;
let mut iter = cursor.iter_start();
while let Some(result) = iter.next() {
match result {
Err(e) => return Err(e.into()),
Ok((key, val)) => {
let mut dbrelay: DbRelay = serde_json::from_slice(val)?;
modify(&mut dbrelay);
let bytes = serde_json::to_vec(&dbrelay)?;
cursor.put(&key, &bytes, WriteFlags::empty())?;
}
}
}
drop(cursor);
txn.commit()?;
Ok(())
}
(the serde_json serialization is not ideal, but easier since my relay objects have arbitrary JSON content due to containing the NIP-11)