dmlab / module-typesense-search
Registers Typesense as a selectable Magento search engine and implements the query side: translates a Magento search request into a Typesense query and returns matched IDs plus aggregations. Serves quick/advanced search, category listings and layered navigation. Drop-in replacement for OpenSearch/El
Package info
github.com/dmlab-work/module-typesense-search
Type:magento2-module
pkg:composer/dmlab/module-typesense-search
Requires
- php: ~8.2.0||~8.3.0||~8.4.0||~8.5.0
- dmlab/module-typesense-core: ^0.2.0
- dmlab/module-typesense-indexer: ^0.2.0
- magento/framework: ^103.0.7
- magento/module-advanced-search: ^100.4.5
- magento/module-catalog: ^104.0.7
- magento/module-catalog-search: ^102.0.7
- magento/module-config: ^101.2.7
- magento/module-customer: ^103.0.7
- magento/module-elasticsearch: ^101.0.7
- magento/module-search: ^101.1.7
- magento/module-store: ^101.1.7
- psr/log: ^2.0||^3.0
Requires (Dev)
- phpunit/phpunit: ^11.5||^12.5
Suggests
- dmlab/module-typesense-instant-search: Search-as-you-type dropdown in the storefront header, served by Typesense
Provides
None
Conflicts
None
Replaces
None
README
Replace OpenSearch with Typesense without touching your frontend: search, category pages, layered navigation and GraphQL keep working. Add or remove a filter attribute in under a second instead of a full reindex.
Install
composer require dmlab/module-typesense-search # pulls core + indexer
bin/magento module:enable DmLab_TypesenseCore DmLab_TypesenseIndexer DmLab_TypesenseSearch
bin/magento setup:upgrade
Requirements
Magento 2.4.7+ · PHP 8.2–8.5 · Typesense ≥ 30.0 · dmlab/module-typesense-core and
dmlab/module-typesense-indexer ^0.2.0 (the indexer owns the connection settings).
Hyvä
Nothing Hyvä-specific to install: search results, category pages and layered navigation are rendered by the theme from Magento's own search API — this module is a server-side adapter with no frontend code. Not yet verified on a Hyvä install.
Pairs with typesense-instant-search for a search-as-you-type dropdown.
Select Typesense
- Stores → Configuration → Catalog → Catalog Search.
- Set Search Engine to Typesense and fill in the connection fields (host / port / protocol /
API key — owned by
typesense-indexer). Test Connection checks the unsaved form values (node health + API key); a masked key falls back to the saved one only for the saved node. - Reindex:
bin/magento indexer:reindex catalogsearch_fulltext.
Selection is admin-only: Magento's setup:install accepts only elasticsearch8|opensearch.
setup:upgrade validates the saved Typesense connection. Switching back to OpenSearch/Elasticsearch
is reversible — pick the other engine and reindex.
Covered
Quick + advanced search · category listings · layered navigation (facet buckets + counts) · price
ranges / dynamic intervals · native search synonyms (Marketing → Search Synonyms, synced by
typesense-indexer) · "Did you mean" = one typo-corrected query (search_suggestion_count only
enables/disables it). etc/search_engine.xml declares only what is implemented.
Known differences
- Advanced Search by SKU matches a token prefix (
WSH1→WSH12…); ES/OpenSearch match*value*. - Position sort with several categories uses the first category id (Typesense has no min-over-fields sort).
- Sort/facet clauses on fields missing from the schema (e.g. a category not yet reindexed) are dropped and logged, not fatal; a filter on a missing field, or with a value that does not fit the field type, returns an empty result.
- The alias schema is cached 5 min and dropped on every alias swap or attribute schema PATCH.
Synonym-set existence is cached 5 min: a store's first synonym group takes effect within 5 min
(
bin/magento cache:flushapplies it now). - Constructs Typesense's grammar cannot express (negated wildcard / compound filters) degrade to an empty result rather than a wrong one; standard catalog flows never produce them.
Hosting
docker run -d --name typesense -p 8108:8108 -v typesense-data:/data \ typesense/typesense:30.2 --data-dir /data --api-key=<admin-key>
One binary. Plan roughly 0.5–1 GB RAM for a typical catalog — only fields used for search, filter or sort are held in memory. Typesense Cloud works: use its host, port 443, https. Not available on Adobe Commerce Cloud or Hypernode.
Integration tests
Test/Integration/ runs against a real Typesense (throw-away it_search_* collections) and is
skipped unless TYPESENSE_TEST_URL and TYPESENSE_TEST_API_KEY are set:
docker exec -w /var/www/html/m2-modules/module-typesense-search -e MAGENTO_ROOT=/var/www/html \ -e TYPESENSE_TEST_URL=http://typesense:8108 -e TYPESENSE_TEST_API_KEY=<key> \ magento-app php /var/www/html/vendor/bin/phpunit -c Test/Integration/phpunit.xml
Architecture
L2 of the Typesense suite: registers Typesense as a selectable Magento search engine, translates a
Magento\Framework\Search\RequestInterface into a Typesense query and returns matched product IDs
plus aggregations. Since Magento 2.4 the engine also drives category listings and layered-navigation
counts, so this one module serves all of them. It reads only — writing documents and patching
schema is typesense-indexer's job.
Extension seams
Both are di-sorted pipelines, never a preference — so paid modules (merchandising, semantic)
compose instead of colliding. Both ship empty; steps run in sortOrder, each receiving the previous
step's output.
QueryModifierInterface::modify(array $query, RequestInterface $request): array — rewrites the
Typesense search payload (already carrying facet_by). Sort/facet clauses on missing fields are
dropped afterwards; modifier params also reach price statistics.
<type name="DmLab\TypesenseSearch\SearchAdapter\Query\QueryModifier"> <arguments> <argument name="modifiers" xsi:type="array"> <item sortOrder="10" name="merchandising" xsi:type="object">Vendor\Module\Query\MyModifier</item> </argument> </arguments> </type>
ResponseProcessorInterface::process(array $response, array $params, RequestInterface $request): array
— rewrites the raw Typesense response before hits and facets are mapped.
<type name="DmLab\TypesenseSearch\SearchAdapter\Response\ResponseProcessor"> <arguments> <argument name="processors" xsi:type="array"> <item sortOrder="10" name="merchandising" xsi:type="object">Vendor\Module\Response\MyProcessor</item> </argument> </arguments> </type>
License
OSL-3.0 © DMLab.
