hlquery/php-client

PHP client library for hlquery search engine

Maintainers

Package info

github.com/hlquery/php-api

pkg:composer/hlquery/php-client

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 2

Open Issues: 3

dev-unstable 2026-04-30 19:04 UTC

This package is auto-updated.

Last update: 2026-04-30 19:04:54 UTC


README

hlquery logo

A modular PHP client library for hlquery, designed with a familiar and intuitive API structure.

Follow hlquery Commit Activity hlquery License

Compact PHP client for hlquery. No framework required, no extra runtime dependencies beyond curl and json.

Included in the current client:

  • Collections
  • Documents
  • Search
  • SQL
  • Keys
  • Aliases
  • Overrides
  • Synonyms
  • Stopwords
  • System helpers, including etc()

Install

Requirements:

  • PHP >= 7.0
  • ext-curl
  • ext-json

Local usage:

require_once __DIR__ . '/lib/autoload.php';

use Hlquery\Client;

$client = new Client(getenv('HLQ_BASE_URL') ?: (getenv('HLQUERY_BASE_URL') ?: 'http://localhost:9200'));

Composer:

composer require hlquery/php-client
require_once __DIR__ . '/vendor/autoload.php';

use Hlquery\Client;

$client = new Client('http://localhost:9200');

Auth:

$client = new Client('http://localhost:9200', [
    'token' => 'your_token_here',
    'auth_method' => 'bearer', // or 'api-key'
]);

// or later
$client->setAuthToken('your_token_here', 'bearer');

Quick Start

require_once __DIR__ . '/lib/autoload.php';

use Hlquery\Client;

$client = new Client('http://localhost:9200');

$health = $client->health();
if ($health->isSuccess()) {
    $body = $health->getBody();
    echo "status: " . ($body['status'] ?? 'ok') . PHP_EOL;
}

$collections = $client->listCollections(0, 10);
print_r($collections->getBody());

Example API Responses

Captured from a local http://localhost:9200 server.

$client->health()->getBody():

{
  "server": "hlquery",
  "status": "ok",
  "version": "1.0"
}

$client->search('readme_demo', ['q' => 'search', 'query_by' => 'title,content', 'limit' => 10])->getBody():

{
  "hits": [
    {
      "document": {
        "id": "doc-2",
        "title": "Search Engineering Notes"
      },
      "highlights": {
        "title": "<em>Search</em> Engineering Notes"
      }
    }
  ],
  "found": 1
}

Reduce Text Example

Use executeRequest() to call custom module routes directly:

$moduleResponse = $client->executeRequest('GET', '/modules/<name>/<route>', null, [
    'q' => 'example query',
]);

print_r($moduleResponse->getBody());

Common Examples

Create a collection

This client uses a service-based API similar to Typesense. The main difference is that hlquery expects the collection name as the first argument, instead of inside the schema array.

<?php

require_once __DIR__ . '/vendor/autoload.php';

use Hlquery\Client;

$client = new Client('http://localhost:9200');

$collections = $client->collections();

$schema = [
    'fields' => [
        ['name' => 'id', 'type' => 'string'],
        ['name' => 'title', 'type' => 'string'],
        ['name' => 'author', 'type' => 'string'],
        ['name' => 'year', 'type' => 'int32'],
    ],
];

$response = $collections->create('books', $schema);

print_r($response->getBody());

Add documents

$documents = $client->documents();

$documents->add('books', [
    'id' => '1',
    'title' => 'The Hobbit',
    'author' => 'J.R.R. Tolkien',
    'year' => 1937,
]);

$documents->import('books', [
    [
        'id' => '2',
        'title' => 'Dune',
        'author' => 'Frank Herbert',
        'year' => 1965,
    ],
    [
        'id' => '3',
        'title' => 'Neuromancer',
        'author' => 'William Gibson',
        'year' => 1984,
    ],
]);

Search without query_by

If q is set and query_by is omitted, the client tries to use the collection's searchable_fields.

$results = $client->search('books', [
    'q' => 'tolkien',
    'limit' => 10,
]);

SQL

Use the SQL service object for a nested API style:

$sql = $client->sql();

Basic SQL example:

$sql = $client->sql();

$results = $sql->query('products', 'SELECT id, title FROM products ORDER BY title ASC LIMIT 3;');

if ($results->isSuccess()) {
    $body = $results->getBody();
    print_r($body['rows'] ?? []);
}

Collection-bound SQL SELECT:

$sql = $client->sql();

$results = $sql->query(
    'products',
    'SELECT id, title, price FROM products WHERE price >= 100 ORDER BY price DESC LIMIT 5;'
);

Top-level SQL execution:

$sql = $client->sql();

$rows = $sql->raw('SHOW COLLECTIONS;');

$insert = $sql->execute(
    "INSERT INTO products (id, title, price) VALUES ('sku-9', 'Camp Stove', 89);"
);

Vector Search Notes

For vector search, the important part is usually not the raw embedding array in the example, but the search knobs around it.

  • field_name must match the vector field stored in your collection.
  • topk controls how many nearest matches you ask for back.
  • threshold can cut off weak matches early.
  • nprobe is the main recall/speed tradeoff on IVF-style indexes.

Briefly: a higher nprobe checks more partitions, which usually improves recall but costs more CPU and latency. Start small, then raise it only if you are missing obvious neighbors. If you are tuning quality, nprobe is one of the first parameters worth testing.

Read a document

$doc = $client->getDocument('books', '1');
print_r($doc->getBody());

Update or delete a document

$client->documents()->update('books', '1', [
    'year' => 1938,
]);

$client->documents()->delete('products', 'sku-3');

Synonyms

$client->synonyms()->create('products', 'shoe_terms', [
    'root' => 'shoe',
    'synonyms' => ['sneaker', 'trainer'],
]);

$synonyms = $client->synonyms()->list('products');
print_r($synonyms->getBody());

Global synonyms are also supported:

$client->synonyms()->createGlobal('global_shoe_terms', [
    'root' => 'shoe',
    'synonyms' => ['sneaker', 'trainer'],
]);