RunAsRoot Magento 2 TypeSense

July 8, 2026 · View on GitHub

Check Extension E2E Tests PHP Magento Mage-OS License

Open-source Typesense search integration for Magento 2 / Mage-OS with visual merchandising, zero-downtime reindexing, and Hyva-native frontend components.


Screenshots

Admin Dashboard

Admin Dashboard

Admin Configuration

Admin Configuration

Collection Browser

Collection Browser

Visual Category Merchandiser

Visual Category Merchandiser

Synonym Management

Synonym Management

Frontend — Category Page

Frontend Category


Features

  • Full-text search powered by Typesense with typo tolerance and instant results
  • Zero-downtime reindexing using collection versioning and atomic alias swaps
  • Multi-entity indexing: products, categories, CMS pages, search suggestions, orders, customers, store, and system config
  • Hyva-compatible autocomplete (CSP-safe Alpine.js) with multi-index results
  • Instant search page replacing the default Magento catalog search
  • Category page powered by Typesense with client-side filtering and sorting
  • Visual category merchandiser on the category edit page (Algolia-style sortable product table)
  • Virtual categories with rule-based product membership, resolved at query time with no reindex required
  • Admin query merchandiser for keyword-level product promotion/demotion
  • Admin synonym manager and collection browser
  • CLI commands for reindexing, collection management, and health checks
  • Cron-based and message-queue-based background reindexing
  • Conversational search (RAG) with AI-generated answers powered by OpenAI + Typesense embeddings
  • Product recommendations on the product detail page using vector similarity search
  • Admin AI Assistant with 24 agentic analytics tools (customer segmentation, inventory forecasting, cohort analysis, and more)
  • CSP whitelist for Typesense API endpoints
  • Configurable per store view

Requirements

DependencyVersion
PHP8.3+
Magento 2 / Mage-OS2.4.7+
Typesense27+
Hyva Theme1.3+
typesense/typesense-php^6.0

Installation

composer require run-as-root/magento2-typesense
bin/magento setup:upgrade
bin/magento setup:di:compile

For production:

bin/magento setup:static-content:deploy
bin/magento cache:flush

Configuration

Navigate to Stores > Configuration > TypeSense > TypeSense Search.

General Settings

SettingDefaultDescription
Enable TypeSense SearchNoMaster switch for the module
Protocolhttphttp or https for server-side (PHP) requests
HostlocalhostInternal hostname for indexing (e.g., typesense in Docker)
Port8108Typesense server port
Admin API KeyServer-side admin key (never exposed to frontend)
Search-Only API KeyPublic key used for frontend search requests
Frontend Search ProtocolOverride protocol for browser-side requests (useful in Warden/Docker setups)
Frontend Search HostPublic hostname the browser uses to reach Typesense
Frontend Search PortOverride port for browser-side requests
Index PrefixrarPrefix for all collection names
Enable LoggingNoLogs search queries and indexing activity

Indexing Settings

SettingDefaultDescription
Batch Size200Documents per import batch
Index ProductsYesEnable product indexing
Index CategoriesYesEnable category indexing
Index CMS PagesYesEnable CMS page indexing
Index SuggestionsYesEnable search suggestion indexing
Additional Attributes to IndexExtra product attributes to include in the Typesense index (see below)
Zero-Downtime ReindexYesUse alias swap strategy for live reindexing
Enable Cron ReindexNoSchedule automatic reindexing
Cron Schedule0 2 * * *Cron expression for scheduled reindex
Enable Queue ReindexNoUse message queue for async reindexing

Instant Search Settings

SettingDefaultDescription
Enable Instant SearchYesReplace the Magento search results page
Products Per Page20Results per page on the search results page
Product Tile AttributesAttributes displayed on product cards (must also be indexed)
Sort OptionsSort options available to customers (see below)
Replace Category PagesNoUse Typesense for category product listings
Image TypeProduct image type to use on tiles
Image Width / HeightDimensions for product tile images

Autocomplete Settings

SettingDefaultDescription
Enable AutocompleteYesShow autocomplete dropdown on search input
Product Count6Max product results in autocomplete
Category Count3Max category results
CMS Page Count2Max CMS page results
Suggestion Count4Max query suggestion results

Conversational Search (AI) Settings

SettingDefaultDescription
Enable Conversational SearchNoShow AI-generated answers above search results
OpenAI API KeyAPI key for answer generation (get one at platform.openai.com)
OpenAI Modelgpt-4o-miniModel used for generating answers
System PromptInstructions for the AI assistant's tone and behavior
Embedding Source Fieldsname, descriptionProduct fields used to generate semantic embeddings
Conversation TTL86400How long conversation history is kept (seconds)

