Skip to main content

A lightweight local JSON database with MongoDB-compatible API

Project description

JSONLite

Build Status PyPI License Issues PRs Welcome

JSONLite is a lightweight, local JSON database for simple data storage.

  • Like SQLite, it's a local database.
  • Its API is 100% modeled after MongoDB, making it easy to migrate between MongoDB and JSONLite.

Features

  • Zero dependency - Pure Python, no external libraries required
  • MongoDB-compatible API - 100% pymongo API compatibility
  • Local storage - Store JSON data in simple files
  • Concurrent access - Multiple processes can read/write safely with file locking
  • Chainable queries - Fluent API with sort, limit, skip, projection
  • Aggregation pipeline - $match, $group, $project, $sort, $unwind, and more
  • Index support - Single-field, compound, unique, and sparse indexes
  • Transactions - Atomic multi-operation transactions with rollback
  • Query caching - LRU cache for improved performance
  • Special types - Full support for datetime, decimal, and binary data

Table of Contents

Installation

pip install jsonlite

Optional Performance Boost

For better JSON serialization performance:

pip install jsonlite[performance]  # Installs orjson

Documentation

Quick Start

from jsonlite import JSONlite

# Initialize database
db = JSONlite('mydata.json')

# Insert documents
db.insert_one({"name": "Alice", "age": 30})
db.insert_many([
    {"name": "Bob", "age": 25},
    {"name": "Charlie", "age": 35}
])

# Query with chainable API
results = (db
    .find({"age": {"$gte": 30}})
    .sort("age", -1)
    .limit(10)
    .toArray())

# Update documents
db.update_one({"name": "Alice"}, {"$set": {"age": 31}})

# Use indexes for faster queries
db.create_index("name")
db.create_index([("last_name", 1), ("first_name", 1)])

# Transactions for atomic operations
from jsonlite import Transaction
with Transaction(db) as txn:
    txn.update_one({"name": "Alice"}, {"$inc": {"balance": -100}})
    txn.update_one({"name": "Bob"}, {"$inc": {"balance": 100}})

Examples

Check the examples/ directory for comprehensive usage examples:

  • basic_usage.py - CRUD operations, cursor API, indexes, transactions
  • advanced_features.py - Aggregation pipelines, query cache, full-text search

Run examples:

python examples/basic_usage.py
python examples/advanced_features.py

Data Layout in json file

{
    "data": [
        {   "_id": 1,
            "name": "Alice",
            "age": 30
        },
        {   "_id": 2,
            "name": "Bob",
            "age": 25
        },
        {   "_id": 3,
            "name": "Charlie",
            "age": 20
        }
    ]
}

Direct Usage

You can use JSONlite directly to perform CRUD operations.

>>> from jsonlite import JSONlite

>>> # Initialize the database
>>> db = JSONlite('mydatabase.json')

>>> # Inserting one document
>>> result = db.insert_one({"name": "John Doe", "age": 30})
>>> result.inserted_id
1

>>> # Inserting multiple documents
>>> result = db.insert_many([
...     {"name": "Jane Doe", "age": 25},
...     {"name": "Alice", "age": 28}
... ])
>>> result.inserted_ids
[2, 3]

>>> # Finding one document
>>> document = db.find_one({"name": "John Doe"})
>>> document
{'_id': 1, 'name': 'John Doe', 'age': 30}

>>> # Finding multiple documents
>>> documents = db.find({"age": {"$gte": 25}})
>>> documents
[
    {'_id': 1, 'name': 'John Doe', 'age': 30},
    {'_id': 2, 'name': 'Jane Doe', 'age': 25},
    {'_id': 3, 'name': 'Alice', 'age': 28}
]

>>> # Updating one document
>>> result = db.update_one({"name": "John Doe"}, {"$set": {"age": 31}})
>>> result.matched_count, result.modified_count
(1, 1)

>>> # Updating multiple documents
>>> result = db.update_many({"age": {"$gte": 25}}, {"$set": {"status": "active"}})
>>> result.matched_count, result.modified_count
(3, 3)

>>> # Deleting one document
>>> result = db.delete_one({"name": "John Doe"})
>>> result.deleted_count
1

