Metadata-Version: 2.4
Name: cypher-guardrail
Version: 1.1.1
Summary: Parse and validate cypher queries against a given FalkorDB Knowledge Graph schema. Validates query syntax, schema validity, and property access.
Keywords: cypher,FalkorDB,validation,parser,graph,database,graphdb,opencypher
Author: Killeroid
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Dist: regex>=2024.11.6
Requires-Dist: pandas>=2.2.3
Requires-Dist: falkordb>=1.8.0
Requires-Dist: redis>=8.0.0,<9
Requires-Python: >=3.11
Project-URL: Homepage, https://github.com/Killeroid/cypher-guardrail
Project-URL: Documentation, https://cypher-guardrail.readthedocs.io/en/latest/
Project-URL: Changelog, https://github.com/Killeroid/cypher-guardrail/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/Killeroid/cypher-guardrail/issues
Project-URL: Repository, https://github.com/Killeroid/cypher-guardrail
Description-Content-Type: text/markdown

# cypher-guardrail

**Syntax, schema and property-access validation of Cypher queries against a [FalkorDB](https://www.falkordb.com/) graph.**

[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://github.com/Killeroid/cypher-guardrail/blob/main/LICENSE)
[![Python](https://img.shields.io/badge/Python-%3E%3D3.11-brightgreen)](https://www.python.org/)
[![Documentation](https://app.readthedocs.org/projects/cypher-guardrail/badge/?version=latest)](https://cypher-guardrail.readthedocs.io/en/latest/)

cypher-guardrail checks and validates a Cypher query before it runs, which is useful for queries written by a person or generated by an LLM. It never executes your query or changes your data.

* **Syntax**: is the query valid, does it write to the graph, are its `$parameters` provided, do its variables carry conflicting labels?
* **Schema**: do the labels, relationship types, paths (and their direction) and full-text or vector indexes it uses exist in the graph?
* **Properties**: does each property it reads exist on the label it is read from?
* **Structured results**: every validator returns a score or a verdict plus metadata that says what is wrong, without parsing text.
* **Sync and async**, and **offline**: validate against a saved snapshot of the schema when no database is at hand.

## Installation

Python 3.11 or later.

```sh
pip install cypher-guardrail
```

or, with uv:

```sh
uv add cypher-guardrail
```

## Quickstart

```python
from falkordb import FalkorDB
from cypher_guardrail import PropertiesValidator, SchemaValidator, SyntaxValidator

driver = FalkorDB(host="localhost", port=6379)
graph = "my_graph"
query = "MATCH (g:Goal)-[:HAS_TARGET]->(t:Target) WHERE g.code = '1' RETURN t"

is_valid, syntax_metadata = SyntaxValidator(driver).validate(query, database_name=graph)
schema_score, schema_metadata = SchemaValidator(driver).validate(
    query, database_name=graph
)
props_score, props_metadata = PropertiesValidator(driver).validate(
    query, database_name=graph
)

kg_valid = is_valid and schema_score == 1 and props_score in (1, None)
```

Always check the syntax first: a syntax error makes the other two results misleading.

## How the results are computed

### Syntax: `is_valid`

`True` if FalkorDB accepts the query, it is read-only (`read_only=True` is the default), and none of its `$parameters` is missing. `False` otherwise.

### Schema score

A number from 0 to 1. The query's nodes, relationships and paths are each given a share:

```
node share         = labels in the query that exist in the graph / labels in the query
relationship share = relationship types that exist / relationship types in the query
path share         = paths that exist, in that direction / paths in the query

schema score = average of the shares that apply
```

* Only the kinds the query has take part, and they weigh the same: 1/3 each if it has nodes, relationships and paths, 1/2 each for two kinds, and the share itself for one.
* A query with none of the three (`MATCH (n) RETURN n`) scores 1.
* If the query calls a full-text or vector index procedure (`db.idx.fulltext.query...`, `db.idx.vector.query...`):

  ```
  index share  = calls whose index exists / index calls
  schema score = (score above + index share) / 2
  ```

### Properties score

```
properties score = properties that exist on their label / properties the query accesses
```

It is `None` when the query accesses no property. Both a property that its label does not have and a property that exists nowhere in the graph count as incorrect. A type-mismatch warning (`check_types=True`) never changes the score.

### KG Valid Query

A query is valid against the graph only if all three checks pass:

```
KG Valid Query = 1  if SyntaxValidator is_valid is True
                    and schema score = 1
                    and properties score is 1 or None
                 0  otherwise
```

## Documentation

* [Using the validators](https://cypher-guardrail.readthedocs.io/en/latest/usage.html): connecting, read-only mode, parameters, `UNION`, type checks
* [Reading the metadata](https://cypher-guardrail.readthedocs.io/en/latest/metadata.html): every issue code and its fields
* [Snapshots](https://cypher-guardrail.readthedocs.io/en/latest/snapshots.html): offline and hybrid validation, and keeping a snapshot fresh
* [Async usage](https://cypher-guardrail.readthedocs.io/en/latest/async.html)
* [Runnable examples](https://github.com/Killeroid/cypher-guardrail/tree/main/examples): a quickstart, offline validation with a snapshot, and async validation (no server needed)
* [CHANGELOG](https://cypher-guardrail.readthedocs.io/en/latest/changelog.html)

## Development

```sh
uv sync            # create the environment (Python >= 3.12 also installs FalkorDBLite for the tests)
uv run pytest      # run the tests
uv run --group docs sphinx-build -W docs docs/_build/html   # build the documentation
```

[`.github/workflows/publish.yml`](https://github.com/Killeroid/cypher-guardrail/blob/main/.github/workflows/publish.yml) publishes a release to PyPI when a version tag is pushed: set `version` in `pyproject.toml`, add the release to `CHANGELOG.md`, then `git tag vX.Y.Z && git push origin vX.Y.Z`.

Contributions are welcome: please open an issue or a pull request.

## License

[Apache License 2.0](https://github.com/Killeroid/cypher-guardrail/blob/main/LICENSE)
