Skip to content

[FEATURE] Neo4jStore adapter — branch ingestion vers Neo4j vector index #253

@pjmalandrino

Description

@pjmalandrino

Context

Suite à #251, le StoreKind.NEO4J est exposé côté domaine + service + UI : on peut créer / éditer / supprimer un Store de kind Neo4j, et la config (index_name, database) est validée et persistée. Mais aucun adaptateur d'ingestion n'est branché : seul OpenSearchStore (document-parser/infra/opensearch_store.py) implémente le port VectorStore. Conséquence : un Store Neo4j créé via l'UI est inerte — le pipeline d'ingestion ne sait pas pousser dedans.

L'infrastructure Neo4j existe déjà partiellement : infra/neo4j.py gère le driver async (auth via NEO4J_URI/NEO4J_USER/NEO4J_PASSWORD) et bootstrap_schema(), utilisé aujourd'hui pour le graphe-natif de structure documentaire (#202+). Cette issue ajoute un second usage du driver : un adaptateur de chunks-vector-store.

Scope

In scope

  • infra/neo4j_store.py — implémente le port VectorStore (méthodes index_chunks, search, delete_for_doc, health) en utilisant le driver Neo4j 5.x et son support natif des vector indexes.
  • Création idempotente du vector index au premier push (CREATE VECTOR INDEX <name> IF NOT EXISTS FOR (c:Chunk) ON c.embedding OPTIONS {…}), avec dimension lue depuis EMBEDDING_DIMENSION.
  • Schéma minimal : nœuds (:Chunk {chunk_id, doc_id, text, page, headings, embedding}) rattachés à un nœud (:Document {doc_id}) via [:HAS_CHUNK]. La database du Store est routée à la session (session(database=…)).
  • Sélection de l'adaptateur côté IngestionService selon Store.kind au lieu d'une instance globale OpenSearchStore.
  • Tests : unitaires (mocked driver) + un test d'intégration optionnel gardé par NEO4J_URI env (skip si absent).

Out of scope

  • Page de query (/index/:slug/query) — toujours hors périmètre.
  • Modèle de retrieval hybride (vector + Cypher graph traversal) — futur.
  • Auth par-store (URI/user/password dans Store.config) — pour l'instant l'auth reste globale via env.
  • Migration des Store Neo4j existants (l'enum a été ajouté dans [ENHANCEMENT] CRUD complet sur Stores (fix 404 + management des configs) #251 ; pas de stores en prod).

Current behavior

Expected behavior

  • IngestionService lit Store.kind (et Store.config) pour choisir l'adaptateur :
    • opensearchOpenSearchStore (existant)
    • neo4jNeo4jStore (à créer)
  • Push d'un document vers un Store Neo4j → chunks indexés dans le vector index Neo4j configuré, recherche par similarité fonctionnelle (cosine/euclidean selon OPTIONS).
  • health() retourne True si le driver répond, False + errorMessage sinon → propagé jusqu'à StoreInfoResponse.connected côté UI.

Steps to reproduce / Acceptance criteria

  • infra/neo4j_store.py implémente VectorStore (port domain).
  • Vector index créé idempotently au premier push (dimension dynamique).
  • IngestionService route vers l'adaptateur selon Store.kind.
  • Tests unitaires (mocked driver) couvrant index/search/delete + erreurs réseau.
  • Test d'intégration optionnel (skip si NEO4J_URI absent) — push doc, search, delete, vérifier comptage.
  • StoreInfoResponse.connected reflète l'état réel du driver (UI montre « Connecté/Déconnecté »).
  • Tous les tests passent (pytest), zéro violation lint.

Impact / Priority rationale

Sans cet adaptateur, l'option « Neo4j » dans le form est trompeuse : elle laisse créer un Store qui n'ingère rien. Soit on livre l'adaptateur, soit on retire l'option du dropdown jusqu'à ce qu'elle soit fonctionnelle. À programmer pour 0.7.0 (pas urgent — pas de régression sur l'existant).

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions