avadim / manticore-query-builder-laravel
Native ManticoreSearch database driver for Laravel
Package info
github.com/aVadim483/manticore-query-builder-laravel
pkg:composer/avadim/manticore-query-builder-laravel
Requires
- php: ^8.2
- ext-json: *
- ext-pdo: *
- ext-pdo_mysql: *
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/pagination: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- ext-pdo_sqlite: *
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpunit/phpunit: ^10.5|^11.0|^12.0|^13.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-27 15:36:20 UTC
README
ManticoreSearch Database Driver for Laravel
A native Laravel Database driver for ManticoreSearch. Use Laravel's database manager, Query Builder, collections, pagination, query events and Schema Builder over PDO's MySQL protocol.
Version 4.0.1 is the stable native driver.
See the 4.0.1 release notes for changes and limitations.
The 3.x wrapper API and its dependency on avadim/manticore-query-builder-php have been removed.
Existing applications should follow the 3.x migration guide.
Contents
- Requirements
- Installation
- Configuration
- Queries and results
- Writes and transactions
- Search extensions
- Schema and migrations
- Differences from Laravel Query Builder
- Testing and benchmarks
Requirements
- PHP 8.2+ and Laravel 11–13, subject to each framework version's PHP requirements.
- PHP extensions
pdo,pdo_mysqlandjson. - ManticoreSearch 28.6.6+ with the MySQL protocol enabled, normally on port 9306.
- Buddy for AUTOCOMPLETE; the KNN library and a vector index for KNN queries.
The supported server floor is 28.6.6. CI tests its official image pinned by digest and the moving
latest image; see the server compatibility policy.
Support for older servers, Lumen and general Eloquent compatibility is not established.
Installation
To install the stable 4.x driver:
composer require "avadim/manticore-query-builder-laravel:^4.0"
To test subsequent development changes:
composer require avadim/manticore-query-builder-laravel:dev-dev-4.x
The migration guide has been exercised on a Laravel 12 application with its original documents preserved. Other applications require their own checks.
Laravel discovers avadim\Manticore\Laravel\ServiceProvider automatically.
If package discovery is disabled, register that provider in bootstrap/providers.php.
DatabaseServiceProvider remains an alternative registration entry point for applications
that used the native driver earlier in 4.x development. Registering either provider is sufficient.
There is no ManticoreDb alias or package configuration to publish.
Configuration
Add this entry to connections in config/database.php:
'manticore' => [ 'driver' => 'manticore', 'host' => env('MANTICORE_HOST', '127.0.0.1'), 'port' => env('MANTICORE_PORT', 9306), 'username' => env('MANTICORE_USER'), 'password' => env('MANTICORE_PASS'), 'timeout' => env('MANTICORE_TIMEOUT', 5), 'prefix' => env('MANTICORE_PREFIX', ''), 'cast_attributes' => true, 'options' => [], ],
Keep the application's primary database as its default and use DB::connection('manticore')
explicitly. Additional connection names can use the same manticore driver with their own settings.
The driver reads no config/manticore.php, MANTICORE_CONNECTION, force_prefix or
lazy_insert_ids. Laravel always prefixes builder table names; use products, without ?.
Raw SQL is not rewritten. PDO emulated prepares must stay enabled.
Use DB::listen() for logging and DB::purge() / DB::reconnect() for worker connections.
See the migration guide for logging and schema-cache lifecycle examples.
Queries and results
use Illuminate\Support\Facades\DB; $db = DB::connection('manticore'); $query = $db->table('products')->select('id', 'title')->match('galaxy') ->where('price', '>', 100)->orderBy('id'); $rows = $query->get(); // Collection<stdClass> $first = (clone $query)->first(); // stdClass|null $title = $first?->title; $page = (clone $query)->paginate(20); $lazy = (clone $query)->lazyById(500); // LazyCollection<stdClass>
Query Builder select() returns the builder and configures columns. Connection
$db->select($sql, $bindings) executes SQL and returns array<stdClass> without builder casts.
get(['id', 'title']) accepts an array; a comma-separated string is not a column list.
Rows use object properties. To get arrays, use
$rows->map(static fn ($row) => (array)$row)->all().
Use whereFullText() for text input and an explicit field scope. It returns the builder;
get(), first() or search() executes the query. Search operators are escaped by default.
$rows = $db->table('products')->whereFullText('title', 'galaxy phone')->get(); $rows = $db->table('products')->whereFullText(['title', 'body'], 'galaxy') ->where('stock', '>', 0)->get();
Repeated calls produce one MATCH. Text OR groups use orWhereFullText(); OR between text
and attributes is unsupported. See full-text examples and options.
JSON filters use Laravel's familiar syntax:
$rows = $db->table('products')->where('details->color', 'red') ->whereJsonContains('details->tags', 'sale')->whereJsonLength('details->tags', '>=', 2)->get();
See JSON methods and Manticore-specific semantics.
cursor() returns a LazyCollection from one SELECT. lazy() reads offset pages; lazyById()
walks an ID boundary. cursorPaginate() supports selected integer/bool/timestamp keys with ID
as the final tie-breaker. Floats, computed keys, KNN and grouped/faceted cursor pagination are rejected.
See the full driver reference for exact totals and streaming limits.
Writes and transactions
$id = $db->table('products')->insertGetId(['title' => 'Galaxy', 'price' => 120.5]); $db->table('products')->where('id', $id)->update(['price' => 99.5]); $db->table('products')->replace(['id' => $id, 'title' => 'Galaxy phone', 'price' => 99.5]); $db->table('products')->where('id', $id)->delete(); $db->transaction(function () use ($db) { $db->table('products')->insert(['id' => 42, 'title' => 'Phone', 'price' => 50]); });
insert() / replace() return bool, update() / delete() return affected counts,
and insertGetId() / replaceGetId() return int|string. Put explicit IDs in the document.
SQL failures throw Illuminate\Database\QueryException; zero affected rows can be successful.
REPLACE replaces the entire document, resetting omitted attributes, and supports full-text changes.
Nested transactions, DDL inside transactions and retry counts other than one are rejected.
Writes are never automatically replayed after a lost response.
Search extensions
$result = $db->table('products')->match('galaxy')->select('id', 'title') ->withScore()->fieldWeights(['title' => 10])->ranker('bm25') ->withHighlight(['title']) ->facet('brand', 'brands', 10)->limit(20)->search(); $rows = $result->rows(); $total = $result->meta()->totalFound(); $brands = $result->facet('brands');
search() captures rows, metadata and facets before query listeners. get() fetches rows only.
Other extensions include option(), knn(), MVA ANY/ALL, geo distance, and connection-level
keywords(), suggest(), qsuggest(), snippets(), autocomplete() and generic call().
See the reference for signatures and server requirements.
The hybrid search guide progresses from a minimal call to advanced
options. hybridSearch() returns HybridResult, hybridGet() returns Collection<stdClass>,
and hybridFirst() returns stdClass|null. These execute immediately; the alpha.2
hybrid(...)->search() call is removed. knn()->match() retains intersection semantics.
Capability probes and RT replacement are documented separately;
replacement requires maintenance and does not promise zero downtime.
Schema and migrations
use avadim\Manticore\Laravel\Database\ManticoreBlueprint; use Illuminate\Support\Facades\Schema; Schema::connection('manticore')->create('products', function (ManticoreBlueprint $table) { $table->id(); $table->text('title'); $table->string('brand'); $table->float('price'); $table->json('details'); $table->multi('tags'); });
CREATE, DROP and ALTER ADD/DROP use Laravel Schema Builder, including multi64(),
floatVector() and CREATE setting(). Unsupported schema operations fail explicitly.
Extended ALTER supports settings changes/inspection,
int-to-bigint change() and table rename() with Buddy.
Keep the migration repository in the primary SQL database; select Manticore inside individual migrations.
DDL is not transactional. See the migration example.
Differences from Laravel Query Builder
The complete API profile and frozen contracts cover every reviewed public method, explicit rejections, argument defaults, results and exceptions.
The select(), get(), first() and lazy() contracts are preserved: a builder, collection,
object/null and lazy collection respectively. The differences below matter when porting queries
from Laravel's standard SQL drivers. Standard Query Builder capabilities also depend on the
selected database; see Laravel's reference.
| Operation | Standard Laravel Query Builder | This Manticore driver |
|---|---|---|
| Substring matching | whereLike() or where('name', 'like', '%text%') use SQL LIKE |
LIKE in WHERE is unsupported. Use match() with full-text index rules for TEXT, or REGEX() through a SELECT alias for STRING. Their search semantics differ |
| Full-text search | whereFullText() with database-specific options |
whereFullText() / orWhereFullText() compile to one MATCH, escape operators by default and accept only the escape option. Text/attribute OR is unsupported; match() accepts native expressions |
| JSON predicates | details->color syntax and whereJson*() helpers on supported databases |
Arrow paths and whereJson*() are supported. Contains/overlaps test array elements; key checks treat JSON null as absent. See JSON semantics |
| Reading JSON | Query Builder returns PDO values without automatically decoding JSON into PHP arrays | By default, the builder reads DESCRIBE and converts JSON to arrays; cast_attributes => false disables conversion |
get() / cursor() without limit() |
No row-count limit is added to the SQL | Manticore defaults to 20 returned rows. Set limit() or use lazy() / chunk() for batched traversal |
| JOIN, UNION, DISTINCT, WHERE EXISTS, locks | Methods expose SQL constructs supported by the selected database | The current driver rejects these constructs |
Updating text with update() |
Updates the specified text columns | Full-text fields require replace(), which replaces the whole document and resets omitted fields |
upsert() |
Native insert/update by unique keys under the SQL driver's rules | Emulated lookup + INSERT/REPLACE; non-updated fields are preserved. Writes share one transaction, but lookups have no isolation and arbitrary lookup attributes have no uniqueness constraint |
| Writes inside a transaction | INSERT, UPDATE and DELETE can be rolled back on transactional SQL tables | Manticore transactions cover INSERT/REPLACE/DELETE; UPDATE is not rolled back. Driver nesting, savepoints and retries are rejected |
cursorPaginate() |
Builds cursor boundaries from selected ordering columns under Laravel's constraints | Only selected integer/bool/timestamp keys ending in ID are supported; KNN, groups, facets and float/string/JSON/MVA/computed keys are rejected |
LIKE alternatives follow Manticore field types and REGEX. See also result limits, transactions and driver restrictions.
Use bound parameters with whereRaw() / selectRaw(); DB::raw() is for trusted SQL.
Escape literal MATCH text with
avadim\Manticore\Laravel\Database\ManticoreQueryBuilder::escapeMatch().
Build a single MATCH expression per query.
Testing and benchmarks
# From a repository clone composer install composer test
Integration tests use MANTICORE_TEST_HOST / MANTICORE_TEST_PORT (127.0.0.1:9306).
Set MANTICORE_TEST_BUDDY=1 to require AUTOCOMPLETE. Tests skip unavailable server features locally;
CI uses --fail-on-skipped. The default bootstrap rejects loading standalone PHP-builder classes.
Tests also require pdo_sqlite for the primary migration repository.
See verification instructions for isolated production consumers and test matrices.
Benchmarks compare the native driver with PDO and the official HTTP client. Performance covers current bulk-write comparisons and earlier upsert, read, hybrid and worker measurements, with explicit versions and workload limits.