Metadata-Version: 2.2
Name: odata-v4-query
Version: 0.1.0
Summary: A lightweight, simple and fast parser for OData V4 query options supporting standard query parameters. Provides helper functions to apply OData V4 query options to ORM/ODM queries such as SQLAlchemy, PyMongo and Beanie.
Author-email: Dairo Mosquera <dairoandres115@outlook.com>
Project-URL: Homepage, https://github.com/daireto/odata-v4-query
Project-URL: Issues, https://github.com/daireto/odata-v4-query/issues
Project-URL: Repository, https://github.com/daireto/odata-v4-query
Keywords: odata-v4-query,odata-query-parser,tokenizer-parser,wrapper
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: all
Requires-Dist: beanie>=1.23.0; extra == "all"
Requires-Dist: pymongo>=4.3.3; extra == "all"
Requires-Dist: sqlalchemy>=2.0.0; extra == "all"
Provides-Extra: beanie
Requires-Dist: beanie>=1.23.0; extra == "beanie"
Provides-Extra: pymongo
Requires-Dist: pymongo>=4.3.3; extra == "pymongo"
Provides-Extra: sqlalchemy
Requires-Dist: sqlalchemy>=2.0.0; extra == "sqlalchemy"

<!-- omit in toc -->
# OData V4 Query

A lightweight, simple and fast parser for OData V4 query options supporting
standard query parameters. Provides helper functions to apply OData V4 query
options to ORM/ODM queries such as SQLAlchemy, PyMongo and Beanie.

<!-- omit in toc -->
## Table of Contents
- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Utility Functions](#utility-functions)
    - [Beanie](#beanie)
    - [PyMongo](#pymongo)
    - [SQLAlchemy](#sqlalchemy)
- [Contributing](#contributing)
- [License](#license)
- [Support](#support)

## Features

- Support for the following OData V4 standard query parameters:
    - `$count` - Include count of items
    - `$expand` - Expand related entities
    - `$filter` - Filter results
    - `$format` - Response format (json, xml, csv, tsv)
    - `$orderby` - Sort results
    - `$search` - Search items
    - `$select` - Select specific fields
    - `$skip` - Skip N items
    - `$top` - Limit to N items

- Comprehensive filter expression support:
    - Comparison operators: `eq`, `ne`, `gt`, `ge`, `lt`, `le`, `in`, `nin`
    - Logical operators: `and`, `or`, `not`, `nor`
    - Collection operators: `has`
    - String functions: `startswith`, `endswith`, `contains`

- Utility functions to apply options to ORM/ODM queries.
    - See [utility functions](#utility-functions) for more information.

## Requirements

- `Python 3.10+`
- `beanie 1.23+ (optional, for Beanie ODM utils)`
- `pymongo 4.3+ (optional, for PyMongo utils)`
- `sqlalchemy 2.0+ (optional, for SQLAlchemy utils)`

## Installation

You can simply install odata-v4-query from
[PyPI](https://pypi.org/project/odata-v4-query/):
```bash
pip install odata-v4-query
```

To install all the optional dependencies to use all the ORM/ODM utils:
```bash
pip install odata-v4-query[all]
```

You can also install the dependencies for a specific ORM/ODM util:
```bash
pip install odata-v4-query[beanie]
pip install odata-v4-query[pymongo]
pip install odata-v4-query[sqlalchemy]
```

## Quick Start

```python
from odata_v4_query import ODataQueryParser, ODataFilterParser

# Create parser instance
parser = ODataQueryParser()

# Parse a complete URL
options = parser.parse_url('https://example.com/odata?$count=true&$top=10&$skip=20')

# Parse just the query string
options = parser.parse_query_string("$filter=name eq 'John' and age gt 25")

# Parse filter expressions
filter_parser = ODataFilterParser()
ast = filter_parser.parse("name eq 'John' and age gt 25")

# Evaluate filter expressions
filter_parser.evaluate(ast)
```

## Utility Functions

You to need to install the [required dependencies](#requirements) for the
ORM/ODM you want to use.

### Beanie

```python
from beanie import Document
from odata_v4_query import ODataQueryParser
from odata_v4_query.utils.beanie import apply_to_beanie_query

class User(Document):
    name: str
    email: str
    age: int

# Create parser instance
parser = ODataQuery_parser()

# Parse a complete URL
options = parser.parse_query_string("$top=10&$skip=20&$filter=name eq 'John'")

# Apply options to a new query
query = apply_to_beanie_query(options, User)

# Apply options to an existing query
query = User.find()
query = apply_to_beanie_query(options, query)
```

> [!NOTE]
> The `$count`, `$expand` and `$format` options won't be applied.
> You need to handle them manually.

### PyMongo

```python
from pymongo import MongoClient, ASCENDING, DESCENDING
from odata_v4_query import ODataQueryParser
from odata_v4_query.utils.pymongo import PyMongoQuery, get_query_from_options

client = MongoClient()
db = client['db']

# Create parser instance
parser = ODataQuery_parser()

# Parse a complete URL
options = parser.parse_query_string("$top=10&$skip=20&$filter=name eq 'John'")

# Get a PyMongo query from options
query = get_query_from_options(options)

# Apply query to collection
db.users.find(**query)

# Using keyword arguments
db.users.find(
    skip=query.skip,
    limit=query.limit,
    filter=query.filter,
    sort=query.sort,
    projection=query.projection,
)
```

> [!NOTE]
> The `$count`, `$expand` and `$format` options won't be applied.
> You need to handle them manually.

### SQLAlchemy

```python
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from odata_v4_query import ODataQueryParser
from odata_v4_query.utils.sqlalchemy import apply_to_sqlalchemy_query

class User(DeclarativeBase):
    name: Mapped[str] = mapped_column()
    email: Mapped[str] = mapped_column()
    age: Mapped[int] = mapped_column()

# Create parser instance
parser = ODataQuery_parser()

# Parse a complete URL
options = parser.parse_query_string("$top=10&$skip=20&$filter=name eq 'John'")

# Apply options to a new query
query = apply_to_sqlalchemy_query(options, User)

# Apply options to an existing query
query = select(User)
query = apply_to_sqlalchemy_query(options, query)
```

> [!NOTE]
> The `$count` and `$format` options won't be applied. You need to handle them
> manually. Also, the `has` and `nor` operators are not supported in SQL,
> so they are converted to a `LIKE` and `NOT` expressions, respectively.

## Contributing

See the [contribution guidelines](CONTRIBUTING.md).

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE)
file for details.

## Support

If you find this project useful, give it a ⭐ on GitHub!
