cmsig/seal-loupe-adapter
Loupe adapter for the SEAL search engine: write indexed documents into a Loupe SQLite instance. Install via Composer and configure through code or a loupe:// DSN (e.g., loupe://var/indexes/). Part of the cmsig/search project.
Install Dependencies
composer require cmsig/seal cmsig/seal-loupe-adapter
Ensure loupe-php/loupe is also installed (handled as a dependency).
Basic Engine Initialization
use CmsIg\Seal\Engine;
use CmsIg\Seal\Adapter\Loupe\LoupeAdapter;
use Loupe\Loupe\LoupeFactory;
$loupeFactory = new LoupeFactory();
$adapter = new LoupeAdapter(new LoupeHelper($loupeFactory, 'storage/loupe'));
$engine = new Engine($adapter, $schema);
First Use Case: Indexing Documents
$engine->index('documents', [
'id' => 1,
'title' => 'Laravel SEO Guide',
'content' => '...',
]);
LoupeHelper: Configures the SQLite storage path (default: var/indexes/).LoupeAdapter: Bridges SEAL’s abstraction to Loupe’s SQLite backend.Schema-Driven Indexing
Define a schema (e.g., DocumentSchema) with fields like:
$schema = new DocumentSchema();
$schema->addTextField('title');
$schema->addTextField('content');
Pass it to the Engine constructor.
Querying with SEAL
$results = $engine->search('documents', 'Laravel SEO', [
'limit' => 10,
'fields' => ['title', 'content'],
]);
Bulk Operations
$engine->bulkIndex('documents', [
['id' => 1, 'title' => 'Post 1'],
['id' => 2, 'title' => 'Post 2'],
]);
Laravel Service Provider
Bind the engine in AppServiceProvider:
$this->app->singleton(Engine::class, function ($app) {
$loupeFactory = new LoupeFactory();
return new Engine(
new LoupeAdapter(new LoupeHelper($loupeFactory, storage_path('loupe'))),
$app->make(DocumentSchema::class)
);
});
DSN Configuration
Use .env for flexibility:
SEAL_LOUPE_DSN=loupe://storage/loupe
Parse it in a helper:
$dsn = parse_url(env('SEAL_LOUPE_DSN'));
$adapter = new LoupeAdapter(new LoupeHelper($loupeFactory, $dsn['path']));
Field Mapping
Customize Loupe’s field types via LoupeHelper:
$helper = new LoupeHelper($loupeFactory, 'storage/loupe');
$helper->setFieldType('content', 'text'); // Override default
SQLite Locking
WRITE_AHEAD_LOCKING mode in LoupeFactory:
$loupeFactory = new LoupeFactory(['mode' => 'wal']);
Schema Mismatches
LoupeHelper::getFieldType() for unexpected conversions.Path Permissions
var/indexes/) is writable by the web server.storage_path('loupe') in Laravel to avoid hardcoding.Case Sensitivity
LoupeFactory:
$loupeFactory = new LoupeFactory(['collation' => 'NOCASE']);
Enable Loupe Logging
$loupeFactory = new LoupeFactory(['debug' => true]);
Logs appear in storage/logs/loupe.log (Laravel) or stdout.
Inspect SQLite Directly
sqlite3 storage/loupe/index.db ".tables" # List indexes
sqlite3 storage/loupe/index.db "SELECT * FROM documents;" # Query raw data
Custom Field Processors
Extend LoupeHelper to transform SEAL fields before indexing:
class CustomLoupeHelper extends LoupeHelper {
public function getFieldType($fieldName) {
if ($fieldName === 'slug') return 'keyword';
return parent::getFieldType($fieldName);
}
}
Query Modifiers
Override LoupeAdapter::search() to inject Loupe-specific syntax:
$adapter->search($index, $query, [
'boost' => ['title' => 2.0], // Loupe-specific
]);
Multi-Index Management
Use separate LoupeFactory instances for isolated indexes:
$factory1 = new LoupeFactory(['path' => 'storage/loupe/index1']);
$factory2 = new LoupeFactory(['path' => 'storage/loupe/index2']);
Batch Indexing Reduce SQLite overhead by batching inserts:
$engine->bulkIndex('documents', array_chunk($documents, 50));
Index Optimization Rebuild Loupe’s internal indexes periodically:
$loupeFactory->getIndex('documents')->optimize();
How can I help you explore Laravel packages today?