Pre-existing ruff violations surfaced by CI on PR #188: - TC001: move runtime `Neo4jDriver` imports into `TYPE_CHECKING` blocks (queries.py, chunk_writer.py, schema.py, tree_reader.py, tree_writer.py) - SIM117: combine nested `async with` in chunk_writer.write_chunks and tree_writer.write_document - SIM105: replace try/except/pass with `contextlib.suppress` in tree_writer._element_props - F401: remove unused `Neo4jDriver` import in main._init_neo4j - RUF100: remove unused `# noqa: E402` in tests/neo4j/conftest.py - I001: sort imports in tests/neo4j/test_chunk_writer.py and test_tree_writer.py Zero behaviour change. `ruff check .` now passes cleanly.
50 lines
1.6 KiB
Python
50 lines
1.6 KiB
Python
"""Idempotent Neo4j schema bootstrap.
|
|
|
|
Runs at backend startup. All statements use `IF NOT EXISTS`, so calling
|
|
this multiple times is safe — it's the contract integration tests rely on.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from infra.neo4j.driver import Neo4jDriver
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
CONSTRAINTS: tuple[str, ...] = (
|
|
"CREATE CONSTRAINT document_id IF NOT EXISTS FOR (d:Document) REQUIRE d.id IS UNIQUE",
|
|
"CREATE CONSTRAINT element_composite IF NOT EXISTS "
|
|
"FOR (e:Element) REQUIRE (e.doc_id, e.self_ref) IS UNIQUE",
|
|
"CREATE CONSTRAINT page_composite IF NOT EXISTS "
|
|
"FOR (p:Page) REQUIRE (p.doc_id, p.page_no) IS UNIQUE",
|
|
"CREATE CONSTRAINT chunk_id IF NOT EXISTS FOR (c:Chunk) REQUIRE c.id IS UNIQUE",
|
|
)
|
|
|
|
INDEXES: tuple[str, ...] = (
|
|
"CREATE INDEX element_doc IF NOT EXISTS FOR (e:Element) ON (e.doc_id)",
|
|
"CREATE INDEX chunk_doc IF NOT EXISTS FOR (c:Chunk) ON (c.doc_id)",
|
|
)
|
|
|
|
FULLTEXT_INDEXES: tuple[str, ...] = (
|
|
"CREATE FULLTEXT INDEX element_text IF NOT EXISTS FOR (e:Element) ON EACH [e.text]",
|
|
)
|
|
|
|
|
|
async def bootstrap_schema(neo: Neo4jDriver) -> None:
|
|
"""Create constraints and indexes required by the graph model.
|
|
|
|
Idempotent: safe to call on every startup.
|
|
"""
|
|
async with neo.driver.session(database=neo.database) as session:
|
|
for stmt in (*CONSTRAINTS, *INDEXES, *FULLTEXT_INDEXES):
|
|
await session.run(stmt)
|
|
logger.info(
|
|
"Neo4j schema bootstrapped (%d constraints, %d indexes, %d fulltext)",
|
|
len(CONSTRAINTS),
|
|
len(INDEXES),
|
|
len(FULLTEXT_INDEXES),
|
|
)
|