>>> # Deleting multiple documents
>>> result = db.delete_many({"age": {"$lt": 30}})
>>> result.deleted_count
2

>>> # Chainable queries with Cursor API
>>> # Sort by age descending, skip 1, limit to 2 results
>>> results = db.find({"age": {"$gte": 20}}).sort("age", -1).skip(1).limit(2).all()
>>> results
[
    {'_id': 3, 'name': 'Alice', 'age': 28},
    {'_id': 2, 'name': 'Jane Doe', 'age': 25}
]

>>> # Projection - select only specific fields
>>> results = db.find({}).projection({"name": 1, "age": 1, "_id": 0}).all()
>>> results
[
    {'name': 'Alice', 'age': 28},
    {'name': 'Jane Doe', 'age': 25}
]

>>> # Multi-field sort
>>> results = db.find({}).sort([("age", -1), ("name", 1)]).all()

>>> # Atomic find_one_and_update
>>> result = db.find_one_and_update(
...     {"name": "Alice"},
...     {"$set": {"age": 29}},
...     return_document="after"  # or "before"
... )
>>> result
{'_id': 3, 'name': 'Alice', 'age': 29}

Update Operators

JSONLite supports MongoDB-style update operators for fine-grained document modifications.

Field Update Operators

>>> # $set - Set field value
>>> db.update_one({"name": "Alice"}, {"$set": {"age": 30, "status": "active"}})

>>> # $unset - Remove field
>>> db.update_one({"name": "Alice"}, {"$unset": {"status": ""}})

>>> # $inc - Increment/decrement numeric field
>>> db.update_one({"name": "Alice"}, {"$inc": {"age": 1, "score": -5}})

>>> # $rename - Rename field
>>> db.update_one({"name": "Alice"}, {"$rename": {"age": "years_old"}})

>>> # $max - Update only if new value is greater
>>> db.update_one({"name": "Alice"}, {"$max": {"score": 100}})

>>> # $min - Update only if new value is smaller
>>> db.update_one({"name": "Alice"}, {"$min": {"score": 0}})

Array Update Operators

>>> # $push - Add element to array
>>> db.update_one({"name": "Alice"}, {"$push": {"tags": "python"}})

>>> # $push with $each - Add multiple elements
>>> db.update_one({"name": "Alice"}, {"$push": {"tags": {"$each": ["java", "go"]}}})

>>> # $pull - Remove elements matching condition
>>> db.update_one({"name": "Alice"}, {"$pull": {"tags": "java"}})

>>> # $pull with operator - Remove by condition
>>> db.update_one({"name": "Alice"}, {"$pull": {"scores": {"$gte": 60}}})

>>> # $addToSet - Add unique element (no duplicates)
>>> db.update_one({"name": "Alice"}, {"$addToSet": {"tags": "python"}})  # Won't duplicate

>>> # $pop - Remove first/last element
>>> db.update_one({"name": "Alice"}, {"$pop": {"tags": 1}})   # Remove last
>>> db.update_one({"name": "Alice"}, {"$pop": {"tags": -1}})  # Remove first

>>> # $pullAll - Remove multiple specific values
>>> db.update_one({"name": "Alice"}, {"$pullAll": {"tags": ["java", "go"]}})

Nested Field Updates

Use dot notation to update nested fields:

>>> # Insert document with nested structure
>>> db.insert_one({"name": "Alice", "address": {"city": "Beijing", "zip": "100000"}})

>>> # Update nested field
>>> db.update_one({"name": "Alice"}, {"$set": {"address.city": "Shanghai"}})

>>> # Add new nested field
>>> db.update_one({"name": "Alice"}, {"$set": {"address.country": "China"}})

>>> # Unset nested field
>>> db.update_one({"name": "Alice"}, {"$unset": {"address.zip": ""}})

Patching pymongo to use JSONlite

Alternatively, you can patch pymongo to use JSONlite and interact with JSON files as if you were using MongoDB. This allows you to use the familiar pymongo API with JSON data.

>>> from jsonlite import pymongo_patch

>>> pymongo_patch()

>>> from pymongo import MongoClient

>>> client = MongoClient('jsonlite://database')
>>> db = client.test_database
>>> collection = db.test_collection
>>> insert_result = collection.insert_one({"name": "Alice", "age": 30})
>>> # Just like using pymongo
>>> collection.drop()

