symfony/ai-elasticsearch-store
Elasticsearch vector store integration for Symfony AI Store, adding kNN search and dense_vector support so your Symfony AI apps can store and query embeddings directly in Elasticsearch.
Architecture fit
symfony/ai-store abstractions, not a composer require and go.symfony/ai-store defines the vector-store contract (StoreInterface), and this package implements it against Elasticsearch's dense_vector field type and kNN search. Value only exists if we adopt the Symfony AI store abstraction upstream; otherwise the package has no entry point in our codebase.user_id, tenant_id, locale, published_at pre-filters alongside similarity. This is the capability that usually justifies Elasticsearch over pgvector.add, remove, query, and now clear() (v0.11.0) are implemented. remove() and clear() arriving late in the version history is a signal that lifecycle operations have been the lagging surface area — worth confirming against our deletion/GDPR requirements.Integration feasibility
symfony/ai-store + this bridge, bind StoreInterface in AppServiceProvider, and use the package's own store class. Lowest code, highest coupling to Symfony AI's release train.Symfony\Component\AI\StoreInterface. Costs roughly a day of work, buys insulation from pre-1.0 churn. Recommended for anything beyond a prototype.dense_vector and a fixed dimensionality, and an operational story for reindexing. The PHP client (elasticsearch/elasticsearch) is a separate dependency and a separate version-compatibility axis.config/services.php ourselves.Technical risk
symfony/ai monorepo, so governance and bus factor are Symfony's, not a single maintainer's. This is early-adopter territory, not abandoned territory — but we should not be the only production consumer of a code path we depend on._bulk responses now throw. This is the correct behavior and a genuine reliability improvement, but it is a runtime behavior change in a patch release. Any ingestion path we build must assume exceptions on partial bulk failure and must not treat "request returned 200" as "documents were indexed."dense_vector dims are fixed at index creation. Switching embedding models (768 → 1536 → 3072) means a full reindex into a new index plus a cutover. This is the single most under-estimated cost in vector search projects and should be planned for on day one, not deferred.knn: false / exact search is materially slower. Product requirements must state a recall tolerance.Key questions
pgvector on existing Postgres?symfony/ai-store acceptable as an application-level dependency, or do we need an internal adapter to absorb pre-1.0 churn?_bulk failures throw?remove() and clear() as implemented, and how do we verify deletion across replicas?Stack fit
symfony/ai-store StoreInterface → this bridge → Elasticsearch PHP client. Collapsing the adapter layer is fine for a spike and a mistake for a product.try/catch, per-item error extraction from the bulk response, bounded retries with backoff, and a dead-letter path. v0.14.1 makes failures visible; we still own what happens next.Migration path
Compatibility
symfony/ai-store release train closely (v0.11.0 and v0.12.0 were constraint-preparation releases). Upgrading one component without the other is unsupported; upgrade them together._bulk behavior change means integration tests must be updated to assert exceptions on partial failure. Any existing test that mocks a partially-failed bulk response as a success is now wrong.Sequencing
symfony/ai-elasticsearch-store to an exact version (≥ v0.14.1)Maintenance
symfony/ai-store, and the Elasticsearch PHP client. Floating constraints on three interdependent pre-1.0 components is how a routine composer update becomes an incident.Support
symfony/ai issues/PRs). Reasonable responsiveness is likely given Symfony's process, but there is no SLA and no commercial backing. Do not build a customer-facing commitment on this package without an internal fallback path.Scaling
keyword from day one. Getting this wrong means a reindex.Failure modes
Ramp-up
How can I help you explore Laravel packages today?