Product Recommendations Settings

SettingDefaultDescription
Enable RecommendationsNoShow similar products on the product detail page
Number of Products8Maximum recommended products to display

Note: Product Recommendations require Conversational Search to be enabled, as they use the same product embeddings for vector similarity search. The admin UI enforces this: the Product Recommendations fields are hidden until Conversational Search is turned on, keeping the admin state aligned with the backend gate (isRecommendationsEnabled()).

Admin AI Assistant Settings

SettingDefaultDescription
Enable Admin AI AssistantNoActivate the agentic AI chat in the admin panel
System PromptInstructions for the AI assistant's tone, behavior, and data model context
OpenAI Modelgpt-4oModel used for function calling and answer generation (inherits from Conversational Search if empty)
Conversation TTL86400How long conversation history is retained (seconds)

Note: The Admin AI Assistant requires Conversational Search to be enabled with a valid OpenAI API key. The assistant uses the same API key configured under Conversational Search.


Configurable Attributes

Additional Attributes to Index

Navigate to Stores > Configuration > TypeSense > TypeSense Search > Indexing > Additional Attributes to Index.

Select any product attributes you want included in the Typesense product documents. Core fields (name, SKU, price, description, URL, images, categories) are always indexed. Use this to add attributes like color, size, brand, material, or any custom attribute.

Attributes are grouped by input type (select/multiselect, boolean, numeric, text) so you can quickly identify which ones make sense to index or use as facets. Each entry shows the attribute label, code, and input type — for example: Color (color) — select.

Changes to this setting require a full product reindex to take effect.

Product Tile Attributes

Navigate to Stores > Configuration > TypeSense > TypeSense Search > Instant Search > Product Tile Attributes.

Select which attributes from your indexed set should be visible on product cards in search results and category pages. An attribute must appear in "Additional Attributes to Index" before it can be displayed on tiles.

Sort Options

Navigate to Stores > Configuration > TypeSense > TypeSense Search > Instant Search > Sort Options.

Choose which sort options are available to customers on search results and category pages. Available options:

OptionDescription
RelevanceDefault Typesense relevance ranking
Price: Low to HighAscending price
Price: High to LowDescending price
NewestMost recently created products
Name: A–ZAlphabetical ascending
Name: Z–AAlphabetical descending
Best SellingBased on sales order data
Top RatedBased on review ratings
Most ReviewedBased on review count

CLI Commands

CommandDescription
bin/magento typesense:reindex [--entity=TYPE]Reindex all or a specific entity type into Typesense. Valid types: product, category, cms_page, suggestion
bin/magento typesense:collection:listList all Typesense collections with document count and alias mappings
bin/magento typesense:collection:delete <name>Delete a named Typesense collection
bin/magento typesense:healthCheck the health status of the Typesense server

Examples

# Full reindex of all entity types
bin/magento typesense:reindex

# Reindex only products
bin/magento typesense:reindex --entity=product

# List collections to inspect index state and verify alias assignments
bin/magento typesense:collection:list

# Check server connectivity before indexing
bin/magento typesense:health

Indexing

How It Works

The indexer pipeline is split into three layers:

  1. EntityIndexer — per entity type; fetches records and builds document arrays
  2. IndexerOrchestrator — coordinates collection creation, batched import, and alias management
  3. BatchImportService — chunks documents and sends them to Typesense's bulk import API

Entity Types

EntityCollection PatternMagento Indexer ID
Products<prefix>_product_v<n>typesense_product
Categories<prefix>_category_v<n>typesense_category
CMS Pages<prefix>_cms_page_v<n>typesense_cms_page
Suggestions<prefix>_suggestion_v<n>typesense_suggestion
Orders<prefix>_order_v<n>typesense_order
Customers<prefix>_customer_v<n>typesense_customer
Store<prefix>_store_v<n>typesense_store
System Config<prefix>_system_config_v<n>typesense_system_config

Collections are suffixed with a version number (e.g., rar_product_v2) so that the live collection pointed to by the alias is never written to during a reindex.

Zero-Downtime Reindexing

When zero-downtime reindexing is enabled:

  1. ZeroDowntimeService::startReindex() creates a new versioned collection (e.g., rar_product_v2 while rar_product alias points at rar_product_v1)
  2. All documents are indexed into the new versioned collection
  3. ZeroDowntimeService::finishReindex() atomically swaps the alias to point at the new collection and deletes the old one

