Parameterized Queries

Parameterized queries prevent SQL injection and properly handle special characters. This guide shows how to use them.

Basic Usage

Use $parameter syntax in Cypher and pass a dictionary of parameters to Connection.cypher():

from graphqlite import Graph

g = Graph(":memory:")

# Named parameters via the connection
results = g.connection.cypher(
    "MATCH (n:Person {name: $name}) WHERE n.age > $age RETURN n",
    {"name": "Alice", "age": 30}
)

With the Connection API

The Connection.cypher() method accepts parameters as a dictionary:

from graphqlite import connect

conn = connect(":memory:")

# Create with parameters
conn.cypher(
    "CREATE (n:Person {name: $name, age: $age})",
    {"name": "Bob", "age": 25}
)

# Query with parameters
results = conn.cypher(
    "MATCH (n:Person) WHERE n.age >= $min_age RETURN n.name",
    {"min_age": 21}
)

With Raw SQL

When using the SQLite interface directly:

SELECT cypher(
    'MATCH (n:Person {name: $name}) RETURN n',
    '{"name": "Alice"}'
);

Parameter Types

Parameters support all JSON types:

params = json.dumps({
    "string_val": "hello",
    "int_val": 42,
    "float_val": 3.14,
    "bool_val": True,
    "null_val": None,
    "array_val": [1, 2, 3]
})

Use Cases

User Input

Always use parameters for user-provided values:

def search_by_name(user_input: str):
    # Safe - user input is parameterized
    return g.connection.cypher(
        "MATCH (n:Person {name: $name}) RETURN n",
        {"name": user_input}
    )

Batch Operations

people = [
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 25},
    {"name": "Carol", "age": 35},
]

for person in people:
    g.connection.cypher(
        "CREATE (n:Person {name: $name, age: $age})",
        person
    )

Complex Values

Parameters handle special characters automatically:

# This works correctly even with quotes and newlines
text = "He said \"hello\"\nand then left."
g.connection.cypher(
    "CREATE (n:Note {content: $text})",
    {"text": text}
)

Benefits

  1. Security: Prevents Cypher injection attacks
  2. Correctness: Properly handles quotes, newlines, and special characters
  3. Performance: Query plans can be cached (future optimization)
  4. Clarity: Separates query logic from data

Common Patterns

Optional Parameters

def search(name: str = None, min_age: int = None):
    conditions = []
    params = {}

    if name:
        conditions.append("n.name = $name")
        params["name"] = name
    if min_age:
        conditions.append("n.age >= $min_age")
        params["min_age"] = min_age

    where = f"WHERE {' AND '.join(conditions)}" if conditions else ""

    return g.connection.cypher(
        f"MATCH (n:Person) {where} RETURN n",
        params if params else None
    )

Lists in Parameters

names = ["Alice", "Bob", "Carol"]
results = g.connection.cypher(
    "MATCH (n:Person) WHERE n.name IN $names RETURN n",
    {"names": names}
)