Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Elasticbuilder Laravel Package

brokerexchange/elasticbuilder

Brokerexchange ElasticBuilder offers a fluent, Laravel-friendly query builder for Elasticsearch, simplifying complex search queries, filters, and aggregations without hand-writing raw JSON while keeping search logic readable and maintainable in PHP.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package alongside the official Elasticsearch PHP client, since the builder produces query DSL arrays that still need a transport to reach the cluster:

composer require brokerexchange/elasticbuilder
composer require elasticsearch/elasticsearch

There is no evidence of a Laravel auto-discovered service provider or facade in the package metadata, so assume it is a plain library you instantiate yourself. Start by reading, in this order:

  1. src/ — identify the entry point (typically something like a Query, Builder, or Search class). That class's public methods are the API surface; there is little documentation to fall back on.
  2. tests/ — for a small package this is the most reliable usage documentation. Copy the fixtures; they show exact method names and the array shape each clause emits.
  3. composer.json — check the PHP constraint and whether it pulls a specific elasticsearch/elasticsearch major version. This determines ES 7 vs ES 8 compatibility.

First use case — a filtered listing query against an index of, say, orders:

use Brokerexchange\ElasticBuilder\...; // confirm actual namespace from src/

$body = (new Query())
    ->bool()
        ->filter('status', 'paid')
        ->must('customer.name', 'match', $term)
        ->range('created_at', ['gte' => now()->subDays(30)->toIso8601String()])
    ->getBody();

$response = app(Client::class)->search([
    'index' => 'orders',
    'body'  => $body,
]);

Do not trust those method names verbatim — verify against src/ first. The point of the exercise is to confirm the builder's return type: most builders of this kind return a plain array, which is what makes them easy to slot into existing code.

Implementation Patterns

Wrap the builder, don't sprinkle it. The value of a DSL builder is composition, so keep it behind a query object per search screen rather than calling it inline in controllers:

final class OrderSearch
{
    public function build(OrderFilters $filters): array
    {
        $q = (new Query())->bool();

        if ($filters->status) {
            $q->filter('status', $filters->status);
        }
        if ($filters->term) {
            $q->must('description', 'multi_match', [
                'query'  => $filters->term,
                'fields' => ['description^2', 'customer.name'],
            ]);
        }

        return $q->getBody();
    }
}

This keeps the "which clauses are optional" logic in one testable place and gives you a seam to swap the package out later.

Bind it into the container. Register the client and a thin search service in a ScoutServiceProvider-style provider (or your own):

$this->app->singleton(Client::class, fn () => ClientBuilder::create()
    ->setHosts(config('elastic.hosts'))
    ->build());

Reuse clause fragments. Build common filters once (tenant isolation, soft-delete exclusion) as arrays or small builder helpers, then merge them into every query. Because the output is an array, this is just PHP array manipulation — no package support required.

Pair with pagination and sorting deliberately. Keep from/size out of the builder if it doesn't model them, and apply them at the call site; this avoids the builder silently overriding your pagination when you merge arrays.

Test by asserting the array, not the cluster. The killer feature of a DSL builder is that you can unit-test query shape without Elasticsearch running:

it('applies a status filter', function () {
    $body = (new OrderSearch())->build(new OrderFilters(status: 'paid'));

    expect($body)->toMatchArray([
        'query' => ['bool' => ['filter' => [['term' => ['status' => 'paid']]]]],
    ]);
});

Add one or two integration tests against a real (or containerised) cluster for the paths where ES semantics matter — analyzers, minimum_should_match, nested documents.

Consider Laravel Scout for read paths. If most of your search is simple full-text over Eloquent models, Scout plus a custom searchableAs/toSearchableArray may cover 80% of it; reach for this builder for the complex filtered/aggregated screens Scout handles awkwardly.

Gotchas and Tips

  • Version coupling is the main risk. A DSL builder encodes clause syntax for a specific ES major version. ES 8 dropped mapping types and changed some client behaviours; confirm the package's target before mixing it with a newer cluster. Pin the version in composer.json — at 11 stars this is not a package you want floating.
  • must vs filter vs should. Putting exact-match filters in must forfeits the filter cache and skews _score. Default to filter for anything non-relevance and only use must for scoring clauses. Builders rarely enforce this; you have to.
  • bool defaults are a trap. With no must/filter, should clauses default to minimum_should_match: 1, but as soon as a must or filter exists they become optional. If your results suddenly widen when you add a filter, this is why — set minimum_should_match explicitly.
  • Deep pagination. from + size is capped at index.max_result_window (10k by default). For exports or infinite scroll, use search_after with a stable sort (include a tiebreaker like _id or a unique field).
  • Empty clauses. Guard against emitting ['term' => ['status' => null]] or empty must arrays — ES will error or return nothing. Normalise nulls in your filter DTO before they reach the builder.
  • Debugging: always json_encode($body, JSON_PRETTY_PRINT) before sending. If ES rejects the query, run it through _validate/query?explain=true — the error messages point at the exact clause path, which is far more useful than the exception the transport throws.
  • Sort syntax varies by field type. Sorting on a text field fails unless it has a keyword sub-field; sorting on a missing field needs unmapped_type. These are mapping problems, not builder problems, but they surface at the builder call site.
  • Aggregations are usually out of scope. Most lightweight DSL builders cover query only. If you need aggs, check before committing; you may end up hand-rolling the aggregation array and merging it in.
  • Extension point: if a clause you need isn't modelled, most such builders accept a raw array escape hatch (or you can merge the array yourself post-build). Prefer that over forking — a fork of an 11-star package is a maintenance burden you own forever.
  • Vendor risk: low stars, single maintainer, recent release. Read the source (it's likely small enough to do in one sitting) so you can patch it in-place via a Composer repositories path override or a fork if it's abandoned.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk