better docs

This commit is contained in:
bert hubert 2018-12-10 17:42:25 +01:00
parent f15076bc2b
commit 34f8c2188c
4 changed files with 81 additions and 15 deletions

View File

@ -40,7 +40,7 @@ MDB handles are all available should you want to use functionality we did
not (yet) cater for.
# Status
Very early. If using this tiny library, be aware things might change
Fresh. If using this tiny library, be aware things might change
rapidly. To use, add `lmdb-safe.cc` and `lmdb-safe.hh` to your project.
# Philosophy
@ -110,6 +110,67 @@ available for other threads and processes.
A slightly expanded version of this code can be found in
[basic-example.cc](basic-example.cc).
# Input and output of values
The basic data unit of LMDB is `MDB_val` which describes a slab of memory.
Within LMDB, `MDB_val` is used for both input and output. For safety
purposes, in this library we split this up into `MDBInValue` and
`MDBOutValue`. Once split, we can add some very convenient semantics to these
classes.
For example, to store `double` values for 64 bit IDs:
```
auto txn = env->getRWTransaction();
uint64_t id=12345678901;
double score=3.14159;
txn.put(dbi, id, score);
txn.commit();
```
Behind the scenes, the `id` and `score` values are wrapped by `MDBInVal`
which converts these values into byte strings. To retrieve thise values
works similary:
```
auto txn = env->getRWTransaction();
uint64_t id=12345678901;
MDBOutValue val;
txn.get(dbi, id, val);
cout << "Score: " << val.get<double>() << "\n";
```
Note that on retrieval, we have to specify the type of the value stored.
This allows the conversion back from a byte string into the native type.
`MDBOutValue` also tests if the length of the data matches the type.
## Details
The automatic conversion to and from the `MDBVal`s is implemented strictly
for:
* Integer and floating point types
* std::string
* std::string_view
However, if you explicitly ask for it, it is also possible to serialize
`struct`s:
```
struct Coordinate
{
double x,y;
};
C c{12.0, 13.0};
txn.put(dbi, MDBInVal::fromStruct(c), 12.0);
MDBOutVal res;
txn.get(dbi, MDBInVal::fromStruct(c), res);
auto c1 = res.get_struct<Coordinate>();
```
# Cursors, transactions
This example shows how to use cursors and how to mix `lmdb-safe` with direct
@ -127,7 +188,7 @@ This is the usual opening sequence.
```
auto cursor=txn.getCursor(dbi);
MDB_val key, data;
MDBOutVal key, data;
int count=0;
cout<<"Counting records.. "; cout.flush();
while(!cursor.get(key, data, count ? MDB_NEXT : MDB_FIRST)) {
@ -157,7 +218,7 @@ native `mdb_drop` function which we did not wrap. This is possible because
```
cout << "Adding "<<limit<<" values .. "; cout.flush();
for(unsigned int n = 0 ; n < limit; ++n) {
txn.put(dbi, MDBVal(n), MDBVal(n));
txn.put(dbi, n, n);
}
cout <<"Done!"<<endl;
cout <<"Calling commit.. "; cout.flush();
@ -165,9 +226,8 @@ native `mdb_drop` function which we did not wrap. This is possible because
cout<<"Done!"<<endl;
```
Here we add 20 million values using the `MDBVal` wrapper which converts our
unsigned integer into an `MDB_val`. We then commit the `mdb_drop` and the 20
million puts. All this happened in less than 20 seconds.
Here we add 20 million value & then commit the `mdb_drop` and the 20 million
puts. All this happened in less than 20 seconds.
Had we created our database with the `MDB_INTEGERKEY` option and added the
`MDB_APPEND` flag to `txn.put`, the whole process would have taken around 5

View File

@ -313,6 +313,7 @@ public:
mdb_cursor_close(d_cursor);
}
int get(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op)
{
return mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op);
@ -511,6 +512,13 @@ public:
return rc;
}
int find(const MDBInVal& in, MDBOutVal& key, MDBOutVal& data)
{
key.d_mdbval = in.d_mdbval;
return mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, MDB_SET);
}
int put(const MDBOutVal& key, const MDBOutVal& data, int flags=0)
{
return mdb_cursor_put(d_cursor,

View File

@ -26,15 +26,10 @@ int main()
txn = env->getRWTransaction();
auto cursor = txn.getCursor(dbi);
int count=0;
MDBOutVal key, data;
MDBInVal start("lmdb");
key.d_mdbval = start.d_mdbval;
int rc=0;
while(!(rc=cursor.get(key, data, count ? MDB_NEXT_DUP : MDB_SET))) {
cout << key.get<string_view>() << " = " << data.get<string_view>() <<endl;
++count;
for(int rc = cursor.find("lmdb", key, data); !rc; rc = cursor.get(key, data, MDB_NEXT_DUP)) {
cout << key.get<string_view>() << " = " << data.get<string_view>() <<endl;
}
}

View File

@ -20,7 +20,7 @@ struct MDBVal
int main(int argc, char** argv)
{
auto env = getMDBEnv("./database", 0, 0600);
auto dbi = env->openDB("huge", MDB_CREATE | MDB_INTEGERKEY);
auto dbi = env->openDB(0, MDB_CREATE | MDB_INTEGERKEY);
auto txn = env->getRWTransaction();
unsigned int limit=20000000;
@ -32,6 +32,9 @@ int main(int argc, char** argv)
MDBOutVal key, data;
int count=0;
while(!cursor.get(key, data, count ? MDB_NEXT : MDB_FIRST)) {
auto d = data.get<unsigned long>();
if(d==17)
cout <<"Got 17!"<<endl;
count++;
}
cout<<"Have "<<count<<"!"<<endl;
@ -41,7 +44,7 @@ int main(int argc, char** argv)
cout<<"Done!"<<endl;
cout << "Adding "<<limit<<" values .. "; cout.flush();
for(unsigned int n = 0 ; n < limit; ++n) {
for(unsigned long n = 0 ; n < limit; ++n) {
txn.put(dbi, n, n, MDB_APPEND);
}
cout <<"Done!"<<endl;