Skip to content

ykan821/elastx

Repository files navigation

Elastx

A PHP library for building Elasticsearch DSL queries and managing indices. Targets ES 7.x.

Covers all query types (compound, full-text, term-level, geo, joining, span, shape, specialized), all 47 aggregation types, and 26 search request parameters.

Requirements

  • PHP 7.2+
  • Elasticsearch 7.x

Installation

composer require ykan/elastx:^7

Quick Start

use ykan\DSL\Query;
use ykan\DSL\Type\FullText\Match_;

$query = new Query();
$query->bool([
    'must' => function (Query $q) {
        $q->match('title', function (Match_ $m) {
            $m->query('elasticsearch')->fuzziness('AUTO');
        });
    },
    'filter' => function (Query $q) {
        $q->range('price', [10, 100]);
        $q->term('status', 'published');
    },
    'should' => function (Query $q) {
        $q->match('category', 'database');
    },
]);
$query->aggs('price_stats')->stats(['field' => 'price']);
$query->aggs('by_category')->terms(['field' => 'category', 'size' => 10]);

echo $query->toJson();

Output:

{
    "query": {
        "bool": {
            "must": [{ "match": { "title": { "query": "elasticsearch", "fuzziness": "AUTO" } } }],
            "filter": [{ "range": { "price": { "gte": 10, "lte": 100 } } }, { "term": { "status": "published" } }],
            "should": [{ "match": { "category": "database" } }]
        }
    },
    "aggs": {
        "price_stats": { "stats": { "field": "price" } },
        "by_category": { "terms": { "field": "category", "size": 10 } }
    }
}

Retrieve aggregation results after search:

$results->aggregations(); // ['price_stats' => [...], 'by_category' => [...]]

Index Layer

use ykan\Index\Index;

class ProductIndex extends Index
{
    protected $name = 'products';

    protected $mappings = [
        'properties' => [
            'title'  => ['type' => 'text'],
            'price'  => ['type' => 'float'],
            'status' => ['type' => 'keyword'],
        ],
    ];
}

Register the ES client during bootstrap:

Index::setClient($client);

Search

use ykan\DSL\Query;

$results = ProductIndex::newQuery()
    ->bool('must', function (Query $q) {
        $q->match('title', 'elasticsearch');
    })
    ->sort('price', 'asc')
    ->size(20)
    ->get();

$results->total();        // int
$results->docs();         // array of _source
$results->ids();          // array of _id
$results->aggregations(); // aggregation results

For large result sets, use cursor() to iterate all matching documents via scroll:

$search = ProductIndex::newQuery()->match('title', 'test');

foreach ($search->cursor() as $doc) {
    // process $doc
}

Document CRUD

$doc = ProductIndex::newDoc(1);

$doc->create(['title' => 'New Product', 'price' => 29.99]);
$doc->index(['title' => 'Updated', 'price' => 39.99]); // create or overwrite
$doc->source();   // _source array
$doc->get();      // full document with _id, _version, etc.
$doc->exists();   // bool
$doc->update(['price' => 39.99]);
$doc->delete();

$doc->retryOnConflict(3)->update(['price' => 39.99]);

Bulk

use ykan\Index\Bulk;

$bulk = new Bulk(new ProductIndex());

$bulk->index(1, ['title' => 'Product A']);
$bulk->index(2, ['title' => 'Product B']);
$bulk->delete(3);
$bulk->execute();

Index Management

use ykan\Index\Manager;

$manager = new Manager(new ProductIndex());
$manager->create();
$manager->exists();  // true
$manager->delete();
$manager->putMapping($mapping);
$manager->addAlias('products_alias');
$manager->removeAlias('products_alias');
$manager->swapAlias('products_alias', 'old_index');

Rebuild

Zero-downtime rebuild: create new index, import data, swap alias.

use ykan\Index\Rebuild;

$rebuild = new Rebuild(new ProductIndex());

// Run with Index::source()
$rebuild->batchSize(500)->run();

// Rollback to previous index
$rebuild->rollback();
$rebuild->rollback('products_20260525_120000'); // or specify target

// Clean orphan indexes
$rebuild->orphans();        // ['products_20260527_100000', ...]
$rebuild->cleanOrphans();

Provide data by overriding source(), or pass a custom source to run():

// Override in Index subclass
class ProductIndex extends Index
{
    public function source(array $options = []): iterable
    {
        foreach (Product::all() as $product) {
            yield $product->id => $product->toArray();
        }
    }
}

// Or pass at call time
$rebuild->source($rows)->run();

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages