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.1.0] - 2026-03-27

Added

  • Geographic Spatial Queries - Full MongoDB-compatible geospatial support
    • $near operator with $maxDistance and $minDistance filtering
    • $geoWithin operator with $box, $center, and GeoJSON Polygon support
    • $geoIntersects operator for point-polygon intersection testing
    • Haversine distance calculation for Earth sphere distances
    • Coordinate extraction (supports [lng,lat], GeoJSON Point, {lng,lat} formats)
    • Point-in-polygon判断 (ray casting algorithm)
    • Cursor.near() chainable API for fluent geospatial queries
  • Geospatial Indexing - Geohash-based spatial index
    • Geohash encoding/decoding
    • Automatic index maintenance (insert/update/delete)
    • Optimized $near and $geoWithin queries
  • Full-Text Search Index - Optimized text search
    • Inverted index implementation
    • TF-IDF scoring for result ranking
    • Tokenization and stop word filtering
    • Automatic index maintenance
  • Multi-Database/Collection Management - pymongo-compatible API
    • MongoClient class for database client
    • Database class for managing multiple collections
    • Collection class wrapping JSONLite instances
  • Network Mode - Client-server architecture
    • JSONLiteServer TCP server with JSON protocol
    • RemoteMongoClient client proxy
    • Optional HMAC authentication
    • Full CRUD/aggregation/index support over network
  • Data Compression - Gzip compression support
    • Configurable compression level (1-9)
    • Automatic compression detection
    • Compatible with all features (indexes/queries/special types)
  • Encryption - AES-256-GCM encryption
    • PBKDF2-SHA256 key derivation (100,000 iterations)
    • Automatic encryption detection (ENCR magic number)
    • Compatible with compression (compress-then-encrypt)
    • Full feature compatibility (indexes/queries/aggregation/MongoClient)

Changed

  • Test suite expanded from 309 to 369 tests (+60 tests)
  • All v1.1 features include comprehensive test coverage

Security

  • AES-256-GCM encryption for sensitive data
  • HMAC authentication for network mode
  • PBKDF2-SHA256 with 100k iterations for key derivation

[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
1.1.0 2026-03-27 Geospatial Queries, Full-Text Index, Multi-DB, Network Mode, Compression, Encryption
1.0.0 2026-03-21 Transactions, Query Cache, Indexes, Aggregation, PyPI Release
0.1.0 2026-03-20 Initial Release, Basic CRUD, Query Operators

Upcoming (v1.2.0)

Planned Features:

  • Advanced aggregation stages ($lookup, $facet, $bucket)
  • Change streams / real-time notifications
  • Schema validation
  • GridFS-like large file storage
  • Performance optimizations for large datasets
  • MongoDB 5.0+ feature parity

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.1.1.tar.gz (137.5 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.1.1-py3-none-any.whl (88.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: jsonlite-1.1.1.tar.gz
  • Upload date:
  • Size: 137.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for jsonlite-1.1.1.tar.gz
Algorithm Hash digest
SHA256 19ce7e5b843c23bb37d6c1a62292ba6c4a25b1ddd168cf476df9a4ea877507b9
MD5 af533a5f5c282391323286ecc9a0b4ee
BLAKE2b-256 b52595cd0a82dcb6d322deb0332a8bbc40b03c46aa93d15b8785a7989940bffb

See more details on using hashes here.

File details

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

File metadata

  • Download URL: jsonlite-1.1.1-py3-none-any.whl
  • Upload date:
  • Size: 88.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for jsonlite-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1a7d1f7d1b692d27e498c41b1020743ef433e6a99c2210bfacb98d5e4afca8a7
MD5 72a45765ae5394354c7a44151a712b01
BLAKE2b-256 0a4958ec5666132b134f1562765954da7304e1678f1479a950196dd9465b7f4d

See more details on using hashes here.

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