Search by

avadim / manticore-query-builder-php

avadim

Manticore Search Query Builder for PHP (unofficial PHP client)

Package info

github.com/aVadim483/manticore-query-builder-php

pkg:composer/avadim/manticore-query-builder-php

Statistics

Installs: 6 220

Dependents: 2

Suggesters: 0

Stars: 8

Open Issues: 0

v2.5.0 2026-10-05 09:57 UTC

README

English | Русский

GitHub Release Packagist Downloads GitHub License Static Badge

Manticore Search Query Builder for PHP (unofficial PHP client)

Query Builder for Manticore Search in PHP with Laravel-like syntax

Features

  • MySQL multiple connections via PDO (because Manticore is SQL-first)
  • Placeholders as prefix in table names
  • Named parameters in expressions
  • Clear Laravel-like syntax
  • Multiple INSERT and REPLACE
  • Support MATCH() and multi-level WHERE for SELECT
  • Support faceted search, JOIN of two tables and KNN vector search
  • MVA ANY/ALL filters and geographic radius searches, distance columns and sorting
  • Column types are applied in both directions: PHP values on write, PHP types on read
  • The helpers of the Laravel query builder: aggregates, chunked walks, conditional building, conditions on dates, upsert and the rest
  • CALL helpers: callAutocomplete(), callSuggest(), callQsuggest(), callKeywords(), callSnippets() and callPq() (percolate queries)
  • A rejected read throws, a rejected write answers with false or zero and keeps its reason
  • PSR-3 logging of queries and EXPLAIN of full-text expressions

Manticore Search server documentation: https://manual.manticoresearch.com/

Related packages

This is the standalone builder: PHP and PDO are all it needs, and the two packages below are built on it.

Requirements

  • PHP 7.4 or above with the PDO extension
  • Manticore Search reachable over its SQL interface (port 9306 by default). The HTTP/JSON API is not used.

What needs more than the bare server

Most of the builder talks to the server and nothing else. A few methods depend on a library loaded by the server, or on Manticore Buddy running next to it:

Method Needs Without it
everything else the server alone —
floatVector(), whereKnn() KNN library the table cannot be created: knn library not loaded
columnar(), columnEngine('columnar'), engine => 'columnar' Columnar library the table cannot be created: columnar library not loaded
rename() Manticore Buddy the server answers with a syntax error
callAutocomplete() Manticore Buddy and table infixes the server rejects the call

The KNN and columnar libraries both come from the Manticore Columnar Library; the secondary-index library ships with them and speeds filtering up without changing anything in the API.

The libraries have to match the daemon: it checks their ABI version on load and refuses one that is too new, saying so only when started with --console. Which ones did load is written in the version string:

SHOW STATUS LIKE 'version';
-- 28.6.6 … (columnar 13.8.3 …) (secondary 13.8.3 …) (knn 13.8.3 …)

A library that is missing from that line is not loaded, whatever the files on disk say.

Installation

composer require avadim/manticore-query-builder-php

Quick start guide

use avadim\Manticore\QueryBuilder\Builder as ManticoreDb;
use avadim\Manticore\QueryBuilder\Schema\SchemaTable;

// Define config
$config = [
    'defaultConnection' => 'default',
    'connections' => [
        // Default connection which will be used with environment variables
        'default' => [
            'host' => 'localhost',
            'port' => 9306,
            'username' => null,
            'password' => null,
            'timeout' => 5,
            'prefix' => 'test_', // prefix that will replace the placeholder "?<table_name>"
            'force_prefix' => false,
        ],
    ],
];

// Init query builder
ManticoreDb::init($config);

// Create table
ManticoreDb::create('?products', function (SchemaTable $table) {
    $table->timestamp('created_at');
    $table->string('manufacturer');
    $table->text('title');
    $table->json('info');
    $table->float('price');
    $table->multi('categories');
    $table->bool('on_sale');
});

// Insert single row
$singleRow = [
    'created_at' => time(),
    'manufacturer' => 'Samsung',
    'title' => 'Galaxy S23 Ultra',
    'info' => ['color' => 'Red', 'storage' => 512],
    'price' => 1199.00,
    'categories' => [5, 7, 11],
    'on_sale' => true,
];
$id = ManticoreDb::table('?products')->insertGetId($singleRow);
// insert() returns true/false, insertGetId() - the <id> of the new record

// Insert multiple rows
$multipleRows = [
    [
        'created_at' => time(),
        'manufacturer' => '...',
        'title' => '...',
        'info' => [],
        // ...
    ],
    [
        'created_at' => time(),
        'manufacturer' => '...',
        'title' => '...',
        'info' => [],
        // ...
    ],
];
$res = ManticoreDb::table('?products')->insertResultSet($multipleRows);
// $res->result() => array of <id> of new records

// Update and delete answer with the number of affected rows
$updated = ManticoreDb::table('?products')->where('price', '<', 100)->update(['on_sale' => true]);
$deleted = ManticoreDb::table('?products')->where('price', '<', 1)->delete();

// A failed write is not thrown - insert() gives false, update()/delete() give 0,
// and the reason is in the ResultSet left behind
if (!ManticoreDb::table('?products')->insert($singleRow)) {
    $error = ManticoreDb::lastResultSet()->error();
}

// A failed read is thrown instead: null in place of the rows would look like an empty table
try {
    $rows = ManticoreDb::table('?products')->where('nosuchcolumn', 1)->get();
}
catch (�vadim\Manticore\QueryBuilder\QueryErrorException $e) {
    $error = $e->getMessage();
}

// Search: get() returns rows keyed by document id, with values cast back to PHP types
// ('info' as an array, 'categories' as int[], 'on_sale' as bool)
$rows = ManticoreDb::table('?products')->match('galaxy')->where('price', '>', 1100)->get();

Documentation

More detailed documentation is available in the /docs folder: configuration, searching, tables, result set, logging.

Документация на русском языке: README.ru.md и папка /docs/ru.

Tests

vendor/bin/phpunit --testsuite unit   # SQL building and parsing, no server needed
vendor/bin/phpunit                    # adds the integration tests
vendor/bin/phpunit --exclude-group buddy # omit new autocomplete tests on a server without Buddy

The integration tests create and drop their own tables on a live server. They are skipped, not failed, when nothing listens on MANTICORE_HOST:MANTICORE_PORT (127.0.0.1:9306 by default).

GitHub Actions runs the full suite against Manticore 29.9.0 on PHP 7.4 and 8.4, plus a separate latest run on PHP 8.4. Before PHPUnit, tests/ci/check-manticore.php waits for working SQL, Columnar filtering, KNN search and a Buddy table rename, using temporary tables that it removes afterwards. Run this probe only against a test server. The readiness timeout defaults to 90 seconds (MANTICORE_READY_TIMEOUT, 1–120 seconds); the probe uses the same host and port environment variables as the integration tests.

CI uses --fail-on-skipped --fail-on-incomplete: missing components cannot produce a successful full run. Local test skipping remains unchanged. JUnit reports, readiness output and container diagnostics are uploaded as artifacts, including on failure, and retained for 14 days. The workflow also supports manual runs from the Actions tab once the workflow is on the default branch. Both pinned-version and latest runs must pass.

Want to support?

If you find this package useful, just give me a star on GitHub :)