NQLStore, a simple CRUD store python library for `any query launguage` (or in short `nql`).
Reason this release was yanked:
The package was unusable when installed with only one of the extras 'sql', 'redis', or 'mongo' due to import errors
Project description
NQLStore
NQLStore, a simple CRUD store python library for any query launguage (or in short nql)
NQLStore provides an oversimplified API for the mundane things of creating, reading,
updating, and deleting data models that are persisted to any SQL- or NoSQL- database.
In total, all we need are four methods and that is it.
Supported databases include:
-
Relational databases like:
- SQLite
- PostgreSQL
- MySQL
-
NoSQL databases like:
- Redis
- MongoDB
If you like our simple API, you can even easily extend it to support your favourite database technology.
Dependencies
- Python +3.10
- Pydantic +2.0
- SQLModel _(optional) - only required for relational databases
- RedisOM (optional) - only required for redis
- Beanie (optional) - only required for MongoDB
Quick Start
Install NQLStore from Pypi
Install NQLStore from pypi, with any of the options: sql, mongo, redis, all.
pip install nqlstore
Swap out your object mapping (OM) package imports with nqlstore
In your python modules, define your data models as you would define them with your favourite OM package. The only difference is the package you import them from.
Here are examples of OM packages to substitute.
SQL (use SQLModel models)
# models.py
from nqlstore.sql import Field, SQLModel
class Library(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
address: str
name: str
class Book(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
title: str
library_id: int = Field(default=None, foreign_key="library.id")
Redis (use RedisOM models)
Take note that JsonModel, EmbeddedJsonModel require RedisJSON, while queries require RedisSearch to be loaded You need to install redis-stack or load the modules manually
# models.py
from typing import List
from nqlstore.redis import Field, JsonModel, EmbeddedJsonModel
class Book(EmbeddedJsonModel):
title: str = Field(index=True)
class Library(JsonModel):
address: str
name: str = Field(index=True)
books: List[Book]
Mongo (use Beanie)
# models.py
from nqlstore.mongo import Document, Indexed
class Library(Document):
address: str
name: str
class Book(Document):
title: Indexed(str)
library_id: str
Initialize your store
Initialize the store that is to host your models.
Similar to how you imported models from specific packages in nqlstore,
import stores from the appropriately named modules innqlstore.
Here are examples for the different database technologies.
SQL
Migrations are outside the scope of this package
# main.py
from nqlstore.sql import SQLStore
from .models import Book, Library
if __name__ == "__main__":
store = SQLStore(uri="sqlite+aiosqlite:///database.db")
store.register([
Library,
Book,
])
Redis
# main.py
from nqlstore.redis import RedisStore
from .models import Book, Library
if __name__ == "__main__":
store = RedisStore(uri="rediss://username:password@localhost:6379/0")
store.register([
Library,
Book,
])
Mongo
# main.py
from nqlstore.mongo import MongoStore
from .models import Book, Library
if __name__ == "__main__":
store = MongoStore(uri="mongodb://localhost:27017", database="testing")
store.register([
Library,
Book,
])
Use your models in your application
In the rest of you application use the four class methods available on the models.
Filtering follows the MongoDb-style
However, for more complex queries, one can also pass in querying styles native to the type of the database,
alongside the MongoBD-style querying. The two queries would be merged as AND queries.
Or one can simply ignore the MongoDB-style querying and stick to the native querying.
The available querying formats include:
- SQL - SQLModel-style
- Redis - RedisOM-style
- MongoDb - MongoDB-style
Insert
Inserting new items in a store, call store.insert() method.
new_libraries = await store.insert(Library, [{}, {}])
Find
Finding items in a store, call the store.find() method.
The key-word arguments include:
skip (int)- number of records to ignore at the top of the returned results; default is 0.limit (int | None)- maximum number of records to return; default is None.
The querying format is as described above
SQL
MongoDB-style:
libraries = await store.find(
Library, query={"name": {"$eq": "Hairora"}, "address" : {"$ne": "Buhimba"}}
)
Native-style only:
libraries = await store.find(
Library, Library.name == "Hairora", Library.address != "Buhimba"
)
Hybrid
libraries = await store.find(
Library, Library.name == "Hairora", query={"address" : {"$ne": "Buhimba"}}
)
Redis
MongoDB-style:
libraries = await store.find(
Library, query={"name": {"$eq": "Hairora"}, "address" : {"$ne": "Buhimba"}}
)
Native-style only:
libraries = await store.find(
Library, (Library.name == "Hairora") & (Library.address != "Buhimba")
)
Hybrid
libraries = await store.find(
Library, (Library.name == "Hairora"), query={"address" : {"$ne": "Buhimba"}}
)
Mongo
libraries = await store.find(
Library, {"name": "Hairora", "address": {"$ne": "Buhimba"}}
)
Update
Updating items in a store, call the store.update() method.
The method returns the newly updated records.
The filters follow the same style as that used when querying as shown above.
Similarly, updates are different for each type of database technology as alluded to earlier.
SQL updates are just dictionaries of the new field values
MongoDB-style:
libraries = await store.update(
Library,
query={"name": {"$eq": "Hairora"}, "address" : {"$ne": "Buhimba"}},
updates={"name": "Foo"},
)
Native-style only:
libraries = await store.update(
Library,
Library.name == "Hairora", Library.address != "Buhimba",
updates={"name": "Foo"},
)
Hybrid
libraries = await store.update(
Library,
Library.name == "Hairora", query={"address" : {"$ne": "Buhimba"}},
updates={"name": "Foo"},
)
Redis updates are just dictionaries of the new field values
MongoDB-style:
libraries = await store.update(
Library,
query={"name": {"$eq": "Hairora"}, "address" : {"$ne": "Buhimba"}},
updates={"name": "Foo"},
)
Native-style only:
libraries = await store.update(
Library,
(Library.name == "Hairora") & (Library.address != "Buhimba"),
updates={"name": "Foo"},
)
Hybrid
libraries = await store.update(
Library,
(Library.name == "Hairora"),
query={"address" : {"$ne": "Buhimba"}},
updates={"name": "Foo"},
)
Mongo updates are MongoDB-style update dicts
libraries = await store.update(
Library,
{"name": "Hairora", "address": {"$ne": "Buhimba"}},
updates={"$set": {"name": "Foo"}},
)
Delete
Deleting items in a store, call the store.delete() method.
The filters follow the same style as that used when reading as shown above.
SQL
MongoDB-style:
libraries = await store.delete(
Library, query={"name": {"$eq": "Hairora"}, "address" : {"$ne": "Buhimba"}}
)
Native-style only:
libraries = await store.delete(
Library, Library.name == "Hairora", Library.address != "Buhimba"
)
Hybrid
libraries = await store.delete(
Library, Library.name == "Hairora", query={"address" : {"$ne": "Buhimba"}}
)
Redis
MongoDB-style:
libraries = await store.delete(
Library, query={"name": {"$eq": "Hairora"}, "address" : {"$ne": "Buhimba"}}
)
Native-style only:
libraries = await store.delete(
Library, (Library.name == "Hairora") & (Library.address != "Buhimba")
)
Hybrid
libraries = await store.delete(
Library, (Library.name == "Hairora"), query={"address" : {"$ne": "Buhimba"}}
)
Mongo
libraries = await store.delete(
Library, {"name": "Hairora", "address": {"$ne": "Buhimba"}}
)
Contributions
Contributions are welcome. The docs have to maintained, the code has to be made cleaner, more idiomatic and faster, and there might be need for someone else to take over this repo in case I move on to other things. It happens!
When you are ready, look at the CONTRIBUTIONS GUIDELINES
License
Copyright (c) 2025 Martin Ahindura
Licensed under the MIT License
Gratitude
"In that day you will ask in My name. I am not saying that I will ask the Father on your behalf. No, the Father himself loves you because you have loved Me and have believed that I came from God."
-- John 16: 26-27
All glory be to God
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 nqlstore-0.0.1.tar.gz.
File metadata
- Download URL: nqlstore-0.0.1.tar.gz
- Upload date:
- Size: 25.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
720ba5a035aa6fe16f0739a702764d3a86361293c6a8f06f4ea1fb85cd9d7a79
|
|
| MD5 |
191682d8f9b5168fc0e325aac5a3292b
|
|
| BLAKE2b-256 |
2971fdc712c1c00dae67388c21b5d0d31805f6be2018e0da8317a510c3a67bd7
|
File details
Details for the file nqlstore-0.0.1-py3-none-any.whl.
File metadata
- Download URL: nqlstore-0.0.1-py3-none-any.whl
- Upload date:
- Size: 15.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2fcab12f75de05612b771feaa2bbae5e701709f1de2400bd9a2f6047f70708d9
|
|
| MD5 |
2193363af5ac174b50f457ffe4ae5e59
|
|
| BLAKE2b-256 |
1558aa9145b31041b695d7d820c79d962a3812d6e8e1c46b12404fb28ae5feee
|