Skip to content

Commit

Permalink
First PR :D (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
ninoseki committed Mar 26, 2024
1 parent afa62df commit add936e
Show file tree
Hide file tree
Showing 17 changed files with 1,653 additions and 1 deletion.
26 changes: 26 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Publish package
on:
release:
types: ["created"]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Poetry
uses: abatilo/actions-poetry@v3
with:
poetry-version: 1.8.2
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.11
cache: poetry
- name: Install plugin
run: poetry self add "poetry-dynamic-versioning[plugin]"
- name: Set PyPI token
env:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
run: poetry config pypi-token.pypi $PYPI_TOKEN
- name: Build and publish
run: poetry publish --build
23 changes: 23 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Test
on: ["pull_request", "push"]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", 3.11]
poetry-version: [1.8.2]
steps:
- uses: actions/checkout@v4
- name: Setup Poetry
uses: abatilo/actions-poetry@v3
with:
poetry-version: ${{ matrix.poetry-version }}
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: poetry
- name: Install Python dependencies
run: poetry install
- name: Run tests
run: poetry run pytest
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# VS Code
.vscode/
43 changes: 43 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-added-large-files
- id: check-toml
- id: check-yaml
args:
- --unsafe
- id: end-of-file-fixer
- id: trailing-whitespace

- repo: https://github.com/python-poetry/poetry
rev: 1.8.0
hooks:
- id: poetry-check

- repo: https://github.com/andrei-shabanski/poetry-plugin-sort
rev: v0.2.1
hooks:
- id: poetry-sort

- repo: https://github.com/asottile/pyupgrade
rev: v3.15.2
hooks:
- id: pyupgrade
args:
- --py310-plus

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.3.4
hooks:
- id: ruff
args:
- --fix
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.9.0
hooks:
- id: mypy
additional_dependencies:
- types-all
165 changes: 164 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,164 @@
# sqlmodel-filters
# sqlmodel-filters

A Lucene query like filter for [SQLModel](https://github.com/tiangolo/sqlmodel).

> [!NOTE]
> This is an alpha level library. Everything is subject to change & there are some known limitations.
## Installation

```bash
pip install sqlmodel_filters
```

## How to Use

Let's say we have the following model & records:

```py
import datetime
from functools import partial
from typing import Optional

from sqlmodel import Field, SQLModel


class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
created_at: datetime.datetime = Field(
default_factory=partial(datetime.datetime.now, datetime.UTC)
)

hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)


engine = create_engine("sqlite://")


SQLModel.metadata.create_all(engine)

with Session(engine) as session:
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
```

And let's try querying with this library.

```py
# this library relies on luqum (https://github.com/jurismarches/luqum) for parsing Lucene query
from luqum import parse
from sqlmodel import Session

from sqlmodel_filters import Builder

# parse a Lucene query
parsed = parse('name:Spider')
# build SELECT statement for Hero based on the parsed query
builder = Builder(Hero)
statement = builder(parsed)

# the following is a compiled SQL query
statement.compile(compile_kwargs={"literal_binds": True})
>>> SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.created_at
>>> FROM hero
>>> WHERE hero.name = '%Spider%'

# you can use the statement like this
heros = session.exec(statement).all()
assert len(heros) == 1
assert heros[0].name == "Spider-Boy"
```

Note that a value is automatically casted based on a field definition.

```py
# age: Optional[int]
"age:48"
>>> WHERE hero.age = 48

# created_at: datetime.datetime
"created_at:2020-01-01"
>>> WHERE hero.created_at = '2020-01-01 00:00:00'
```

### `Word` (`Term`)

Double quote a value if you want to use the equal operator.

```py
'name:"Spider-Boy"'
>>> WHERE hero.name = 'Spider-Boy'
```

The `LIKE` operator is used when you don't double quote a value.

```py
"name:Spider"
>>> WHERE hero.name LIKE '%Spider%'
```

Use `?` (a single character wildcard) or `*` (a multiple character wildcard) to control a LIKE operator pattern.

```py
"name:Deadpond?"
>>> WHERE hero.name LIKE 'Deadpond_'

"name:o*"
>>> WHERE hero.name LIKE 'o%'
```

### `FROM` & `TO`

```py
"age:>=40"
>>> WHERE hero.age >= 40

"age:>40"
>>> WHERE hero.age > 40
```

```py
"age:<=40"
>>> WHERE hero.age <= 40

"age:<40"
>>> WHERE hero.age < 40
```

### `RANGE`

```py
"age:{48 TO 60}"
>>> WHERE hero.age < 60 AND hero.age > 48

"age:[48 TO 60]"
>>> WHERE hero.age <= 60 AND hero.age >= 48
```

## `AND`, `OR`, `NOT` and `GROUP` (Grouping)

```py
"name:Rusty AND age:48"
>>> WHERE hero.name LIKE '%Rusty%' AND hero.age = 48

"name:Rusty OR age:47"
>>> WHERE hero.name LIKE '%Rusty%' OR hero.age = 47

"name:Rusty NOT age:47"
>>> WHERE hero.name LIKE '%Rusty%' AND hero.age != 47

"(name:Spider OR age:48) AND name:Rusty"
>>> WHERE (hero.name LIKE '%Spider%' OR hero.age = 48) AND hero.name LIKE '%Rusty%'
```

## Known Limitations / Todos

- Relationship join is not supported
- Filed Grouping is not supported

0 comments on commit add936e

Please sign in to comment.