Immutable NoSQL database in a plain file
Project description
fiableDB
A simple, reliable, immutable database that stores all data revisions in JSON format.
Features
- Information is never lost. Even if you make updates or deletions, you will be able to recover any information at any time.
- There are no restrictions on the data structure or columns, since dictionaries are used without limitations on nesting. Similar to MongoDB Documents.
- All the information is stored in a JSON file.
- Extremely fast since it has no queue or locking limitations.
- Minimalistic to implement and use.
Why use fiableDB instead of other relational databases?
- High level of consistency and accuracy of data, such as a hospital patient's chronology or banking data. It cannot be modified once it has been aggregated.
- They simplify the process of backing up and restoring data, because you can always revert to the original version of the data if necessary.
- Very secure, modifying existing data will be detected and rejected.
Install
Python version: >=3.8
pip3 install --user fiable_db
Documentation
All documentation can be read as a sequential tutorial.
Step 1: Start
To load the database you must import fiable_db and start it.
import fiable_db
fiable_db.start()
It will create a file named fiabledb.json in the current directory. If you want to change the name of the file, you can do it by passing the name as a parameter.
fiable_db.start("my_db.json")
# Returns: "my_db.json"
If the file already exists, it will be loaded. Nothing is deleted here!
Error handling:
try:
fiable_db.start("my_db.json")
except Exception as e:
print(f"Database initialization failed: {e}")
# Creates empty database if file is corrupted
Step 2: Aggregation
Add a single record:
result = fiable_db.add({"name": "Miguel", "age": 41, "height": 189})
print(result)
# {"id": 1, "rev": 1, "table": "default", "data": {"name": "Miguel", "age": 41, "height": 189}}
Add multiple records:
result = fiable_db.add([
{"name": "Noelia", "age": 34, "height": 165},
{"name": "Juan", "age": 41, "height": 187},
{"name": "Valentina", "age": 12, "height": 142},
])
print(result)
# [
# {"id": 2, "rev": 1, "table": "default", "data": {"name": "Noelia", "age": 34, "height": 165}},
# {"id": 3, "rev": 1, "table": "default", "data": {"name": "Juan", "age": 41, "height": 187}},
# {"id": 4, "rev": 1, "table": "default", "data": {"name": "Valentina", "age": 12, "height": 142}},
# ]
Input validation:
# These will raise errors
try:
fiable_db.add({}) # ValueError: Cannot add empty dictionary
fiable_db.add([]) # ValueError: Cannot add empty list
fiable_db.add("invalid") # TypeError: new_data must be a dict or list
except (ValueError, TypeError) as e:
print(f"Invalid input: {e}")
Step 3: Update
Update a field:
result = fiable_db.update(4, {"age": 21})
print(result)
# {"id": 4, "rev": 2, "table": "default", "data": {"name": "Valentina", "age": 21, "height": 142}}
Add new fields:
result = fiable_db.update(4, {"is_active": True})
print(result)
# {"id": 4, "rev": 3, "table": "default", "data": {"name": "Valentina", "age": 21, "height": 142, "is_active": True}}
Delete a field (set to None):
result = fiable_db.update(4, {"height": None})
print(result)
# {"id": 4, "rev": 4, "table": "default", "data": {"name": "Valentina", "age": 21, "is_active": True}}
Force overwrite (replace entire data):
result = fiable_db.update(4, {"name": "Javier", "email": "foo@example.com"}, force=True)
print(result)
# {"id": 4, "rev": 5, "table": "default", "data": {"name": "Javier", "email": "foo@example.com"}}
Handle non-existent records:
result = fiable_db.update(999, {"name": "Ghost"})
print(result)
# None
Input validation:
try:
fiable_db.update(-1, {"name": "Invalid"}) # ValueError: id must be a positive integer
fiable_db.update(1, "not_dict") # TypeError: new_data must be a dictionary
except (ValueError, TypeError) as e:
print(f"Invalid input: {e}")
Step 4: Delete
You can delete by using the id. This creates a new revision with empty data.
result = fiable_db.delete(id=4)
print(result)
# {"id": 4, "rev": 6, "table": "default", "data": {}}
Input validation:
try:
fiable_db.delete(-1) # ValueError: id must be a positive integer
fiable_db.delete("invalid") # TypeError: id must be an integer
except (ValueError, TypeError) as e:
print(f"Invalid input: {e}")
Step 5: Find One
Search by ID (gets latest revision):
result = fiable_db.find_one(id=2)
print(result)
# {"id": 2, "rev": 1, "table": "default", "data": {"name": "Noelia", "age": 34, "height": 165}}
Search by data filter (first match):
result = fiable_db.find_one(data={"name": "Noelia"})
print(result)
# {"id": 2, "rev": 1, "table": "default", "data": {"name": "Noelia", "age": 34, "height": 165}}
Search by multiple criteria:
result = fiable_db.find_one(data={"name": "Noelia", "age": 34})
print(result)
# {"id": 2, "rev": 1, "table": "default", "data": {"name": "Noelia", "age": 34, "height": 165}}
No results return None:
result = fiable_db.find_one(data={"name": "NonExistent"})
print(result)
# None
Input validation:
try:
fiable_db.find_one(id="invalid") # TypeError: id must be an integer
fiable_db.find_one(data="invalid") # TypeError: data must be a dictionary
except TypeError as e:
print(f"Invalid input: {e}")
Step 6: Find All
Find all records in default table:
result = fiable_db.find_all()
print(result)
# Returns all active records (latest revisions) sorted by table and id
Filter by data:
result = fiable_db.find_all(data={"age": 41})
print(result)
# [
# {"id": 1, "rev": 1, "table": "default", "data": {"name": "Miguel", "age": 41, "height": 189}},
# {"id": 3, "rev": 1, "table": "default", "data": {"name": "Juan", "age": 41, "height": 187}},
# ]
No results return empty list:
result = fiable_db.find_all(data={"age": 999})
print(result)
# []
Input validation:
try:
fiable_db.find_all(data="invalid") # TypeError: data must be a dictionary
except TypeError as e:
print(f"Invalid input: {e}")
Step 7: View Previous Revisions
At any time you can view the previous information of any row using the rev parameter.
Get specific revision:
result = fiable_db.find_one(id=4, rev=3)
print(result)
# {"id": 4, "rev": 3, "table": "default", "data": {"name": "Valentina", "age": 21, "height": 142, "is_active": True}}
Use negative numbers for relative revisions:
-1 will be the previous state, -2 is 2 states back, etc.
# Get previous revision
result = fiable_db.find_one(id=4, rev=-1)
print(result)
# {"id": 4, "rev": 5, "table": "default", "data": {"name": "Javier", "email": "foo@example.com"}}
# Get 2 revisions back
result = fiable_db.find_one(id=4, rev=-2)
print(result)
# {"id": 4, "rev": 4, "table": "default", "data": {"name": "Valentina", "age": 21, "is_active": True}}
Non-existent revisions return None:
result = fiable_db.find_one(id=4, rev=999)
print(result)
# None
result = fiable_db.find_one(id=4, rev=-999)
print(result)
# None
Step 8: Working with Tables/Collections
You can create as many tables as you want. The default table is called default. Use the table parameter in any function to work with different tables.
Add to specific table:
result = fiable_db.add({"name": "Luciano", "age": 54, "height": 165}, table="users")
print(result)
# {"id": 1, "rev": 1, "table": "users", "data": {"name": "Luciano", "age": 54, "height": 165}}
Find in specific table:
# Find in "users" table
result = fiable_db.find_one(id=1, table="users")
print(result)
# {"id": 1, "rev": 1, "table": "users", "data": {"name": "Luciano", "age": 54, "height": 165}}
# Find in "default" table
result = fiable_db.find_one(id=1, table="default")
print(result)
# {"id": 1, "rev": 1, "table": "default", "data": {"name": "Miguel", "age": 41, "height": 189}}
Update in specific table:
result = fiable_db.update(1, {"age": 10}, table="users")
print(result)
# {"id": 1, "rev": 2, "table": "users", "data": {"name": "Luciano", "age": 10, "height": 165}}
Delete in specific table:
result = fiable_db.delete(1, table="users")
print(result)
# {"id": 1, "rev": 3, "table": "users", "data": {}}
Find all in specific table:
result = fiable_db.find_all(table="users")
print(result)
# Returns all records from "users" table
Cross-table search (leave table empty):
result = fiable_db.find_one(data={"name": "Luciano"}, table="")
print(result)
# Searches across all tables
Step 9: Save Changes
Save the database to the current file.
success = fiable_db.save()
print(success) # True if saved successfully, False if error occurred
Save to specific file:
success = fiable_db.save("backup.json")
print(success) # True if saved successfully
Other Helper Functions
Get All Data
Get the complete database (all revisions, all tables).
all_data = fiable_db.get_database()
print(len(all_data)) # Total number of records (including all revisions)
Load File
Load a file into the database.
try:
success = fiable_db.load("backup.json")
print(f"File loaded: {success}")
except FileNotFoundError as e:
print(f"File not found: {e}")
Error Handling
fiableDB includes comprehensive error handling:
Common Exceptions
TypeError: Invalid data types passed to functionsValueError: Invalid values (negative IDs, empty data)FileNotFoundError: Database file not found or corruptedjson.JSONDecodeError: Corrupted JSON file
Best Practices
import fiable_db
try:
# Initialize database
fiable_db.start("myapp.json")
# Add data with validation
if user_data and isinstance(user_data, dict):
result = fiable_db.add(user_data, table="users")
# Always check for None results
user = fiable_db.find_one(id=user_id, table="users")
if user is not None:
print(f"Found user: {user['data']['name']}")
else:
print("User not found")
# Save changes
if not fiable_db.save():
print("Warning: Could not save database")
except (TypeError, ValueError) as e:
print(f"Invalid input: {e}")
except FileNotFoundError as e:
print(f"Database error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
Data Structure
Each record in fiableDB has the following structure:
{
"id": 1, # Unique identifier within table
"rev": 3, # Revision number (increments with each update)
"table": "users", # Table/collection name
"data": { # Your actual data
"name": "John",
"age": 30
}
}
Performance Notes
- IDs are auto-generated starting from 1 for each table
- All operations are O(n) where n is the total number of records
- Memory usage grows with each revision (immutable design)
- File I/O is synchronous - consider frequency of save() calls
- Best for small to medium datasets (< 100k records)
Implementations in Other Languages
Thanks to the power of 🐍 Python 🐍
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fiabledb-1.0.0.tar.gz.
File metadata
- Download URL: fiabledb-1.0.0.tar.gz
- Upload date:
- Size: 21.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17179ed8f69652620f2c8c9e8522a2b8e3446b06ede9e36ef760a7ab688c96af
|
|
| MD5 |
dfdc4c2b2f7b3bd08df8b424c77ddb6d
|
|
| BLAKE2b-256 |
8b531bf4f9b167c8b534b944499fd1bba6263aca7518bd3e5da19b5fff40fcb5
|
File details
Details for the file fiabledb-1.0.0-py3-none-any.whl.
File metadata
- Download URL: fiabledb-1.0.0-py3-none-any.whl
- Upload date:
- Size: 22.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
556d199dbbfed747d54213201c6b59cfa2771238bf691cef470ec0a1a61ad806
|
|
| MD5 |
25e91e82361d1b74797cc3c12f21d510
|
|
| BLAKE2b-256 |
5a4cbb4b7f74734b401bee0acddec6ca591df58c3fc7dc6811e3af713b04ab50
|