Bison
Bison is a fast, lightweight NoSQL database, written in Rust with seamless Python bindings. It combines the speed and safety of Rust with the flexibility of JSON storage, offering a MongoDB-like query language to easily store, query, and manipulate your data. Perfect for developers who need a powerful, schema-less database that integrates smoothly into Python projects, Bison is designed to handle complex queries and efficient updates while keeping your data operations simple and intuitive.
Features
- NoSQL Document Storage: Stores JSON documents in collections.
- MongoDB-like Query Language: Use familiar query operators such as
$eq
, $ne
, $gt
, $gte
, $lt
, $lte
for filtering documents. - Insert and Query: Easily insert documents into collections and retrieve them based on queries.
- Update Operators: Modify documents using
$set
, $inc
, $dec
, $add
, $substract
, and $delete
operators. - Mixed Queries: Perform complex queries with multiple conditions and nested fields.
- Conditional Updates: Update only the documents that match a query filter.
- Simple nested field access: Access nested fields using dot notation.
- Python Bindings: Fully integrated with Python via bindings, allowing you to use Bison in Python projects.
- File Commit: Changes are committed to disk only when explicitly requested via
db.write()
or db.write_all()
.
Installation
To use Bison in your Python project, install it using:
pip install bison-db
Performance
I decided to compare Bison against TinyDB as it is the most similar database to Bison in terms of purpose.
In our performance benchmarks, Bison is around 2-13x faster than TinyDB (depending on the type of operations and
number of documents). The chart shows the median time to execute 1000 operations on a database with 1000 collections.
Each update triggers a write to file in both Bison and TinyDB.
You can re-create the performance by running:
pytest -k comparison
Basic Usage
Creating a Collection and Inserting Documents
from bison import Bison
db = Bison()
db.create_collection("test")
db.insert("test", {"a": 10, "b": 20})
db.insert("test", {"a": True, "b": False})
Querying Data
result = db.find("test", {"a": 10})
print(result)
result = db.find("test", {"a": {"$gt": 5}})
print(result)
Update Documents Conditionally
You can update documents only when a filter query is matched. If no filter query is provided, all documents in the collection will be updated.
db.update("test", {"b": {"$set": 30}}, {"a": {"$eq": 10}})
db.update("test", {"b": {"$set": 50}}, None)
Committing Changes to Disk
By default, Bison stores all updates in memory. Changes will only be committed to a file when you explicitly call db.write(collection_name)
for a specific collection, or db.write_all()
to write all collections to disk:
db.write("test")
db.write_all()
Update Documents
db.update("test", {"a": {"$set": 30}})
db.update("test", {"a": {"$inc": ""}})
db.update("test", {"a": {"$dec": ""}})
Delete Fields
db.update("test", {"a": {"$delete": ""}})
Query Operators
Bison supports a range of MongoDB-like query operators:
-
$eq
: Matches values that are equal to a specified value.
-
$ne
: Matches all values that are not equal to a specified value.
-
$gt
: Matches values that are greater than a specified value.
-
$gte
: Matches values that are greater than or equal to a specified value.
-
$lt
: Matches values that are less than a specified value.
-
$lte
: Matches values that are less than or equal to a specified value.
Example Queries
result = db.find("test", {"a": {"$eq": 10}})
result = db.find("test", {"a": {"$ne": 20}})
result = db.find("test", {"a": {"$gt": 10}})
result = db.find("test", {"a": {"$lt": 100}})
Update Operators
Bison provides several operators for updating fields within documents:
-
$set
: Sets the value of a field.
-
$inc
: Increments a field by 1.
-
$dec
: Decrements a field by 1.
-
$add
: Adds a specified value to a field.
-
$substract
: Subtracts a specified value from a field.
-
$delete
: Deletes a field from a document.
Example Updates
db.update("test", {"a": {"$set": 40}})
db.update("test", {"b": {"$inc": ""}})
db.update("test", {"a": {"$delete": ""}})
Mixed Queries
You can combine multiple query conditions, including nested fields:
result = db.find(
"test",
{
"a": {"nested_field": {"myobj": 20}},
"b": {"$gt": 19},
"c.nested_field.really_nested": {"$lte": 120}
}
)
print(result)
Handling Errors
Invalid queries will raise exceptions. For example:
from bison import Bison
import pytest
db = Bison()
db.insert("test", {"a": 10})
with pytest.raises(ValueError):
db.find("test", {"a": {"$gt": False}})