jackardios/elastic-query-wizard

Laravel Elastic Query Wizard

Installs: 437

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 2

Forks: 0

pkg:composer/jackardios/elastic-query-wizard

v2.2.0 2025-04-25 06:55 UTC

This package is auto-updated.

Last update: 2026-02-09 22:33:42 UTC


README

Tests License: MIT PHP Version

A powerful Laravel package for building Elasticsearch queries with JSON:API style filtering, sorting, and relation loading. Built on top of laravel-query-wizard and es-scout-driver.

Table of Contents

Features

  • Declarative API — define allowed filters, sorts, and includes in one place
  • Security — only explicitly allowed parameters are applied to queries
  • Full-text Search — match, multi_match, fuzzy and other Elasticsearch query types
  • Geo Queries — filter and sort by geographic coordinates
  • Flexible Configuration — pass additional parameters to any query
  • Eloquent Integration — load relations and accessors after Elasticsearch query execution
  • JSON:API Compatible — standardized query parameter format

Requirements

Installation

composer require jackardios/elastic-query-wizard

Make sure your model uses the Searchable trait from es-scout-driver:

use Jackardios\EsScoutDriver\Searchable;

class Post extends Model
{
    use Searchable;

    // ...
}

Quick Start

Basic Example

use Jackardios\ElasticQueryWizard\ElasticQueryWizard;
use Jackardios\ElasticQueryWizard\ElasticFilter;
use Jackardios\ElasticQueryWizard\ElasticSort;

// GET /posts?filter[status]=published&filter[title]=laravel&sort=-created_at&include=author

$posts = ElasticQueryWizard::for(Post::class)
    ->allowedFilters([
        ElasticFilter::term('status'),
        ElasticFilter::match('title'),
        ElasticFilter::range('created_at'),
    ])
    ->allowedSorts([
        ElasticSort::field('created_at'),
        ElasticSort::field('title'),
    ])
    ->allowedIncludes(['author', 'comments'])
    ->build()
    ->execute()
    ->models();

Geo Filtering Example

// GET /places?filter[nearby][lat]=55.75&filter[nearby][lon]=37.62&filter[nearby][distance]=10km

$places = ElasticQueryWizard::for(Place::class)
    ->allowedFilters([
        ElasticFilter::geoDistance('location', 'nearby'),
        ElasticFilter::term('category'),
    ])
    ->allowedSorts([
        ElasticSort::geoDistance('location', 55.75, 37.62, 'distance'),
    ])
    ->build()
    ->execute()
    ->models();

Full-text Search Example

// GET /articles?filter[search]=elasticsearch tutorial&filter[category]=tech

$articles = ElasticQueryWizard::for(Article::class)
    ->allowedFilters([
        ElasticFilter::multiMatch(['title^2', 'body', 'tags'], 'search')
            ->withParameters([
                'type' => 'best_fields',
                'fuzziness' => 'AUTO',
            ]),
        ElasticFilter::term('category'),
    ])
    ->defaultSorts('-created_at')
    ->build()
    ->execute()
    ->models();

Documentation

Detailed documentation for each section:

Section Description
Filters All filter types: term, match, range, geo, fuzzy, and more
Sorts Sorting by fields, geography, and scripts
Includes Loading Eloquent relations after Elasticsearch query
Advanced Usage Custom filters, aggregations, working with SearchBuilder

Usage Examples

Date Range Filtering

// GET /orders?filter[created_at][gte]=2024-01-01&filter[created_at][lte]=2024-12-31

ElasticQueryWizard::for(Order::class)
    ->allowedFilters([
        ElasticFilter::range('created_at'),
        ElasticFilter::term('status'),
    ])
    ->build()
    ->execute()
    ->models();

Autocomplete Search (Prefix)

// GET /users?filter[username]=joh

ElasticQueryWizard::for(User::class)
    ->allowedFilters([
        ElasticFilter::prefix('username'),
    ])
    ->build()
    ->execute()
    ->models();

Typo-tolerant Search (Fuzzy)

// GET /products?filter[name]=iphon (will find "iphone")

ElasticQueryWizard::for(Product::class)
    ->allowedFilters([
        ElasticFilter::fuzzy('name')->withParameters([
            'fuzziness' => 'AUTO',
        ]),
    ])
    ->build()
    ->execute()
    ->models();

Field Selection

// GET /posts?fields[posts]=id,title,status

ElasticQueryWizard::for(Post::class)
    ->allowedFields(['id', 'title', 'status', 'body', 'created_at'])
    ->build()
    ->execute()
    ->models();

Using Aliases

Aliases allow you to use different parameter names in your API:

// GET /products?filter[tag]=electronics&sort=-date

ElasticQueryWizard::for(Product::class)
    ->allowedFilters([
        // Internal field: category, API parameter: tag
        ElasticFilter::term('category', 'tag'),
    ])
    ->allowedSorts([
        // Internal field: created_at, API parameter: date
        ElasticSort::field('created_at', 'date'),
    ])
    ->build()
    ->execute()
    ->models();

Default Sorting

ElasticQueryWizard::for(Post::class)
    ->allowedSorts(['created_at', 'title', 'views'])
    ->defaultSorts('-created_at') // Default: newest first
    ->build()
    ->execute()
    ->models();

Working with Soft Deletes

// GET /posts?filter[trashed]=with (include trashed)
// GET /posts?filter[trashed]=only (only trashed)

ElasticQueryWizard::for(Post::class)
    ->allowedFilters([
        ElasticFilter::trashed(),
    ])
    ->build()
    ->execute()
    ->models();

Query Parameter Format

The package uses a standardized JSON:API style parameter format:

Parameter Format Example
Filters filter[field]=value ?filter[status]=active
Sorting sort=field or sort=-field ?sort=-created_at
Includes include=relation1,relation2 ?include=author,comments
Fields fields[resource]=field1,field2 ?fields[posts]=id,title
Appends append=accessor1,accessor2 ?append=full_name

Testing

composer test

License

MIT License. See LICENSE file for details.