This means the live alias (rar_product) always points at a fully populated collection.

Magento Indexer Integration

The module registers standard Magento indexers and supports incremental updates via MView (MySQL change log). After configuration changes or new product saves, the relevant indexer is flagged and can be reindexed via the standard bin/magento indexer:reindex command.


Merchandising

All merchandising is powered by Typesense Curation override rules synced from the Magento admin.

Visual Category Merchandiser

The category merchandiser is embedded directly on the Catalog > Categories edit page — no separate admin screen needed.

On any category's edit page, scroll to the TypeSense Merchandising section to find an Algolia-style sortable product table. You can:

  • Drag and drop products to pin them to specific positions
  • Promote products to the top of the category listing
  • Demote or hide specific products

Changes are saved alongside the category and sync to Typesense curation rules on save.

Virtual Category (Rule-Based Categories)

On any category's edit page, toggle Virtual Category and build a rule with Magento's native nested AND/OR condition builder — the same rule engine used by Catalog Price Rules, limited to attributes present in the Typesense product schema. A virtual category's product membership is computed from that rule instead of manual assignment.

Rules resolve at query time: the condition tree compiles into a Typesense filter_by clause that swaps in for the category's normal product filter, so there's no reindex after saving a rule. A category can also be set up as a virtual root, mirroring another category's matches (optionally narrowed by its own rule), and virtual categories always fold in matches from their own child categories (anchor bleed-up). Pinning and hiding products via the Visual Category Merchandiser continues to work on virtual categories exactly as it does on static ones.

Query Merchandiser

Navigate to Content > TypeSense > Query Merchandiser.

Promote or bury products for specific search queries. Each rule targets a keyword and specifies pinned (top positions) or hidden product IDs.


Frontend

All frontend components require the Hyva theme. They use Alpine.js for reactivity and make direct Typesense API calls using the public search-only API key. All components are written to be CSP-compatible — no inline eval() or dynamic script injection.

Autocomplete

Injected into the Hyva search form. Opens a dropdown with results from all configured entity collections. Configured via the autocomplete settings in admin.

Instant Search Page

Replaces the Magento search results page (/catalogsearch/result). Renders a full search results page with facets, pagination, and sorting powered by Typesense. The default Magento search result blocks are removed from the layout to prevent duplicate results.

Category Page

When enabled, replaces the Magento category product listing with a Typesense-powered equivalent. Filtering, sorting, and pagination all happen client-side against Typesense.

Conversational Search (AI)

When enabled with an OpenAI API key, search results include an AI-generated answer box above the product grid. Uses Typesense's built-in embedding model (ts/all-MiniLM-L12-v2) for semantic search and OpenAI for answer generation. Configurable model, system prompt, and embedding source fields.

Product Recommendations

When enabled, a "You May Also Like" slider appears on every product detail page. Uses Typesense vector similarity search to find semantically similar products based on the current product's embedding — no manual curation needed. Requires Conversational Search to be enabled (for embeddings). The number of recommended products is configurable in admin.


Admin AI Assistant

The Admin AI Assistant is an agentic analytics tool built into the Magento admin panel. It uses OpenAI function calling to intelligently query your store data — deciding which tools to invoke, executing them, and synthesizing the results into a conversational answer.

How It Works

  1. You type a question in the admin chat panel (e.g., "What are my best-selling products this quarter?")
  2. The assistant sends your question plus tool definitions to OpenAI
  3. OpenAI decides which tools to call (e.g., product_velocity, compare_periods)
  4. PHP executes the tools and sends results back to OpenAI
  5. The loop repeats (up to 10 iterations) until a final answer is generated

Available Tools (24)

CategoryTools
Data Accessexecute_sql (read-only sandbox), describe_database, search_typesense
Time Intelligencecompare_periods, trend_analysis
Customer Intelligencecustomer_segmentation (RFM), cohort_analysis, customer_lifetime_value, customer_churn_risk, customer_concentration_risk, customer_purchase_journey
Product Intelligencefrequently_bought_together, inventory_forecast, product_velocity, profit_analysis
Marketing & Attributiondiscount_effectiveness, revenue_attribution, funnel_analysis
Operationalgeographic_performance, time_pattern_analysis, shipping_performance, returns_analysis, basket_analysis, detect_anomalies

