Skip to content

Commit

Permalink
Merge pull request #4 from saurabh0719/develop
Browse files Browse the repository at this point in the history
v0.2.0
  • Loading branch information
saurabh0719 authored May 12, 2021
2 parents a922c87 + 19f7bc7 commit 25d244b
Show file tree
Hide file tree
Showing 12 changed files with 727 additions and 123 deletions.
223 changes: 143 additions & 80 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div align="center">
<img src="elara.png" width ="75%">
<p>Elara DB is an easy to use, json based key-value database written in python. Includes various methods to manipulate data structures in-memory, secure database files and export data.</p>
<p>Elara DB is an easy to use, key-value database written for python that can also be used as a fast in-memory cache. Includes various methods to manipulate data structures in-memory, secure database files and export data.</p>
</div>

```sh
Expand All @@ -11,41 +11,46 @@ $ pip install elara

## Key Features :
* Offers two modes of execution - normal and secure - exe_secure() generates a key file and encrypts the key-value storage for additional security.
* Perform various operations on strings, lists and dictionaries.
* Manipulate data structures in-memory.
* Can be used as a fast in-memory cache.
* Choose between manual commits after performing operations in-memory or automatically commit every change into the storage.
* Includes methods to export certain keys or the entire storage.
* Includes methods to export certain keys from the database or the entire storage.
* Based on python's in-built json module for easy manipulation and access.
* Takes inspiration from `pickleDB` and `Redis`.

## Table of Contents :
* [Installation](#installation)
* [License](#license)
* [Usage](#usage)
* [Basics](#basics)
* [Basic](#basic)
* [Cache](#cache)
* [API](#api)
* [Strings](#strings)
* [Lists](#lists)
* [Dictionaries](#dict)
* [Miscallaneous](#misc)
* [Export](#export)
* [Update Key](#misc)
* [Export data](#export)
* [Tests](#tests)
* [Releases](#releases)
* [Acknowledgments](#ack)
* [Contributors](#contrib)

<hr>

<span id="installation"></span>
## Installation Guide
## Installation

From pypi :
From [pypi](https://pypi.org/project/elara/) :

```sh
$ pip install elara
```

OR, Clone the repository and install the dependencies.
OR,

Clone the repository and install the dependencies :

```sh
$ pip install -r requirements.py
$ python -m unittest -v # Run tests
```

<hr>
Expand All @@ -63,16 +68,21 @@ This source code is licensed under the BSD-style license found in the LICENSE fi
<span id="usage"></span>
## Usage

<span id="basic"></span>
### Basic usage :

You can choose between normally transacting data from the file or you can transact data from an encrypted file.

```python
>>> import elara as elara
import elara

# exe_secure() encrypts the db file
>>> db = elara.exe_secure("new.db", True, "newdb.key")
>>> db.set("name", "Elara")
>>> print(db.get("name"))
Elara
db = elara.exe_secure("new.db", True, "newdb.key")

db.set("name", "Elara")

print(db.get("name"))
# Elara

```

Expand All @@ -83,73 +93,123 @@ Using `exe_secure()` without a key file or without the correct key file correspo
* *`commit`* - this argument defaults to *`False`* ie. you will have to manually call the `commit()` method to write the in-memory changes into the database. If set to *`True`*, changes will be written into the file after every operation.

```python
>>> import elara as elara
import elara

>>> db = elara.exe("new.db", "newdb.key") # commit=False
>>> db.set("num", 20)
>>> print(db.get("num"))
20
db = elara.exe("new.db", "newdb.key") # commit=False

>>> db.commit() # Writes in-memory changes into the file
db.set("num", 20)

print(db.get("num"))
# 20

db.commit() # Writes in-memory changes into the file
```

* `exe(db_file_path, commit=False)` - Loads the contents of the database into the program memory or generates a new database file if it doesn't exist in the given path. The database file is NOT encrypted and is present in a human-readable json format.

```python
>>> import elara as elara
import elara as elara

>>> db = elara.exe("new.db", True)
>>> db.set("name", "Elara")
>>> print(db.get("name"))
Elara
db = elara.exe("new.db", True)

```
db.set("name", "Elara")

print(db.get("name"))
# Elara

<span id="basics"></span>
### Basic operations :
```

All the following operations are methods that can be applied to the instance returned from `exe()` or `exe_secure()`. These operations manipulate/analyse data in-memory after the Data is loaded from the file. Set the `commit` argument to `True` else manually use the `commit()` method to sync in-memory data with the database file.

