Skip to content
This repository was archived by the owner on Dec 22, 2020. It is now read-only.

Repository files navigation

KeyValueStore Build Status

Transactional key-value store on top of any database.

Why?

I needed a key-value store supporting ACID transactions. Since currently there are not many solutions out there, I created a layer above SQL databases like MySQL. My hope is that in the near future I will be able to replace the SQL database with something doing just what I want.

Features

  • Simple API.
  • If a key is an array it is serialized in a way that sort element-wise.
  • Easy transactions with automatic begin/commit/rollback.
  • Asynchronous functions return promises, feel free to handle them with ES7 async/await feature.

Supported databases

  • Every databases supported by AnySQL.
  • More to come...

Installation

npm install --save key-value-store

Usage

Simple operations

importKeyValueStorefrom'key-value-store';letstore=newKeyValueStore('mysql://test@localhost/test');asyncfunctionsimple(){letkey=['users','abcde12345'];// Createawaitstore.put(key,{firstName: 'Manu',age: 42});// Readletuser=awaitstore.get(key);// Updateawaitstore.put(key,{firstName: 'Manu',age: 43});// Deleteawaitstore.delete(key);}

Range queries

importKeyValueStorefrom'key-value-store';letstore=newKeyValueStore('mysql://test@localhost/test');asyncfunctionquery(){returnawaitstore.find({prefix: 'users',startAfter: 'abcde12345',limit: 30});}

Transactions

importKeyValueStorefrom'key-value-store';letstore=newKeyValueStore('mysql://test@localhost/test');asyncfunctioncriticalOperation(){awaitstore.transaction(asyncfunction(transaction){letkey=['users','abcde12345'];letuser=awaittransaction.get(key);user.age++;awaittransaction.put(key,user);// ...// if no error has been thrown, the transaction is automatically committed});}

Basic concepts

Keys and values

Keys and values can be any kind of data.

If a key is an array it is automatically serialized in a way that sort element-wise. Thanks to that, keys can easily represent "path" (e.g.: ['users', 'abcde12345']).

Values are serialized with JSON.stringify. If you need to customize the serialization of your objects, you can implement a toJSON() method on them.

Promise based API

Every asynchronous operation returns a promise. It is a good idea to handle them with the great ES7 async/await feature. Since ES7 is not really there yet, you should compile your code with something like Babel.

API

new KeyValueStore(url)

Create a store for the specified URL.

importKeyValueStorefrom'key-value-store';letstore=newKeyValueStore('mysql://test@localhost/test');

store.get(key, [options])

Get an item from the store.

letuser=awaitstore.get(['users','abcde12345']);

options

  • errorIfMissing(default: true): if true, an error is thrown if the specified key is missing from the store. If false, the method returns undefined when the key is missing.

store.put(key, value, [options])

Put an item in the store.

awaitstore.put(['users','abcde12345'],{firstName: 'Manu',age: 42});

options

  • createIfMissing(default: true): if false, an error is thrown if the specified key is missing from the store. This way you can ensure an "update" semantic.
  • errorIfExists(default: false): if true, an error is thrown if the specified key is already present in the store. This way you can ensure a "create" semantic.

store.delete(key, [options])

Delete an item from the store.

awaitstore.delete(['users','abcde12345']);

options

  • errorIfMissing(default: true): if true, an error is thrown if the specified key is missing from the store. If false, the method returns false when the key is missing.

store.getMany(keys, [options])

Get several items from the store. Return an array of objects composed of two properties: key and value. The order of the specified keys is preserved in the result.

letusers=awaitstore.getMany([['users','abcde12345'],['users','abcde67890'],// ...]);

options

  • errorIfMissing(default: true): if true, an error is thrown if one of the specified keys is missing from the store.
  • returnValues(default: true): if false, only keys found in the store are returned (no value property).

store.putMany(items, [options])

Not implemented yet.

store.deleteMany(keys, [options])

Not implemented yet.

store.find([options])

Fetch items matching the specified criteria. Return an array of objects composed of two properties: key and value. The returned items are ordered by key.

// Fetch all usersletusers=awaitstore.find({prefix: 'users'});// Fetch 30 users after the 'abcde12345' keyletusers=awaitstore.find({prefix: 'users',startAfter: 'abcde12345',limit: 30});

options

  • prefix: fetch items with keys starting with the specified value.
  • start, startAfter: fetch items with keys greater than (or equal to if you use the start option) the specified value.
  • end, endBefore: fetch items with keys less than (or equal to if you use the end option) the specified value.
  • reverse(default: false): if true, reverse the order of returned items.
  • limit(default: 50000): limit the number of fetched items to the specified value.
  • returnValues(default: true): if false, only keys found in the store are returned (no value property).

store.count([options])

Count items matching the specified criteria.

letusers=awaitstore.count({prefix: 'users',startAfter: 'abcde12345'});

options

  • prefix: count items with keys starting with the specified value.
  • start, startAfter: count items with keys greater than (or equal to if you use the start option) the specified value.
  • end, endBefore: count items with keys less than (or equal to if you use the end option) the specified value.

store.findAndDelete([options])

Delete items matching the specified criteria. Return the number of deleted items.

letdeletedItemsCount=awaitstore.findAndDelete({prefix: 'users',startAfter: 'abcde12345'});

options

  • prefix: delete items with keys starting with the specified value.
  • start, startAfter: delete items with keys greater than (or equal to if you use the start option) the specified value.
  • end, endBefore: delete items with keys less than (or equal to if you use the end option) the specified value.

store.transaction(fun)

Run the specified function inside a transaction. The function receives a transaction handler as first argument. This handler should be used as a replacement of the store for every operation made during the execution of the transaction. If any error occurs, the transaction is aborted and the store is automatically rolled back.

// Increment a counterawaitstore.transaction(asyncfunction(transaction){letvalue=awaittransaction.get('counter');value++;awaittransaction.put('counter',value);});

store.close()

Close all connections to the store.

License

MIT

About

Transactional key-value store on top of any database

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages