Introduction
LevelDBWinRT
is a Windows Runtime component which enables you to use LevelDB
in your Windows 10 Universal Platform (UWP). It's designed to replicate the LevelDB
API as close as possible giving the developer full control over core LevelDB
. For those who don't know what LevelDB
is, it's a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.
This document gives you a brief overview for getting started with LevelDB
in UWP application using the LevelDBWinRT
.
Why LevelDB?
LevelDB
is a no-brainer and blazing fast key-value store! Here are some of the basic features this runtime component provides you:
- Keys and values can be arbitrary byte arrays (Slices)
- Data is stored and sorted by key
- Callers can provide custom comparison function to override the sort order (experimental support)
- Basic Get(key)/Put(key, value)/Delete(key) support
- Atomic batch operations can make multiple changes
- Users can create transient snapshot to get a consistent view of data
- Iterator support, with forward and backward iteration
- Basic wrappers for
WriteOptions
, ReadOptions
, Options
- Builtin compression support with Snappy
- Custom comparator support (experimental)
- ARM, x86, and x64 architecture support
- Supports Windows 8 and WP 8.1
Installing
You can install LevelDBWinRT by simply installing nuget from here. Or by simply running Install-Package LevelDB.UWP
on you Package Management Console.
Creating Database
Creating and opening database is really simple. Everything lives under LevelDBWinRT
namespace, import the namespace first by using LevelDBWinRT
. Each database corresponds to a file, creating a database means creating a LevelDB file. The primary class for manipulating a database is LevelDBWinRT.DB
. The constructor takes two parameters - the first one is options to create database, the second one is the file path. Example:
var db = new DB(new Options { CreateIfMissing = true }, "foo.db");
This creates database file foo.db
under ApplicationData.Current.LocalFolder.Path
, if you don't specify full path to the file (i.e., it doesn't start with X:\ where X is drive letter) the default folder is assumed to be ApplicationData.Current.LocalFolder.Path
. The operation will fail if you don't have access on the folder or your path does not exist. In case of failure, a COMException
is thrown with captured error code and error message. If you want to only open database if it exists and fail otherwise, remove CreateIfMissing
option. Under the hood, CreateIfMissing
is hooked up to leveldb::Options
object (Almost all options are available for your disposal and they are pretty powerful). We will keep it simple for now, most of the default options are already tuned for a good balance.
Disposing a Database
The DB class is disposable. So calling Dispose()
actually deletes the underlaying leveldb
object, which in turn properly releases memory and closes any opened handles to the files. So once you are done using a database, be sure to dispose it off.
db.Dispose();
Not disposing the database object and losing reference to the object leaves it upto GC for cleanup, which may have undesired results, so be sure to dispose your objects properly.
Adding, and Removing Entries
Once database has been opened, one can easily add and remove entries to it. To add or remove anything from LevelDB
, you need to provide slices, a key slice and a value slice. Slices are nothing but a wrapper around byte array in LevelDB
terms. LevelDBWinRT.Slice
is the class you can use to create slices from byte[]
or string
. Although byte[]
is enough for generating any type of slice, string
overloads have been provided due to its frequent usage. Inserting a key-value pair "foo" => "bar"
is as simple as:
db.Put(new WriteOptions(), Slice.FromString("foo"), Slice.FromString("bar"));
You can use WriteOptions
to control if you want to Sync changes to disc right away or not. Delete has similar API, deleting "foo
" is as simple as:
db.Delete(new WriteOptions(), Slice.FromString("foo"));
Flushing changes to disk immediately is pretty simple, just set Sync to true
in any of the write operations, e.g.
db.Put(new WriteOptions{ Sync = true },
Slice.FromString("foo"), Slice.FromString("bar"));
Bytes and Slices
By now, we can see Slices are an integral part of LevelDB
and bytes are a fundamental part of slice. One of the design choices made here is to only provide two methods to create a Slice. There are two static
methods, Slice.FromString
and Slice.FromByteArray
. The first method Slice.FromString
for being frequent, and Slice.FromByteArray
to support vitally everything else. Reverse methods are also available, ToByteArray
and AsString
to convert values back. You can use BitConverter
and any sort of object serializers to convert your complex objects to byte[]
, LevelDB just stays out of your way and lets you take care of serailization business.
Reading the Values
Reading individual values is also pretty simple. You can simply do:
Slice slice = db.Get(new ReadOptions(), Slice.FromString("foo"));
string sliceValue = slice.AsString();
ReadOptions
again exposes few options to give you more control over your read. Details will be discussed in a different section.
Batch Writes
Just like any good database, LevelDB
provides options to do batch inserts and deletes in a single write operation (atomic operations). A write batch can be created by WriteBatch
class provided by LevelDB
. You can create and commit a batch like the following:
using (var batch = new WriteBatch())
{
batch.Put(Slice.FromString("foo"), Slice.FromString("bar"));
batch.Delete(Slice.FromString("del"));
db.Write(new WriteOptions(), batch);
}
A more complex example can be:
using(var writeBatch = new WriteBatch())
{
for (var i = 0; i < 10; i++)
{
writeBatch.Put(Slice.FromString("Key"+i), Slice.FromByteArray(BitConverter.GetBytes(i)));
}
db.Write(new WriteOptions(), writeBatch);
}
It Has More Advanced Stuff (Iterators and Snapshots)
There is more than meets the eyes. LevelDB
has snapshots and iterators that provide read-only views to the entire database. Checkout the source code and detailed documentation on the official GitHub repository.
Download the attached sample project to see the code in action.
More tutorials: