symfony/ai-neo4j-store
Neo4j vector store integration for Symfony AI Store. Persist embeddings in Neo4j and run vector similarity searches through its native vector indexes, giving Symfony AI applications a graph-database-backed store.
Architecture fit. Unchanged. This remains a thin adapter — a Neo4j vector-store bridge implementing Symfony AI's vector store contract — whose value is narrow but real: graph-aware retrieval using Neo4j's native semantic indexes, with relationship traversal available in the same query. If retrieval does not need traversal, a cheaper store still wins. v0.14.0 does nothing to change the fit calculus; notably, none of the four changelog entries are Neo4j-specific hardening, schema tooling, or documentation. They are contract-level changes propagated from the parent symfony/ai monorepo.
What v0.14.0 actually delivers.
| Change | Relevance to this bridge | Read |
|---|---|---|
StoreFactory for "remaining HTTP-based bridges" (#2574) |
Likely not applicable — this bridge talks Bolt via the Neo4j PHP client, not HTTP | Verify; if a factory is exposed here, it mildly reduces hand-wiring |
count() capability (#1755) |
Applies. Adds a method to the store contract | Useful (corpus size, pagination, health assertions) but it is a contract expansion |
VectorDocumentInterface (#2495) |
Applies. Documents become an interface rather than a concrete shape | Additive now; a precursor to further signature churn |
| Version bump / demo sync (#2599) | Routine monorepo tagging | Confirms the release cadence is synchronization-driven |
The material read for a TPM: adding a method to a published interface is a breaking change for implementers, and we are the implementers. Our recommended posture — an internal port with our own adapter over this package — means every contract addition lands in our code. count() is trivial; the pattern is not. This is the first concrete evidence for the churn risk that the prior assessment carried as a projection. It should now be treated as observed behavior, and the exact-version pin and adapter isolation are no longer optional hygiene — they are the control that makes the dependency survivable.
Integration feasibility. Mechanics are unchanged: Composer install, Neo4j PHP client, interface binding. The three non-code gaps from the prior assessment are untouched by this release:
count() helps you observe corpus size — it does not help you migrate it.Technical risk. Still the dominant section, still unfavorable, and now slightly better evidenced.
| Risk | Severity | Change since last review |
|---|---|---|
| Pre-1.0 churn | High | Confirmed, not projected. 0.13→0.14 shipped an interface method addition and a new document interface. Under pre-1.0 rules this is legitimate; for us it is breaking. |
| Adoption signal | High | No change indicated in release data; re-verify stars/dependents before relying on the prior figures. |
| Documentation depth | Medium-High | Unchanged. No worked examples added for filters, remove(), count(), VectorDocumentInterface, or dimension handling. |
| Upstream concentration | Medium | Reinforced — all four entries are parent-repo PRs (#2599, #2574, #1755, #2495). Roadmap control sits with a project whose priorities are not ours. |
| Metadata anomaly | Low-Medium | Still open. Version numbers are a poor stability proxy here; judge by changelog content, which shows contract motion. |
| Vendor coupling | Medium | Marginally worse: VectorDocumentInterface deepens the Symfony AI contract in our code, not just the store binding. |
| Neo4j cost/ops | Medium | Unchanged. New stateful service with backup, upgrade, and capacity obligations. |
Key questions to resolve before committing.
StoreFactory apply to this bridge at all, and does count() have defined semantics against a filtered query or only the full collection? Get both answers before designing the internal port, or you will guess twice.Stack fit and the abstraction decision. The single most leverageable decision is still to not depend on this package directly. Define an internal VectorStore port in the Laravel app, implement one adapter over symfony/ai-neo4j-store, and keep a second adapter over pgvector or the in-memory store. v0.14.0 strengthens the case: the port must now cover count() and normalize VectorDocumentInterface into an internal DTO, so that the next contract change costs one class, not a codebase.
Concretely, the port should be:
add(documents) query(embedding, filter, topK) count(filter) remove(ids)
count() is welcome — it gives you a cheap index-health assertion and a corpus-size metric for capacity planning, and it is a legitimate small win. Do not, however, read it as a sign of stabilization; a contract that grows by one method per minor release is a contract that is still being designed.
Compatibility gates. Before any code: verify PHP 8.2+ and target Laravel version against the package's composer.json; Neo4j 5.12+ for semantic indexes; a pinned Neo4j PHP client major; and driver compatibility with any managed Neo4j tier. Pin the bridge and driver to exact versions in composer.lock and treat patch upgrades as reviewable events. With 0.14 shipped, assume the next minor also moves the interface.
Sequencing.
count(), not just connectivity. Rollback documented to the alternate adapter.Anti-pattern to avoid. Binding the Symfony interface as a singleton in AppServiceProvider and calling it from controllers. That is the fastest path to Phase 2 and the slowest path out — and as of this release, "out" now means chasing interface additions in every call site.
Maintenance. Three surfaces, not one: the bridge package (moving, pre-1.0), the Neo4j PHP client (independent cadence), and Neo4j itself. v0.14.0 is a useful calibration point for the estimate: roughly a half-day per quarter of dependency review is adequate only if upgrades are batched and the adapter is genuinely thin. A minor release that adds an interface method and a document abstraction is a multi-hour change, not a zero-hour one. Index DDL and embedding-dimension constants belong in version control alongside schema migrations.
Support. Community-only, routed through the parent symfony/ai repository. No SLA, no security-response commitment specific to this package, no commercial backstop. Treat support as "we are the maintainers of last resort." The code surface remains small enough that forking is credible — that credibility is the mitigation, and it is worth confirming every release that it stays true.
Scaling. Two independent constraints: traversal cost and vector index performance. Neo4j's semantic index is not a specialized vector engine; expect practical ceilings earlier than a dedicated store at equal spend. Plan read replicas and a cache for hot queries rather than assuming vertical scale. Embedding storage grows linearly with corpus and dimension; a model upgrade that increases dimensions remains a full rebuild plus capacity re-plan.
Failure modes to instrument.
| Failure | Symptom | Control |
|---|---|---|
Index not ONLINE |
Empty or partial results, no error | Startup health check asserting index state; fail loud. count() now gives a cheap secondary probe |
| Dimension mismatch after model upgrade | Insert rejected or silently dropped | Assert dimensions at write; version the embedding model in node properties |
| Cypher timeout / slow traversal | p95 regression, queue backlog | Per-query timeout, query plan review, alert on p95 |
| Neo4j memory pressure | Driver errors, restart loops | Page cache/heap sizing in capacity plan; alert on GC and page faults |
| Connection pool exhaustion | Connection errors at traffic peaks | Connection budget per worker; load-test at 2× peak |
| Contract drift on minor upgrade | Fatal on boot after composer update |
Now the top of this list. Exact pin; adapter isolates blast radius to one class; changelog review before any bump |
Ramp-up. 3–5 days for a mid-level Laravel engineer to become productive with Cypher indexes and the adapter pattern; 2–3 weeks to genuine fluency in Neo4j tuning and query planning. Unchanged. The non-recoverable cost remains organizational: someone must own Neo4j as a production system.
Recommendation. Unchanged in direction, firmer in tone. Fund a two-week Phase 0/1 evaluation only, behind a hard benchmark gate against pgvector and a pre-commitment to the internal port abstraction. v0.14.0 does not warrant escalation — it is not a regression, and count() is a genuine small addition — but it does confirm the dependency's defining trait: it changes to serve the parent framework's contract, on someone else's schedule, with no Neo4j-specific hardening. Do not place this package on a roadmap critical path until it clears 1.0 or gains meaningful dependent adoption. The capability it unlocks is strategically interesting; the artifact is not yet something to build a platform on.
How can I help you explore Laravel packages today?