Performance

JSONLite is optimized for local development and small-to-medium datasets:

Operation Performance
Single document insert < 1ms
Batch insert (1000 docs) ~50ms
Indexed query < 5ms
Full collection scan ~10ms per 1000 docs
Aggregation pipeline ~20ms per stage

See docs/BENCHMARK_REPORT.md for detailed performance analysis.

Optimization Tips

  1. Use indexes for frequently queried fields
  2. Batch operations with insert_many/update_many
  3. Enable query cache for repeated queries
  4. Use projection to fetch only needed fields
  5. Install orjson for faster JSON serialization
# Enable query cache
db.cache_enabled = True
db.cache_max_size = 1000

# Create indexes for common queries
db.create_index("email")
db.create_index([("category", 1), ("created_at", -1)])

Contributing

Contributions are welcome! Please read our Roadmap to see what we're working on.

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run tests: pytest tests/
  5. Submit a pull request

Support

License

JSONLite is licensed under the MIT License. See the LICENSE file for more information.

Changelog

Changelog

All notable changes to JSONLite will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[1.0.0] - 2026-03-21

Added

  • Transaction support with atomic multi-operation rollback
  • Optional orjson acceleration for JSON serialization
  • Query result caching with LRU eviction
  • Index support (single-field, compound, unique, sparse) with automatic maintenance
  • Aggregation pipeline support ($match, $group, $project, $sort, $skip, $limit, $count, $unwind)
  • Array update operators ($push, $pull, $addToSet, $pop, $pullAll)
  • Field update operators ($unset, $inc, $rename, $max, $min)
  • Chainable Cursor API with sort, limit, skip, projection
  • Full-text search support
  • Upsert support
  • Atomic operations: find_one_and_delete, find_one_and_replace, find_one_and_update
  • Special type serialization: datetime, decimal, binary
  • pymongo compatibility patch

Changed

  • Optimized batch insert performance (262x improvement)
  • Improved query cache hash function to handle callable keys

Fixed

  • Document replacement behavior when no operators present

[0.1.0] - 2026-03-20

Added

  • Initial release
  • Basic CRUD operations (insert/find/update/delete)
  • Query operators: $gt, $lt, $gte, $lte, $eq, $regex, $in, $all, $ne, $exists, $not
  • Logical operators: $or, $and, $nor
  • Concurrent access support (fcntl file locking)
  • Zero dependency core

Version History Summary

Version Date Key Features
Unreleased 2026-03-21 Transactions, Query Cache, Indexes, Aggregation
0.1.0 2026-03-20 Initial Release, Basic CRUD, Query Operators

Upcoming (v1.0.0)

  • Complete API reference documentation
  • Usage tutorials
  • MongoDB → JSONLite migration guide
  • Expanded example code library
  • PyPI release
  • GitHub Actions CI/CD pipeline
  • Test coverage > 85%

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

jsonlite-1.0.0.tar.gz (77.1 kB view details)

Uploaded Source

Built Distribution

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

jsonlite-1.0.0-py3-none-any.whl (47.7 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for jsonlite-1.0.0.tar.gz
Algorithm Hash digest
SHA256 e9cb927e2589a12e9e6f7c718c37567e1a7894249fb264e56484a65e3fae6f5e
MD5 d8e222ea3a99c07069ed0f3c2bc57bd0
BLAKE2b-256 5aa6b64bf921ca72f7b1f64108f6566ad51a1cff3c8480d01414a002ae1bfe16

See more details on using hashes here.

Provenance

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

Publisher: python-publish.yml on simpx/jsonlite

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

File details

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

File metadata

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

File hashes

Hashes for jsonlite-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e99d6872e802aa7dd7ffe1a847810440b47d2b045a40f772c0d0d5f7ad31e287
MD5 3ab8451e358dc2a7b6b323d38bd5177f
BLAKE2b-256 e7d86bbad7f2412bbc1bb4ad68cea012dfd38c615d3640f95f6b8e36d3c0cf15

See more details on using hashes here.

Provenance

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

Publisher: python-publish.yml on simpx/jsonlite

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