oginisearch-laravel-scout maintained by ogini
OginiSearch Laravel Scout Driver
A comprehensive Laravel Scout driver for OginiSearch, providing full-text search capabilities with advanced features and performance optimizations.
Features
- Full Laravel Scout integration
- Advanced search capabilities
- Performance optimization with caching and connection pooling
- Asynchronous operations support
- Event-driven architecture
- Comprehensive error handling
- Query optimization and caching
- Synonym and stopword management
- Real-time search suggestions and autocomplete
Requirements
- PHP >= 8.2
- Laravel >= 12.0
- Laravel Scout >= 10.0
Installation
- Install the package via Composer:
composer require ogini/oginisearch-laravel-scout
- Publish the configuration file:
php artisan vendor:publish --tag=ogini-config
- Set up your OginiSearch configuration in
config/ogini.phpor via environment variables:
OGINI_BASE_URL=http://localhost:3000
OGINI_API_KEY=your-api-key-here
Configuration
The package provides extensive configuration options in config/ogini.php:
return [
'base_url' => env('OGINI_BASE_URL', 'http://localhost:3000'),
'api_key' => env('OGINI_API_KEY'),
'client' => [
'timeout' => 30,
'retry_attempts' => 3,
'retry_delay' => env('OGINI_BATCH_RETRY_DELAY', 100),
],
'engine' => [
'max_results' => 1000,
'default_limit' => 15,
],
'performance' => [
'query_optimization' => [
'enabled' => true,
'min_term_length' => env('OGINI_MIN_TERM_LENGTH', 3),
'max_complexity_score' => env('OGINI_MAX_COMPLEXITY_SCORE', 15),
'performance_check_threshold' => env('OGINI_PERFORMANCE_CHECK_THRESHOLD', 100),
'wildcard_penalty' => env('OGINI_WILDCARD_PENALTY', 5),
'phrase_boost' => env('OGINI_PHRASE_BOOST', 1.5),
'exact_match_boost' => env('OGINI_EXACT_MATCH_BOOST', 2.0),
'fuzzy_match_boost' => env('OGINI_FUZZY_MATCH_BOOST', 1.0),
],
'cache' => [
'enabled' => true,
'driver' => 'redis',
'query_ttl' => 300,
'result_ttl' => 1800,
'suggestion_ttl' => 600,
],
'connection_pool' => [
'enabled' => true,
'pool_size' => 5,
'connection_timeout' => 5,
'idle_timeout' => 30,
],
'batch_processing' => [
'enabled' => true,
'batch_size' => 100,
'max_retry_attempts' => 3,
'retry_delay' => env('OGINI_BATCH_RETRY_DELAY', 100),
],
],
];
Basic Usage
Model Configuration
Configure your Eloquent models to use OginiSearch:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Article extends Model
{
use Searchable;
/**
* Get the indexable data array for the model.
*/
public function toSearchableArray(): array
{
return [
'title' => $this->title,
'content' => $this->content,
'author' => $this->author->name,
'published_at' => $this->published_at,
];
}
/**
* Get the index name for the model.
*/
public function searchableAs(): string
{
return 'articles';
}
}
Searching
// Basic search
$articles = Article::search('laravel scout')->get();
// Search with additional options
$articles = Article::search('laravel scout')
->options([
'filter' => ['published' => true],
'sort' => ['published_at' => 'desc'],
])
->paginate(15);
Enhanced Laravel Integration
The package provides seamless Laravel request integration with automatic type casting for pagination parameters.
Automatic Type Casting
All pagination parameters automatically handle both integer and string values:
// In your controller - no manual casting needed!
public function search(Request $request)
{
$validated = $request->validate([
'query' => 'required|string',
'per_page' => 'integer|min:1|max:100',
'page' => 'integer|min:1',
]);
// These work directly without (int) casting:
return Article::search($validated['query'])
->paginate(
$validated['per_page'] ?? 15, // No need for (int) casting!
'page',
$validated['page'] ?? 1
);
}
// Direct client usage also supports strings
use OginiScoutDriver\Facades\Ogini;
$results = Ogini::search('articles', 'query', [
'size' => $request->get('per_page', '15'), // String works!
'from' => $request->get('offset', '0'), // String works!
]);
Laravel Request Validation Benefits
This enhancement eliminates common integration issues:
// Before v1.0.7 - Required manual casting
->paginate((int) $request->validated('per_page', 15))
// After v1.0.7 - Works directly
->paginate($request->validated('per_page', 15))
// Validation rules work seamlessly
$request->validate([
'per_page' => 'integer|between:1,100', // Laravel returns string
'page' => 'integer|min:1', // Automatically handled
]);
Supported Parameters
All pagination-related parameters support automatic string-to-integer conversion:
per_page,perPage- Results per page (minimum: 1)page- Page number (minimum: 1)size- Result size (minimum: 1)from,offset- Starting offset (minimum: 0)
Invalid values are automatically corrected to sensible minimums.
Advanced Features
1. Dynamic Model Discovery & Bulk Processing
The package includes a powerful dynamic model discovery system that automatically finds all searchable models in your Laravel application, making bulk operations seamless and universal.
Bulk Import Command
Import all your searchable models with a single command:
# List all available searchable models
php artisan ogini:bulk-import --list
# Import a specific model
php artisan ogini:bulk-import User --limit=1000 --batch-size=500
# Queue the import for large datasets
php artisan ogini:bulk-import Product --queue --batch-size=200
# Import with pagination support for large datasets
php artisan ogini:bulk-import User --limit=1000 --offset=0
php artisan ogini:bulk-import User --limit=1000 --offset=1000
# Validate model before import
php artisan ogini:bulk-import Article --validate
# Dry run to see what would be imported
php artisan ogini:bulk-import Order --dry-run --limit=100
Dynamic Model Resolution
The system supports flexible model naming:
# Short name
php artisan ogini:bulk-import User
# Full class name
php artisan ogini:bulk-import "App\Models\User"
# Legacy namespace
php artisan ogini:bulk-import "App\User"
Bulk Processing Performance
- 500x performance improvement - reduces 1K API calls to just 2 calls
- 90% reduction in processing time for large datasets
- Automatic chunking and parallel processing
- Error resilience with automatic retry mechanisms
- Progress tracking with detailed statistics
Universal Compatibility
The dynamic system works with:
- Standard Laravel application structures
- Custom model namespaces
- Legacy Laravel applications
- Multi-tenant applications
- Packages with searchable models
2. Advanced Client Methods
Query Suggestions
use OginiScoutDriver\Facades\Ogini;
// Get query suggestions
$suggestions = Ogini::getQuerySuggestions('articles', 'larav', [
'size' => 10,
'fuzzy' => true,
'highlight' => true,
]);
// Get autocomplete suggestions
$completions = Ogini::getAutocompleteSuggestions('articles', 'lar', [
'size' => 5,
'completion_field' => 'suggest',
]);
Synonym Management
// Add synonyms
Ogini::addSynonyms('articles', [
['car', 'automobile', 'vehicle'],
['fast', 'quick', 'rapid'],
]);
// Get synonyms
$synonyms = Ogini::getSynonyms('articles');
// Update synonyms
Ogini::updateSynonyms('articles', [
['updated', 'modified', 'changed'],
]);
// Delete synonyms
Ogini::deleteSynonyms('articles');
Stopword Configuration
// Configure stopwords
Ogini::configureStopwords('articles', ['the', 'a', 'an', 'and', 'or'], 'en');
// Get current stopwords
$stopwords = Ogini::getStopwords('articles');
// Update stopwords
Ogini::updateStopwords('articles', ['the', 'a', 'an']);
// Reset to default
Ogini::resetStopwords('articles', 'en');
2. Asynchronous Operations
Using the Async Client
use OginiScoutDriver\Facades\AsyncOgini;
// Index documents asynchronously
$promise = AsyncOgini::indexDocumentAsync('articles', [
'title' => 'Async Article',
'content' => 'This article was indexed asynchronously',
]);
// Bulk index with callback
AsyncOgini::bulkIndexDocumentsAsync('articles', $documents,
function ($result) {
Log::info('Bulk indexing completed', $result);
},
function ($error) {
Log::error('Bulk indexing failed', ['error' => $error]);
}
);
// Search asynchronously
$searchPromise = AsyncOgini::searchAsync('articles', [
'query' => ['match' => ['title' => 'Laravel']],
]);
// Wait for all pending operations
$results = AsyncOgini::waitForAll();
Queue Integration
// Enable queue integration
AsyncOgini::setQueueEnabled(true);
// Now operations will be queued instead of executed immediately
$jobId = AsyncOgini::indexDocumentAsync('articles', $document);
Parallel Execution
// Execute multiple requests in parallel
$requests = [
['method' => 'POST', 'endpoint' => '/api/indices/articles/search', 'data' => $query1],
['method' => 'POST', 'endpoint' => '/api/indices/articles/search', 'data' => $query2],
['method' => 'POST', 'endpoint' => '/api/indices/articles/search', 'data' => $query3],
];
$results = AsyncOgini::executeParallel($requests, function ($completed, $total, $result) {
echo "Progress: {$completed}/{$total}\n";
});
3. Event System
The package dispatches events for various operations, allowing you to listen and respond to search activities:
Available Events
IndexingCompleted- When document indexing succeedsIndexingFailed- When document indexing failsSearchCompleted- When search operation succeedsSearchFailed- When search operation failsDeletionCompleted- When document deletion succeedsDeletionFailed- When document deletion fails
Listening to Events
use OginiScoutDriver\Events\IndexingCompleted;
use OginiScoutDriver\Events\SearchCompleted;
// In your EventServiceProvider
protected $listen = [
IndexingCompleted::class => [
YourIndexingCompletedListener::class,
],
SearchCompleted::class => [
YourSearchCompletedListener::class,
],
];
Custom Event Listener
<?php
namespace App\Listeners;
use OginiScoutDriver\Events\IndexingCompleted;
class LogIndexingSuccess
{
public function handle(IndexingCompleted $event): void
{
\Log::info('Document indexed successfully', [
'job_id' => $event->getJobId(),
'index_name' => $event->getIndexName(),
'document_id' => $event->getDocumentId(),
'is_bulk' => $event->isBulk(),
]);
}
}
4. Performance Features
Query Optimization
// The package automatically optimizes queries based on configuration:
// - Removes short terms (< min_term_length)
// - Applies complexity scoring
// - Optimizes wildcard usage
// - Boosts phrase matches and exact matches
Caching
// Query results are automatically cached based on configuration
// Cache keys are generated from query parameters and settings
// TTL values are configurable for different operation types
Connection Pooling
// HTTP connections are pooled and reused for better performance
// Pool size and timeout settings are configurable
Batch Processing
// Large operations are automatically batched
// Batch size and retry logic are configurable
Error Handling
The package provides comprehensive error handling with detailed exception information:
try {
$results = Article::search('query')->get();
} catch (\OginiScoutDriver\Exceptions\OginiException $e) {
Log::error('Search failed', [
'message' => $e->getMessage(),
'context' => $e->getContext(),
]);
}
Update Management
The package includes an intelligent update checking system:
Check for Updates
# Check for available updates
php artisan ogini:check-updates
# Clear cache and check
php artisan ogini:check-updates --clear-cache
# Security updates only
php artisan ogini:check-updates --security-only
# JSON output for automation
php artisan ogini:check-updates --json
Programmatic Update Checking
use OginiUpdateChecker;
// Check if updates are available
if (OginiUpdateChecker::hasUpdate()) {
$updateInfo = OginiUpdateChecker::getUpdateInfo();
if ($updateInfo['security_update']) {
Log::warning('Security update available', $updateInfo);
}
if ($updateInfo['breaking_changes']) {
Log::info('Major version update available', $updateInfo);
}
}
// Get current and latest versions
$current = OginiUpdateChecker::getCurrentVersion();
$latest = OginiUpdateChecker::getLatestVersion();
// Clear update cache
OginiUpdateChecker::clearCache();
Testing
The package includes comprehensive testing with enterprise-grade quality assurance:
Test Coverage
- 430+ Tests: Complete functionality coverage
- 90%+ Code Coverage: Automated coverage validation
- Edge Case Testing: Boundary conditions and error scenarios
- Security Testing: Vulnerability and security validation
- Laravel Compatibility: Multi-version Laravel support (8.x-11.x)
Run Tests
# Using the convenient test runner script
./run-tests.sh # CI-safe tests (default)
./run-tests.sh unit # Unit tests only
./run-tests.sh api-calls # Real API call tests
./run-tests.sh all # All tests (CI-safe + API calls if server available)
./run-tests.sh coverage # Generate coverage report
# Or use composer/phpunit directly:
composer test # CI-safe tests
vendor/bin/phpunit --exclude-group=quality-assurance,benchmarks,load-tests,error-conditions,integration-tests,real-api-calls # CI-safe tests (manual)
vendor/bin/phpunit --group=real-api-calls # Real API call tests
Running Tests with Real API Calls
Some integration tests require a real Ogini server running locally. These tests are automatically skipped in CI/CD environments but can be run locally for comprehensive testing:
# 1. Start an Ogini server on localhost:3000
docker run -p 3000:3000 ogini/server
# 2. Run the real API call tests
vendor/bin/phpunit tests/Integration/DocumentCreationTest.php
# Or run all tests that require real API calls
vendor/bin/phpunit --group=real-api-calls
Note: Tests marked with @group real-api-calls are excluded from CI pipelines and composer test to ensure fast, reliable automated testing without external dependencies.
Quality Assurance
- PSR-12 Compliance: PHP coding standards
- Security Scanning: Vulnerability detection
- Code Quality Analysis: Complexity and duplication checks
- Documentation Coverage: PHPDoc requirements
Contributing
- Fork the repository
- Create a feature branch
- Write tests for your changes
- Ensure all tests pass
- Submit a pull request
License
This package is open-sourced software licensed under the MIT license.