Skip to content
Search

ETL Adapter: Seal

This is the Flow PHP Adapter for SEAL. SEAL is a PHP Search Engine Abstraction Layer with one API for many search engines (Algolia, Elasticsearch, Meilisearch, OpenSearch, Solr, Typesense, RediSearch, Loupe). With this adapter, your Flow PHP ETL pipelines can load data into each search backend that SEAL supports. The pipelines do not depend on a specific search engine implementation.

Installation

For the installation instructions, read the installation page.

Also install the SEAL engine adapter for your search backend, for example:

composer require flow-php/etl-adapter-seal cmsig/seal-elasticsearch-adapter
Backend Package
Algolia cmsig/seal-algolia-adapter
Elasticsearch cmsig/seal-elasticsearch-adapter
Loupe cmsig/seal-loupe-adapter
Meilisearch cmsig/seal-meilisearch-adapter
Memory cmsig/seal-memory-adapter
OpenSearch cmsig/seal-opensearch-adapter
RediSearch cmsig/seal-redisearch-adapter
Solr cmsig/seal-solr-adapter
Typesense cmsig/seal-typesense-adapter

Description

The adapter works on a CmsIg\Seal\EngineInterface. You build it yourself from the SEAL adapter of your choice and a SEAL schema. The Doctrine and PostgreSQL adapters work in the same way: they accept a pre-configured connection/client. It also works with the SEAL Symfony bundle, which can inject a fully configured engine.

  • Loader: to_seal_upsert() / to_seal_delete() write or remove documents in a search index in bulks.
  • Schema Conversion: to_seal_schema() / seal_schema_to_flow() convert between Flow and SEAL schemas.

Building an Engine

use CmsIg\Seal\Adapter\Elasticsearch\ElasticsearchAdapter;
use CmsIg\Seal\Engine;

use function Flow\ETL\Adapter\Seal\to_seal_schema;
use function Flow\ETL\DSL\{int_schema, schema, str_schema};

$sealSchema = to_seal_schema(
    schema(
        str_schema('id'),
        str_schema('name'),
        int_schema('age'),
    ),
    index_name: 'users',
    identifier: 'id',
);

$engine = new Engine(new ElasticsearchAdapter($client), $sealSchema);
$engine->createIndex('users');

Loader

Upserting Documents

to_seal_upsert() saves documents into the index. Search engines upsert by document identifier. A document with an existing identifier replaces the old document.

use function Flow\ETL\Adapter\Seal\to_seal_upsert;
use function Flow\ETL\Adapter\CSV\from_csv;
use function Flow\ETL\DSL\data_frame;

data_frame()
    ->read(from_csv(__DIR__ . '/users.csv'))
    ->write(to_seal_upsert($engine, 'users'))
    ->run();

Bulk Size

By default, documents are written in bulks of 100. To change this, use withBulkSize():

to_seal_upsert($engine, 'users')->withBulkSize(500);

Deleting Documents

to_seal_delete() removes documents by their identifiers, which it reads from the id column. withIdentifierColumn() names a different column.

use function Flow\ETL\Adapter\Seal\to_seal_delete;
use function Flow\ETL\DSL\{data_frame, from_array};

data_frame()
    ->read(from_array([['id' => '1'], ['id' => '2']]))
    ->write(to_seal_delete($engine, 'users'))
    ->run();
to_seal_delete($engine, 'products')->withIdentifierColumn('sku');

Data Normalization

Each row becomes one document:

Flow Type Document Value
string, int, float, bool unchanged
uuid string representation
datetime string, DateTimeInterface::ATOM by default
date string, Y-m-d by default
enum case name
time microseconds
xml, xml_element, html, html_element markup text, for example <a b="1"><c></c></a>
json decoded array
list, map, structure array (nested values normalized recursively)

Schema Conversion

to_seal_schema() converts a Flow schema into a SEAL schema for one index. SEAL needs exactly one identifier field for each index. Name it with the identifier argument, or mark a definition with SealMetadata::identifier().

use Flow\ETL\Adapter\Seal\Values\Schema\SealMetadata;

use function Flow\ETL\Adapter\Seal\to_seal_schema;
use function Flow\ETL\DSL\{datetime_schema, float_schema, int_schema, schema, str_schema};

$sealSchema = to_seal_schema(
    schema(
        str_schema('id'),
        str_schema('title', metadata: SealMetadata::filterable()->merge(SealMetadata::sortable())),
        float_schema('price'),
        datetime_schema('published_at'),
    ),
    index_name: 'products',
    identifier: 'id',
);

seal_schema_to_flow() does the reverse conversion, from a SEAL schema back to a Flow schema.

Type Mapping

Flow Type SEAL Field Default Flags
string, uuid, enum, xml, html TextField searchable
integer IntegerField filterable, sortable
float FloatField filterable, sortable
boolean BooleanField filterable
datetime, date DateTimeField filterable, sortable
json, map JsonObjectField -
list<T> field of T with multiple: true as T
structure ObjectField (recursive) -

Field Flags

You can override the default flags for each definition through its metadata:

Metadata Effect
SealMetadata::identifier() marks the field as the index identifier
SealMetadata::searchable() full-text searchable
SealMetadata::filterable() usable in filters
SealMetadata::sortable() usable in sorting
SealMetadata::distinct() usable for distinct queries
SealMetadata::facet() usable as a facet

Note: The index lifecycle (create and drop indexes) is a SEAL task. Use the engine directly: $engine->createIndex('users'), $engine->dropIndex('users').

Note: Some search backends index documents asynchronously. A document that the loader saved can be invisible to countDocuments() for a short time. Read the documentation of your backend. To wait for indexing tasks, SEAL has the ['return_slow_promise_result' => true] option.

Found a typo or an outdated section? Edit this page on GitHub


Contributors

Built in the open.

Join us on GitHub
scroll back to top