Installation:
composer require hungneox/ramen-elastic-query
Register the service provider in bootstrap/app.php:
$app->register(Neox\Ramen\Elastic\ElasticQueryServiceProvider::class);
First Query:
Use the facade (ES) or container binding (app(Builder::class)) to query an index:
$result = ES::use('content')->from('article')->find('TIYKtQX', '_id', ['id', 'title']);
Key Files:
config/elasticsearch.php (if present, check for custom configurations).app/Providers/ElasticQueryServiceProvider.php (for service binding overrides).Fetch a document by ID:
$article = ES::use('content')->from('article')->find('TIYKtQX', '_id', ['id', 'title']);
Search with full-text matching:
$results = ES::use('content')
->from('article')
->where('title', 'like', 'Laravel')
->get();
Query Chaining: Chain methods for readability and maintainability:
$results = ES::use('content')
->from('article')
->select('id', 'title')
->where('published', '=', true)
->orderBy('created_at', 'desc')
->limit(10)
->get();
Dynamic Index/Collection Handling:
Use the use() method to switch between Elasticsearch collections (e.g., content, analytics):
// Switch collections dynamically
$collection = request()->input('collection');
$results = ES::use($collection)->from('type')->get();
Pagination:
Leverage limit() and offset() for pagination:
$results = ES::use('content')
->from('article')
->limit(20)
->offset(0)
->get();
Bulk Operations:
Use delete() for bulk deletions (e.g., soft deletes via _id):
$ids = [1, 2, 3];
foreach ($ids as $id) {
ES::use('content')->from('article')->delete($id);
}
Laravel Eloquent Integration: Use the package alongside Eloquent for hybrid queries:
$user = User::whereHas('articles', function ($query) {
$query->where('title', 'like', '%Laravel%');
})->first();
Then fetch Elasticsearch results for the same data:
$esResults = ES::use('content')->from('article')->where('title', 'like', '%Laravel%')->get();
Caching Results: Cache frequent queries to reduce Elasticsearch load:
$results = Cache::remember("es_articles_{$searchTerm}", now()->addHours(1), function () use ($searchTerm) {
return ES::use('content')->from('article')->where('title', 'like', $searchTerm)->get();
});
Error Handling: Wrap queries in try-catch blocks to handle Elasticsearch exceptions gracefully:
try {
$results = ES::use('content')->from('article')->find($id, '_id');
} catch (\Exception $e) {
Log::error("Elasticsearch error: " . $e->getMessage());
return response()->json(['error' => 'Failed to fetch data'], 500);
}
Collection vs. Index Confusion:
use() method sets the collection (e.g., content), while from() sets the index/type (e.g., article).NotFoundException errors.use() and from() methods in your queries.Full-Text Search Quirks:
like operator in where() uses Elasticsearch's match query under the hood. For advanced full-text search, consider using raw queries:
$result = ES::use('content')->from('article')->rawQuery([
'query' => [
'match_phrase' => ['title' => 'Laravel Elasticsearch']
]
])->get();
Missing select():
select() returns all fields, which can be inefficient for large documents.->select('id', 'title', 'created_at') // Explicit fields
Facade vs. Container Binding:
ES) is convenient but may not support dependency injection. For testing, prefer container binding:
$builder = app(Neox\Ramen\Elastic\Builder::class);
Bulk API Limitations:
delete() method processes deletions one-by-one. For bulk deletions, use Elasticsearch's Bulk API directly:
$client = app('elasticsearch');
$client->bulk(['body' => $bulkPayload]);
Raw Query Inspection:
Use toQuery() to inspect the generated Elasticsearch query:
$query = ES::use('content')->from('article')->where('title', 'like', 'Laravel')->toQuery();
dd($query); // Debug the raw query
Logging:
Enable Elasticsearch client logging in config/elasticsearch.php:
'log' => [
'enabled' => true,
'level' => 'debug',
],
Common Errors:
NotFoundException: Verify the collection/index exists in Elasticsearch.InvalidArgumentException: Check for typos in field names or operators (e.g., = vs. ==).ConnectionException: Ensure Elasticsearch is running and the client is configured correctly.Custom Query Builders:
Extend the Builder class to add domain-specific methods:
class ArticleQueryBuilder extends \Neox\Ramen\Elastic\Builder
{
public function published()
{
return $this->where('published', '=', true);
}
}
Bind it in ElasticQueryServiceProvider:
$this->app->bind('article.query', function () {
return new ArticleQueryBuilder();
});
Raw Query Support:
Use rawQuery() for complex queries not supported by the fluent interface:
$result = ES::use('content')->from('article')->rawQuery([
'query' => [
'bool' => [
'must' => [
['match' => ['title' => 'Laravel']],
['range' => ['created_at' => ['gte' => 'now-1y']]]
]
]
]
])->get();
Event Listeners:
Listen for query events (e.g., QueryExecuted) to log or modify queries:
Event::listen('Neox\Ramen\Elastic\Events\QueryExecuted', function ($event) {
Log::debug('Executed Elasticsearch query:', $event->query);
});
Testing: Use mocks for Elasticsearch in tests:
$mock = Mockery::mock('overload', 'elasticsearch');
$mock->shouldReceive('get')->andReturn(['hits' => ['hits' => []]]);
How can I help you explore Laravel packages today?