Skip to main content

A Python package to manipulate dictionaries like databases.

Project description

DictDB Logo

CI License Python 3.13+ Code style: Ruff Type checking: MyPy


DictDB is an in-memory, dictionary-based database for Python with SQL-like CRUD operations, optional schemas, fast lookups via indexes, and a fluent query DSL.

Perfect for prototyping, testing, and lightweight relational workflows without a full database engine.

Features

  • SQL-like CRUDINSERT, SELECT, UPDATE, DELETE with familiar semantics
  • Fluent Query DSL — Build conditions with Python operators: table.age >= 18
  • Indexes — Hash indexes for O(1) equality lookups, sorted indexes for range queries
  • Optional Schemas — Type validation when you need it, flexibility when you don't
  • Persistence — Save/load to JSON or Pickle
  • Thread-Safe — Reader-writer locks for concurrent access
  • Zero Config — No server, no setup, just Python

Installation

pip install dctdb
from dictdb import DictDB, Condition

Note: The PyPI package is dctdb, but the import name is dictdb.

Development Setup

git clone https://github.com/mhbxyz/dictdb.git
cd dictdb
make setup

Quickstart

Basic CRUD

from dictdb import DictDB, Condition

# Create database and table
db = DictDB()
db.create_table("employees", primary_key="emp_id")
employees = db.get_table("employees")

# Insert records (auto-assigns emp_id if missing)
employees.insert({"emp_id": 101, "name": "Alice", "department": "IT", "salary": 75000})
employees.insert({"name": "Bob", "department": "HR", "salary": 65000})
employees.insert({"name": "Charlie", "department": "IT", "salary": 85000})
employees.insert({"name": "Diana", "department": "Sales", "salary": 70000})

# Select all
all_employees = employees.select()

# Update records
employees.update({"salary": 68000}, where=Condition(employees.name == "Bob"))

# Delete records
employees.delete(where=Condition(employees.name == "Diana"))

Query DSL

# Comparison operators: ==, !=, <, <=, >, >=
seniors = employees.select(where=Condition(employees.salary >= 80000))

# Logical AND
it_seniors = employees.select(
    where=Condition((employees.department == "IT") & (employees.salary >= 80000))
)

# Logical OR
it_or_hr = employees.select(
    where=Condition((employees.department == "IT") | (employees.department == "HR"))
)

# IN operator
tech_teams = employees.select(
    where=Condition(employees.department.is_in(["IT", "Engineering", "Data"]))
)

# String matching
employees.select(where=Condition(employees.name.startswith("A")))
employees.select(where=Condition(employees.name.contains("li")))

Sorting & Pagination

# Order by salary descending
top_earners = employees.select(order_by="-salary")

# Multi-field sort: department asc, then salary desc
sorted_employees = employees.select(order_by=["department", "-salary"])

# Pagination
page_size = 10
page = 2
paginated = employees.select(
    order_by="name",
    limit=page_size,
    offset=(page - 1) * page_size
)

Column Projection

# Select specific columns
names = employees.select(columns=["name", "department"])
# [{"name": "Alice", "department": "IT"}, ...]

# Column aliasing
aliased = employees.select(columns={"employee": "name", "team": "department"})
# [{"employee": "Alice", "team": "IT"}, ...]

Schema Validation

# Define schema for type enforcement
schema = {"emp_id": int, "name": str, "department": str, "salary": int}
db.create_table("staff", primary_key="emp_id", schema=schema)
staff = db.get_table("staff")

staff.insert({"emp_id": 1, "name": "Alice", "department": "IT", "salary": 75000})  # OK
staff.insert({"emp_id": 2, "name": "Bob", "department": "HR", "salary": "high"})   # Raises SchemaValidationError

Indexes

# Hash index for O(1) equality lookups
employees.create_index("department", index_type="hash")
employees.select(where=Condition(employees.department == "IT"))  # Fast

# Sorted index for range queries
employees.create_index("salary", index_type="sorted")
employees.select(where=Condition(employees.salary > 70000))      # Fast
employees.select(where=Condition(employees.salary <= 80000))     # Fast

# Check indexes
employees.indexed_fields()      # ["department", "salary"]
employees.has_index("salary")   # True

Persistence

# Save to JSON (human-readable)
db.save("employees.json", file_format="json")

# Save to Pickle (faster, binary)
db.save("employees.pkl", file_format="pickle")

# Load from disk
db2 = DictDB.load("employees.json", file_format="json")

# Async I/O for non-blocking operations
import asyncio
asyncio.run(db.async_save("employees.json", file_format="json"))

Introspection

# Table metadata
employees.columns()          # ["emp_id", "name", "department", "salary"]
employees.count()            # 3
employees.primary_key_name() # "emp_id"

# Database metadata
db.list_tables()             # ["employees", "staff"]

Documentation

Full documentation available at mhbxyz.github.io/dictdb

Section Description
Getting Started Installation and first steps
Query DSL Build conditions with Python operators
Indexes Speed up queries
Schemas Type validation
Persistence Save and load databases
API Reference Complete API documentation

Development

# Setup environment
make setup
make hooks-install

# Run all checks (format, lint, types, tests)
make check

# Run tests with coverage
make coverage

# Run benchmarks
make benchmark

License

Apache License 2.0 — see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dctdb-1.0.0.tar.gz (185.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dctdb-1.0.0-py3-none-any.whl (36.3 kB view details)

Uploaded Python 3

File details

Details for the file dctdb-1.0.0.tar.gz.

File metadata

  • Download URL: dctdb-1.0.0.tar.gz
  • Upload date:
  • Size: 185.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dctdb-1.0.0.tar.gz
Algorithm Hash digest
SHA256 96b0836d4856e42c83020abc7c1b2710b6a988c6ecbbc4f861d7bf24c08a886f
MD5 80854023babbd04400a9cdb1f84dc998
BLAKE2b-256 585dcdf6b542362a5b47b11dd6e11b091e201ae196426f1c6dbde0d8fabde1c3

See more details on using hashes here.

Provenance

The following attestation bundles were made for dctdb-1.0.0.tar.gz:

Publisher: publish.yml on mhbxyz/dictdb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dctdb-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: dctdb-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 36.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dctdb-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6985a32e307aa25f84d8585cf178e006d2e0ff47eac50771ea2f70b7a7f24321
MD5 ec509d51fc721ef1d97c094a68c4c97e
BLAKE2b-256 4d55b4961c9e56739c407fd819aa7cc2d78c9b4393e531db10c2a2e4e7c7841d

See more details on using hashes here.

Provenance

The following attestation bundles were made for dctdb-1.0.0-py3-none-any.whl:

Publisher: publish.yml on mhbxyz/dictdb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page