* `get(key)` - returns the corresponding value from the db or *`None`*
* `set(key, value)` - returns *`True`* or an Exception. The `key` has to be a String.
* `rem(key)` - deletes the key-value pair if it exists.
* `clear()` - clears the database data currently stored in-memory.
* `exists(key)` - returns `True` if the key exists.
* `exists(key)` - returns *`True`* if the key exists.
* `commit()` - write in-memory changes into the database file.
* `getset(key, value)` - Sets the new value and returns the old value for that key or returns *`False`*.
* `getkeys()` - returns the list of keys in the database with. The list is ordered with the *`least recently accessed`* keys starting from index 0.
* `numkeys()` - returns the number of keys in the database.
* `retkey()` - returns the Key used to encrypt/decrypt the db file; returns *`None`* if the file is unprotected.
* `retmem()` - returns all the in-memory db contents.
* `retdb()` - returns all the db file contents.

```python
>>> import elara as elara
import elara

>>> db = elara.exe("new.db", False)
>>> db.set("num1", 20)
>>> db.commit() # ("num1", 20) is written into the file db
>>> db.set("num2", 30)
>>> print(db.retmem())
{'num1': 20, 'num2': 30}
db = elara.exe("new.db")

>>> print(db.retdb())
{'num1': 20}
db.set("num1", 20)

# ("num1", 20) is written into the file db
db.commit()

db.set("num2", 30)

print(db.retmem())
# {'num1': 20, 'num2': 30}

print(db.retdb())
# {'num1': 20}

```

Note - `retmem()` and `retdb()` will return the same value if *`commit`* is set to *`True`* or if the `commit()` method is used before calling `retdb()`

<span id="cache"></span>
### Cache:

Elara can also be used as a fast in-memory cache. Start/open a new instance and ensure the `commit` argument is *`False`* or left empty (`commit` defaults to `False`), to prevent writes into the database file.

* `cull(percentage)` - `percentage` (0 <= percentage <= 100) defines the percentage of Key-Value pairs to be deleted, with the *Least recently accessed* keys being deleted first. Elara maintains a simple LRU list to track key access.

```python
import elara

cache = elara.exe("new.db")

cache.set("num1", 10)
cache.set("num2", 20)
cache.set("num3", 30)
cache.set("num4", 40)

if cache.exists("num1"):
print(cache.get("num1"))
# 10

print(cache.retmem())
# {'num1': 10, 'num2': 20, 'num3': 30, 'num4': 40}

# least recently accessed keys come first
print(cache.getkeys())
# ['num1', 'num4', 'num3', 'num2']

# delete 25% of the stale keys (follows LRU)
cache.cull(25)

# least recently accessed keys come first
print(cache.getkeys())
# ['num1', 'num4', 'num3']

```

<hr>

<span id="api"></span>
## API

<span id="strings"></span>
### String operations :
### Strings :

* `mget(keys)` - takes a list of keys as an argument and returns a list with all the corresponding values IF they exist; returns an empty list otherwise.
* `mset(dict)` - takes a dictionary of key-value pairs as an argument and calls the `set(key, value)` method for each pair. Keys have to be a String.
* `setnx(key, value)` - Sets the key-value if the key DOES NOT exist and returns *`True`*; returns *`False`* otherwise.
* `setnx(key, value)` - Sets the key-value if the key does not exist and returns *`True`*; returns *`False`* otherwise.
* `msetnx(dict)` - takes a dictionary of key-value pairs as an argument and calls the `setnx(key, value)` method for each pair. Keys have to be a string.
* `slen(key)` - returns the length of the string value if the key exists; returns `-1` otherwise.
* `append(key, data)` - Append the data (String) to an existing string value; returns *`False`* if it fails.

<span id="lists"></span>
### List operations :
### Lists :

* `lnew(key)` - Initialises an empty list for the given key and returns `True` or an Exception; key has to be a string.
* `lpush(key, value)` - Appends the given value to the list and returns `True`; returns `False` if the key does not exist.
Expand All @@ -163,31 +223,31 @@ Note - `retmem()` and `retdb()` will return the same value if *`commit`* is set
* `lappend(key, pos, value)` - appends `value` to the existing data at index `pos` using the `+` operator. Returns `True` or `False`.

