ogini / oginisearch-laravel-scout
OginiSearch Laravel Scout driver with PHP client - High-performance search engine integration
Installs: 28
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/ogini/oginisearch-laravel-scout
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.0
- laravel/framework: ^10.0|^11.0
- laravel/scout: ^10.0
Requires (Dev)
- nunomaduro/larastan: ^2.0
- orchestra/testbench: ^8.0|^9.0
- phpunit/phpunit: ^10.0
README
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.php
or 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.