Security

  • SQL execution is sandboxed: SELECT-only, blocked sensitive tables/columns, 100-row limit, 5-second timeout
  • Rate limited: 100 requests/hour per admin user
  • All queries are audit-logged with context
  • System prompt and tool call internals are filtered from responses
  • Agent loop has a 60-second wall-clock timeout
  • ACL-protected: requires RunAsRoot_TypeSense::ai_assistant permission

Additional Indexers

The assistant indexes four additional entity types into Typesense beyond the standard search collections:

EntityPurpose
OrdersRevenue, fulfillment, and basket analytics
CustomersSegmentation, LTV, and churn analysis
StoreStore configuration context
System ConfigNon-sensitive configuration entries

Extending

Adding a Custom Entity Indexer

  1. Implement RunAsRoot\TypeSense\Api\EntityIndexerInterface:
<?php

declare(strict_types=1);

namespace Vendor\Module\Model\Indexer;

use RunAsRoot\TypeSense\Api\EntityIndexerInterface;

class BlogPostEntityIndexer implements EntityIndexerInterface
{
    public function getEntityType(): string
    {
        return 'blog_post';
    }

    public function getIndexerCode(): string
    {
        return 'vendor_module_blog_post';
    }

    public function getSchemaFields(): array
    {
        return [
            ['name' => 'id', 'type' => 'string'],
            ['name' => 'title', 'type' => 'string'],
            ['name' => 'content', 'type' => 'string'],
        ];
    }

    public function buildDocuments(array $entityIds, int $storeId): iterable
    {
        // Fetch and yield document arrays
        yield ['id' => '1', 'title' => 'Hello World', 'content' => '...'];
    }
}
  1. Register in your module's etc/di.xml:
<type name="RunAsRoot\TypeSense\Model\Indexer\EntityIndexerPool">
    <arguments>
        <argument name="indexers" xsi:type="array">
            <item name="blog_post" xsi:type="object">Vendor\Module\Model\Indexer\BlogPostEntityIndexer</item>
        </argument>
    </arguments>
</type>
  1. Run bin/magento setup:di:compile.

Your entity type is now available to typesense:reindex --entity=blog_post and to the orchestrator for full and partial reindexing.


Development

Setup

# Clone the repository
git clone git@github.com:run-as-root/Typesense-Magento-2.git

# Install dependencies
composer install

# Run static analysis
vendor/bin/phpstan analyse

# Run unit tests
vendor/bin/phpunit --testsuite Unit

# Run integration tests (requires running Typesense + Magento)
vendor/bin/phpunit --testsuite Integration --group integration

# Run E2E tests (requires Warden environment with indexed data)
cd tests/e2e && npm install && npx playwright install chromium
npx playwright test

# Run E2E tests in UI mode (interactive debugging)
npx playwright test --ui

Architecture Overview

├── Api/                        Interfaces (EntityIndexerInterface, etc.)
├── Block/                      Admin blocks
├── Console/Command/            CLI commands (reindex, collection:list, etc.)
├── Controller/                 Admin AJAX controllers
├── Cron/                       Scheduled reindexing job
├── Model/
│   ├── Client/                 TypeSenseClientFactory (wraps typesense-php)
│   ├── Collection/             CollectionManager, AliasManager, ZeroDowntimeService
│   ├── Config/                 TypeSenseConfig, source models
│   ├── Curation/               Sync services for merchandising rules
│   ├── Indexer/                Entity indexers and orchestrator
│   └── Merchandising/          ORM models and repositories
├── Observer/                   Layout load observer for CSP / frontend injection
├── Plugin/                     CSP dynamic collector
├── Queue/Consumer/             Message queue consumer for async reindexing
├── Test/
│   ├── Unit/                   PHPUnit unit tests
│   └── Integration/            Integration test stubs (@group integration)
├── tests/e2e/                     Playwright E2E tests (Page Object Models + specs)
├── Ui/                         Magento UI component data providers
├── view/adminhtml/             Admin layout, templates, JS
├── view/frontend/              Hyva layout, Alpine.js components
└── ViewModel/                  View models for admin and frontend templates

Code Standards

  • PHP 8.3+ features (readonly properties, enums, fibers where appropriate)
  • Final classes throughout — prefer composition over inheritance
  • declare(strict_types=1) in every file
  • snake_case test method names
  • All public methods covered by unit tests
  • PHPStan level 6

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Write tests first (TDD)
  4. Implement the feature
  5. Ensure all tests pass: vendor/bin/phpunit --testsuite Unit
  6. Run static analysis: vendor/bin/phpstan analyse
  7. Submit a pull request against main

Please keep pull requests focused. One feature or fix per PR.


License

MIT — see LICENSE for full text.