spatie/elasticsearch-search-string-parser
Parse custom search strings into Elasticsearch queries. Supports regex-based directives like status:active or @user, grouping directives, and autocomplete suggestions. Build searches via spatie/elasticsearch-query-builder and get results + directive suggestions.
Installation:
composer require spatie/elasticsearch-search-string-parser
Requires spatie/elasticsearch-query-builder (install via composer require spatie/elasticsearch-query-builder).
Basic Usage:
Define a parser class extending Spatie\SearchStringParser\SearchStringParser:
use Spatie\SearchStringParser\SearchStringParser;
class CustomSearchParser extends SearchStringParser
{
public function __construct()
{
$this->addDirective('status', 'active|inactive');
$this->addDirective('group_by', 'project|user');
}
}
First Use Case: Parse a search string into an Elasticsearch query:
$parser = new CustomSearchParser();
$query = $parser->parse('foo bar status:active group_by:project');
// Outputs a query builder instance (compatible with spatie/elasticsearch-query-builder)
tests/ directory for real-world usage patterns.spatie/elasticsearch-query-builder for advanced query construction.Define Directives: Register custom directives (e.g., filters, groupings) with regex patterns:
$parser->addDirective('priority', '/high|medium|low/');
$parser->addDirective('date_range', '/(\d{4}-\d{2}-\d{2})\s*to\s*(\d{4}-\d{2}-\d{2})/');
Parse and Build Queries:
$parser = new CustomSearchParser();
$query = $parser->parse('priority:high date_range:2023-01-01 to 2023-12-31');
// Convert to Elasticsearch DSL
$elasticsearchQuery = $query->toElasticsearchQuery();
Integration with Laravel:
Bind the parser to the container in AppServiceProvider:
$this->app->singleton(CustomSearchParser::class, function () {
return new CustomSearchParser();
});
Use in controllers/routes:
$query = app(CustomSearchParser::class)->parse(request('q'));
Dynamic Directives: Load directives from a config file or database:
$directives = config('search.directives');
foreach ($directives as $name => $pattern) {
$parser->addDirective($name, $pattern);
}
Grouping and Nesting:
Use groupBy() for hierarchical queries:
$parser->addDirective('group_by', 'project|user');
$query = $parser->parse('group_by:project')->groupBy('project');
Auto-Completion:
Implement Spatie\SearchStringParser\AutoCompleteProvider for suggestions:
class ProjectAutoCompleteProvider implements AutoCompleteProvider
{
public function getSuggestions(string $query): array
{
return Project::where('name', 'like', "%{$query}%")->limit(5)->pluck('name')->toArray();
}
}
Combining with Query Builder:
Extend Spatie\QueryBuilder\QueryBuilder to support parsed queries:
$queryBuilder = new QueryBuilder();
$queryBuilder->addQuery($parser->parse('status:active')->toElasticsearchQuery());
Regex Overlap:
status:active vs. active) may cause parsing ambiguity.^/$).Case Sensitivity:
i modifier for case-insensitive matches:
$parser->addDirective('status', '/active|inactive/i');
Whitespace Handling:
group_by:project may fail if the input has inconsistent spacing (e.g., group_by : project).$parser->addDirective('group_by', '/group_by\s*:\s*(project|user)/i');
Query Builder Incompatibility:
spatie/elasticsearch-query-builder's expected structure.dd($query->toElasticsearchQuery()) to inspect the generated DSL.Log Parsed Tokens: Enable debug mode to see tokenized input:
$parser->enableDebug();
$tokens = $parser->tokenize('status:active'); // Inspect tokens
Validate Directives:
Use getDirectives() to list registered directives:
dd($parser->getDirectives());
Test Edge Cases: Test with:
status: without a value).Custom Tokenizers:
Override tokenize() to handle non-standard formats (e.g., JSON-encoded queries).
Post-Processing:
Use afterParsing() callback to modify the query:
$parser->afterParsing(function ($query) {
$query->addMustNot(['term' => ['deleted' => true]]);
});
Dynamic Directive Loading:
Implement Spatie\SearchStringParser\DirectiveLoader to fetch directives from an API or cache.
Performance:
Default Directives:
The package includes basic directives (e.g., not:, must:). Override them carefully:
$parser->addDirective('not', '/not\s*(.+)/i'); // Customize negation logic
Grouping Syntax:
Ensure group_by: directives are registered before parsing to avoid conflicts with other directives.
Elasticsearch Version:
The generated DSL may need adjustments for Elasticsearch <7.0 (e.g., filter vs. bool queries). Use toElasticsearchQuery() with version-specific options.
How can I help you explore Laravel packages today?