meilisearch / heed

A fully typed LMDB wrapper with minimum overhead 🐦
https://docs.rs/heed
MIT License
519 stars 52 forks source link

Add `Database::get_or_put`: insert if value does not exist, otherwise return previous value #252

Closed nolanderc closed 3 months ago

nolanderc commented 3 months ago

Pull Request

Related issue

N/A Based on #251

What does this PR do?

which can now be written as (with one fewer lookups):

if let Some(value) = db.get_or_put(&mut txn, key, new_value) {
    // ...
}

One drawback of the above is that it would serialize/allocate the new_value even if it would not end up being inserted. This can be avoided using the get_or_put_reserved* methods, mirroring the put_reserved* method.

PR checklist

Please check if your PR fulfills the following requirements:

Thank you so much for contributing to Meilisearch!

nolanderc commented 3 months ago

However, I am wondering if I don't want to write a system similar to the HashMap::entry method, at some point 🤔 What do you think?

This could probably be done using LMDB's cursor API. Looking at the source, there is an optimization in case a cursor is already positioned on the correct data item for an insert. Thus, an entry-API could efficiently be implemented as (pseudocode):

mdb_cursor_get(&mut cursor, &mut key, &mut data, MDB_SET);
if data.is_none() {
    // ... encode value into `data` ...
    mdb_cursor_put(&mut cursor, &mut key, &mut data);
}
Kerollmops commented 3 months ago

You are absolutely right, and I already worked on a cursor wrapper in the cursor.rs file, It can maybe be improved 🤔 Thank you for your changes 👍