renatomaldonado/laravel-manticore-eloquent
Manticore Eloquent integrates Manticore Search with Laravel models, letting you define searchable Eloquent-style models, sync data, and run fast full-text and attribute queries using familiar Laravel ORM patterns.
Architecture fit
This package is an Eloquent driver for Manticore Search, not a general-purpose library. Architecturally it inserts a second persistence target beside the primary RDBMS: ManticoreModel subclasses read from Manticore indexes while the transactional source of truth stays in MySQL/Postgres. That dual-store shape — not the package code — is the dominant architectural decision. v2.0.0 sharpens the fit for AI/search-heavy products by making vector retrieval a first-class Eloquent concern: document-id KNN ("more like this"), the full {ef=…, rescore=…} option block, and VectorCast for round-tripping vector attributes as PHP arrays. Teams already using Manticore for lexical search can now add semantic/hybrid retrieval without standing up a separate vector database, which is a meaningful consolidation argument.
Two design choices cut both ways. First, option names/values are passed through to the server with shape validation rather than an allowlist — this is genuinely good forward compatibility (new Manticore options work without a package release) but it converts a class of developer mistakes into runtime server errors rather than client-side validation failures. Second, VectorCast is bidirectional and convenient, but it hides two server realities documented in the release: cosine columns normalize stored vectors on read-back (you get the unit vector, not what you wrote), and Manticore refuses UPDATE on a KNN-indexed column — vectors must change via replace(). Both are places where Eloquent's mental model ("what I saved is what I read back", "save() updates") silently diverges from engine behavior.
Integration feasibility
Low friction to install, moderate friction to operate. Composer install, connection config, $casts on the model — a competent Laravel developer is productive in a day. The friction is elsewhere: Manticore index DDL (including vector column type, dimensionality, and similarity function) sits outside Laravel's migration system, so schema lifecycle is partly manual or requires a bespoke migration harness. Any real use case also requires an embedding pipeline (external model/service, batching, backfill, re-embedding on model upgrades) that the package does not and should not provide.
Technical risk
knn() and whereKnn() changed signature, the wheres entry key ef became options, and any OPTION value that is a function call must now be wrapped in DB::raw(). Code that worked on 1.x throws InvalidArgumentException after upgrade. This is a coordinated-deploy change, not a drop-in.->option(). But validation is shape-based (identifier / number / bool / bare word / quoted literal), and DB::raw() is an explicit escape hatch. Anything reaching option() from user input should still be treated as untrusted and, ideally, never wired to request data at all.'oversampling' => 3.0, never 3) is exactly the kind of trap that passes code review and fails at runtime.UPDATE-rejected-on-KNN-column rule will produce confusing bugs (apparent vector drift; writes that fail or must be routed to replace()). Any code that diffs stored vectors or relies on save() for embeds is at risk.Key questions
knn() / whereKnn() / wheres['ef'] / DB::raw() changes, or is 1.x support continuing in parallel?replace()-instead-of-UPDATE pattern at the Eloquent layer, or must every consuming team reinvent it?Stack fit
Strong fit for Laravel 10/11 services already running Manticore for search, particularly ones with an embedding pipeline and a need for "similar to this document" UX. Weak fit for teams without an existing Manticore footprint — the operational surface of a second search engine is not justified by the package alone. Mixed-stack or non-Laravel services should not adopt it; the value is concentrated in the Eloquent integration, and the underlying protocol is reachable directly.
Migration path
Upgrade from 1.x is a bounded but real refactor, and should be treated as such:
->knn(, ->whereKnn(, and ->option( call site, plus anything reading the wheres array (ef → options).option() values: bare keywords, numbers, bools, arrays, and quoted literals are unaffected; every function-call value (expr('sum(lcs)') and friends) must be wrapped in DB::raw().For greenfield adoption, sequence: text search on Manticore → vector column + embeddings → VectorCast on the model → KNN query surface.
Compatibility
Two axes to pin in composer.json and in the deployment manifest: package major (2.x) and Manticore server version. Because the package deliberately does not enumerate valid options, the server becomes part of your dependency graph without appearing in a lockfile. Record the tested server version in runbooks and gate server upgrades behind the query test suite.
Sequencing
Land the upgrade as a discrete, no-feature PR: signature/DB::raw()/float fixes only, verified by the existing test suite plus a smoke test against staging Manticore. Adopt KNN document-id search and VectorCast in a subsequent PR. Do not combine a major package upgrade with a retrieval-quality or ranking change — you will not be able to attribute regressions.
Maintenance
Moderate and non-trivial. The package itself is small and releases appear infrequent, but there are three moving parts: the package, the Manticore server, and the embedding model. Server upgrades can change option grammar or KNN behavior without any package release — the passthrough design means the package will happily emit options your server no longer accepts. Budget for re-running the query suite on every Manticore upgrade. Re-embedding on model changes is a recurring project, not a one-off.
Support
Support load will concentrate in three predictable areas: "my option throws a syntax error" (passthrough with no client-side allowlist), "my vector came back different" (cosine normalization), and "save() fails on my embedding column" (no UPDATE on KNN-indexed columns). All three are documented in the README, which means the first-line answer is a doc link — but only if the team reads it. Write an internal one-pager covering exactly these three before rollout; it will pay for itself.
Scaling
Scaling splits into query-side and index-side. Query-side, k and ef are the levers: higher ef improves recall at direct latency cost, and rescore trades latency for ranking quality — these should be configuration, not hardcoded, and tuned against a representative corpus. Index-side, vector indexes are memory-hungry; capacity planning must account for dimensionality × document count, and for the fact that re-embedding means holding two generations of vectors during migration. replace()-based vector updates are heavier than in-place updates and should be batched.
Failure modes
ef/rescore misconfiguration — no error, just worse results. Needs retrieval-quality monitoring, not just error monitoring.UPDATE.knn() / wheres signature changes if any call site is missed.Ramp-up
Roughly one to two days for a Laravel developer to be productive with basic text search, plus two to three days to be genuinely safe with vector search — the concepts that take time are not Laravel concepts but Manticore ones: index DDL and similarity functions, HNSW/ef tuning, normalization behavior, and the immutability of KNN-indexed columns. The upgrade work itself is mechanical; the risk is in teams treating a major version bump as a minor one because the changelog is short and the API examples look familiar.
How can I help you explore Laravel packages today?