```python
>>> import elara as elara
import elara

>>> db = elara.exe('new.db', True)
db = elara.exe('new.db', True)

>>> db.lnew('newlist')
>>> db.lpush('newlist', 3)
>>> db.lpush('newlist', 4)
>>> db.lpush('newlist', 5)
db.lnew('newlist')
db.lpush('newlist', 3)
db.lpush('newlist', 4)
db.lpush('newlist', 5)

>>> print(db.lpop('newlist'))
5
print(db.lpop('newlist'))
# 5

>>> print(db.lindex('newlist', 0))
3
print(db.lindex('newlist', 0))
# 3

>>> new_list = [6, 7, 8, 9]
>>> db.lextend('newlist', new_list)
>>> print(db.get('newlist'))
[3, 4, 6, 7, 8, 9]
new_list = [6, 7, 8, 9]
db.lextend('newlist', new_list)
print(db.get('newlist'))
# [3, 4, 6, 7, 8, 9]

```

=> The following methods do not have complete test coverage yet :
<span id="dict"></span>
### Hashtable/Dictionary operations :
### Hashtable/Dictionary :

* `hnew(key)` - Initialises an empty dictionary for the given key and returns `True` or an Exception; key has to be a string.
* `hadd(key, dict_key, value)` - Assigns a value to a dictionary key and returns *`True`*; returns *`False`* if the dictionary doesn't exist.
Expand All @@ -199,24 +259,25 @@ Note - `retmem()` and `retdb()` will return the same value if *`commit`* is set
* `hmerge(key, dict)` - updates (dict.update()) the dictionary pointed by the key with the new dictionary `dict` passed as an argument.

<span id="misc"></span>
### Miscellaneous operations :
### Update key and Secure DB :

* `updatekey(key_path)` - This method works for instances produced by `exe_secure()`. It updates the key in the key file path and re-encyrpts the database with the new key. If the file doesn't exist, the method generates a new file with a key and uses that to encrypt the database file.

```python
>>> import elara as elara
import elara

# exe_secure() encrypts the db file
>>> db = elara.exe_secure("new.db", True, "newdb.key")
>>> db.set("name", "Elara")
>>> print(db.get("name"))
Elara
db = elara.exe_secure("new.db", True, "newdb.key")
db.set("name", "Elara")

print(db.get("name"))
# Elara

>>> db.updatekey('newkeypath.key')
db.updatekey('newkeypath.key')

# Regular program flow doesn't get affected by key update
>>> print(db.get("name"))
Elara
print(db.get("name"))
# Elara

```

Expand All @@ -225,7 +286,7 @@ However, the next time you run the program, you have to pass the new updated key
* `securedb(key_path)` - Calls `updatekey(key_path)` for instances which are already protected with a key. For an unprotected instance of `exe()`, it generates a new key in the given key_path and encrypts the database file. This db file can henceforth only be used with the `exe_secure()` function.

<span id="export"></span>
### Export operations :
### Export data :

* `exportdb(export_path, sort=True)` - Copies the entire content of the database file into the specified export file path using `json.dump()`. To prevent sorting of Keys, use `exportdb(export_path, False)`

Expand All @@ -234,17 +295,18 @@ However, the next time you run the program, you have to pass the new updated key
* `exportkeys(export_path, keys = [], sort=True)` - Takes a list of keys as an argument and exports those specific keys from the in-memory data to the given export file path.

```python
>>> import elara as elara
import elara

db = elara.exe('new.db', False)
db.set("one", 100)
db.set("two", 200)
db.commit()
db.set("three", 300)

>>> db = elara.exe('new.db', False)
>>> db.set("one", 100)
>>> db.set("two", 200)
>>> db.commit()
>>> db.set("three", 300)
db.exportdb('exportdb.txt')

>>> db.exportdb('exportdb.txt')
>>> db.exportmem('exportmem.txt')
>>> db.exportkeys('exportkeys.txt', keys = ['one', 'three'])
db.exportmem('exportmem.txt')
db.exportkeys('exportkeys.txt', keys = ['one', 'three'])

'''
# exportdb.txt
Expand Down Expand Up @@ -280,14 +342,15 @@ $ python -m unittest -v
<span id="releases"></span>
### Releases :
* Latest - `v0.1.3`
* Latest - `v0.2.0`
* Previous - `v0.1.3`
Donwload the latest release from [here](https://github.com/saurabh0719/elara/releases/).
<hr>
<span id="ack"></span>
### Acknowledgment :
<span id="contrib"></span>
### Contributors :
Author - Saurabh Pujari
<br>
Logo design - Jonah Eapen
Loading

0 comments on commit 25d244b

Please sign in to comment.