Service Hooks

Customize search engine service configuration and settings.

Provider-Specific Hooks

Many hooks in this section are provider-specific (e.g., celersearch_service_meilisearch_*). Similar hooks will be available for other providers as they're added.

Service Settings

celersearch_service_settings

Modify settings for all services.

PHP
add_filter('celersearch_service_settings', function($settings, $service) {
    // Add global settings
    $settings['maxTotalHits'] = 10000;
    return $settings;
}, 10, 2);

celersearch_service_{provider}_settings

Modify settings for a specific provider.

PHP
add_filter('celersearch_service_meilisearch_settings', function($settings, $config, $service) {
    // MeiliSearch-specific settings
    $settings['displayedAttributes'] = ['title', 'content', 'url'];
    return $settings;
}, 10, 3);

celersearch_required_filterable_attributes

Filter the attributes that are always kept filterable when settings are pushed to the engine, regardless of how an admin curated the index's filterable list. The plugin's own query paths filter on these (e.g. the status = publish storefront filter) — if such an attribute stopped being filterable, Meilisearch would reject the filter and silently break search.

PHP
add_filter('celersearch_required_filterable_attributes', function($required, $index) {
    // My integration always filters on visibility
    $required[] = 'visibility';

    return $required;
}, 10, 2);
ParameterTypeDescription
$requiredstringAttribute names merged into the engine's filterableAttributes
$indexBaseIndexThe index whose settings are being built

Defaults: ['status'] for all indices. The Products index adds price, in_stock, ratings.average_rating, and taxonomies (required by the shop browse/filter feature).


MeiliSearch Configuration

celersearch_service_meilisearch_stop_words

Customize stop words (words ignored in search).

PHP
add_filter('celersearch_service_meilisearch_stop_words', function($words, $service) {
    // Add custom stop words
    $words = array_merge($words, [
        'lorem', 'ipsum', 'dolor',
        'click', 'here', 'read', 'more',
    ]);

    // Add language-specific stop words
    $words = array_merge($words, [
        // German
        'der', 'die', 'das', 'und',
        // French
        'le', 'la', 'les', 'et',
    ]);

    return array_unique($words);
}, 10, 2);

celersearch_service_meilisearch_synonyms

Define search synonyms.

PHP
add_filter('celersearch_service_meilisearch_synonyms', function($synonyms, $service) {
    return array_merge($synonyms, [
        // Product synonyms
        'laptop'    => ['notebook', 'computer', 'pc'],
        'phone'     => ['mobile', 'smartphone', 'cell'],

        // Brand synonyms
        'iphone'    => ['apple phone', 'ios phone'],

        // Size synonyms
        'small'     => ['s', 'sm', 'petit'],
        'medium'    => ['m', 'med'],
        'large'     => ['l', 'lg', 'big'],
        'xl'        => ['extra large', 'xlarge'],
    ]);
}, 10, 2);

celersearch_service_meilisearch_ranking_rules

Customize result ranking.

PHP
add_filter('celersearch_service_meilisearch_ranking_rules', function($rules, $service) {
    // Default MeiliSearch rules
    return [
        'words',        // Number of matched words
        'typo',         // Typo tolerance
        'proximity',    // Word proximity
        'attribute',    // Attribute ranking order
        'sort',         // Custom sort rules
        'exactness',    // Exact matches first
        // Add custom ranking
        'is_featured:desc',  // Featured items first
        'popularity:desc',   // Popular items higher
    ];
}, 10, 2);

celersearch_service_meilisearch_custom_ranking

Filter the numeric tie-breakers (attribute:asc|desc) from the Custom Ranking index setting before they're appended to rankingRules, after the textual rules above. See Custom Ranking.

PHP
add_filter('celersearch_service_meilisearch_custom_ranking', function($rules, $service) {
    $rules[] = 'total_sales:desc'; // always tie-break by sales
    return array_values(array_unique($rules));
}, 10, 2);

celersearch_service_meilisearch_typo_tolerance

Configure typo tolerance.

PHP
add_filter('celersearch_service_meilisearch_typo_tolerance', function($config, $service) {
    return [
        'enabled' => true,
        'minWordSizeForTypos' => [
            'oneTypo' => 4,   // Allow 1 typo for 4+ char words
            'twoTypos' => 8,  // Allow 2 typos for 8+ char words
        ],
        'disableOnWords' => [
            // Exact match required for these
            'sku', 'isbn', 'upc',
        ],
        'disableOnAttributes' => [
            // No typo tolerance on these fields
            'sku', 'product_code',
        ],
    ];
}, 10, 2);

celersearch_service_meilisearch_faceting

Configure faceted search.

PHP
add_filter('celersearch_service_meilisearch_faceting', function($config, $service) {
    return [
        'maxValuesPerFacet' => 1000,  // Max facet values
        'sortFacetValuesBy' => [
            'category' => 'alpha',    // Sort alphabetically
            'price'    => 'count',    // Sort by occurrence
        ],
    ];
}, 10, 2);

celersearch_service_meilisearch_pagination

Configure pagination limits.

PHP
add_filter('celersearch_service_meilisearch_pagination', function($config) {
    return [
        'maxTotalHits' => 10000,  // Max searchable results
    ];
});

celersearch_service_meilisearch_proximity_precision

Configure proximity precision.

PHP
add_filter('celersearch_service_meilisearch_proximity_precision', function($precision, $service) {
    // 'byWord' or 'byAttribute'
    return 'byWord';
}, 10, 2);

Token Configuration

celersearch_service_meilisearch_separator_tokens

Define characters that separate words.

PHP
add_filter('celersearch_service_meilisearch_separator_tokens', function($tokens, $service) {
    return array_merge($tokens, [
        '/', '\\', '|', '@',
    ]);
}, 10, 2);

celersearch_service_meilisearch_non_separator_tokens

Define characters that don't separate words.

PHP
add_filter('celersearch_service_meilisearch_non_separator_tokens', function($tokens, $service) {
    // Keep these as part of words
    return ['#', '@', '+', '-'];
}, 10, 2);

celersearch_service_meilisearch_dictionary

Add custom dictionary entries.

PHP
add_filter('celersearch_service_meilisearch_dictionary', function($dictionary, $service) {
    // Keep compound words together
    return array_merge($dictionary, [
        'new york',
        'los angeles',
        'san francisco',
        'iphone pro',
        'macbook air',
    ]);
}, 10, 2);

AI / Embedders

Hooks for AI semantic and hybrid search. See the AI Semantic Search guide for setup and concepts.

celersearch_embedder_sources

Register or modify embedder source definitions. Each source declares its admin form fields — type, default, sanitizer, secret flag — and how each field maps onto Meilisearch's embedder payload keys (engine_key). The plugin registers openAi by default; third parties can register additional sources (e.g. rest, ollama, userProvided). Fields render generically in the admin UI, and fields flagged secret get value masking automatically. Storage is per-source (embedder.configs[<slug>]), so adding a source is data, not a migration.

PHP
add_filter('celersearch_embedder_sources', function($sources) {
    $sources[] = [
        'slug'   => 'ollama',
        'label'  => 'Ollama',
        'fields' => [
            [
                'name'       => 'model',
                'label'      => fn() => __('Model', 'my-plugin'),
                'type'       => 'text',
                'default'    => 'nomic-embed-text',
                'engine_key' => 'model',
                'sanitize'   => fn($value) => sanitize_text_field((string) $value),
            ],
            [
                'name'       => 'url',
                'label'      => fn() => __('URL', 'my-plugin'),
                'type'       => 'text',
                'default'    => 'http://localhost:11434/api/embed',
                'engine_key' => 'url',
                'sanitize'   => fn($value) => esc_url_raw((string) $value),
            ],
            [
                'name'       => 'document_template',
                'label'      => fn() => __('Document template', 'my-plugin'),
                'type'       => 'textarea',
                // Context-less fallback; with an index in scope the default is
                // the index type's own template. Guard fields with {% if %} —
                // an unknown field fails the document's whole batch.
                'default'    => "{% if doc.title %}{{ doc.title }}\n{% endif %}{% if doc.content %}{{ doc.content }}{% endif %}",
                'engine_key' => 'documentTemplate',
                'sanitize'   => fn($value) => sanitize_textarea_field((string) $value),
            ],
        ],
    ];

    return $sources;
});
ParameterTypeDescription
$sourcesarraySource definitions. Each: slug, label, fields — and each field: name, label (callable returning the translated string), type, default, engine_key, sanitize (callable), optional secret (bool) and options

celersearch_embedder_document_template

Filter the default document template an index pre-fills (and falls back to when the stored template is empty). Each index type ships a default built from its own document fields — posts emit title/excerpt/content while products emit post_title/short_description/content — so use this to adjust the default for a specific index or a custom index type. Keep every field inside {% if doc.field %}…{% endif %} guards: rendering is strict and an unknown field fails the document's whole batch.

PHP
add_filter('celersearch_embedder_document_template', function($template, $index) {
    if ($index->get_slug() === 'woocommerce-products') {
        $template .= "\n{% if doc.sku %}SKU {{ doc.sku }}{% endif %}";
    }
    return $template;
}, 10, 2);
ParameterTypeDescription
$templatestringLiquid template rendered per document to produce the embedded text
$indexBaseIndexThe index the template applies to

celersearch_service_meilisearch_embedders

Filter the embedders payload pushed to Meilisearch as part of index settings. The plugin maps the stored embedder config to a single embedder named default; external integrations can inject or override embedders here. Because this rides push_settings(), embedders added here are re-applied on every full rebuild (index delete/recreate) — they inherit the plugin's rebuild persistence.

PHP
add_filter('celersearch_service_meilisearch_embedders', function($embedders, $index, $service) {
    // Add a second, self-hosted embedder. Use the index's own template so the
    // fields always match its documents (or write your own guarded template).
    $embedders['ollama'] = [
        'source'           => 'ollama',
        'model'            => 'nomic-embed-text',
        'url'              => 'http://localhost:11434/api/embed',
        'documentTemplate' => $index->get_embedder_document_template(),
    ];

    return $embedders;
}, 10, 3);
ParameterTypeDescription
$embeddersarrayEmbedder name => Meilisearch embedder payload. Contains default when an embedder is configured and enabled
$indexBaseIndexThe index whose settings are being built
$serviceMeiliSearchThe service instance

When the filtered array is empty, the embedders key is omitted from the settings payload entirely — non-destructive to embedders managed outside the plugin.

celersearch_hybrid_search_params

Override the hybrid (semantic + keyword) search parameters per query. This runs centrally in the MeiliSearch service, so every call path — main query interception, autocomplete REST, views, WooCommerce shop filters — is covered.

PHP
add_filter('celersearch_hybrid_search_params', function($hybrid, $index, $params) {
    // Lean more semantic on the products index
    if ($index->get_slug() === 'products') {
        return ['embedder' => 'default', 'semanticRatio' => 0.8];
    }

    // Return null (or an empty array) to force pure keyword search
    return $hybrid;
}, 10, 3);
ParameterTypeDescription
$hybridarray|nullHybrid parameters (embedder, semanticRatio), or null when no embedder is enabled
$indexBaseIndexThe index being searched
$paramsarrayThe search parameters for this request (before hybrid injection)

The default is built from the index's embedder settings: ['embedder' => 'default', 'semanticRatio' => <ratio>] when an embedder is enabled with a semantic ratio above 0 (the ratio is clamped to 0–1), otherwise null. Callers that set hybrid in the search params themselves win — the filter is skipped in that case.


Provider Registration

celersearch_supported_providers

Register custom search providers.

PHP
add_filter('celersearch_supported_providers', function($providers) {
    $providers[] = [
        'name'   => 'Typesense',
        'slug'   => 'typesense',
        'model'  => \MyPlugin\Providers\TypesenseProvider::class,
        'fields' => [
            [
                'label' => __('API URL', 'my-plugin'),
                'name'  => 'url',
                'type'  => 'url',
                'rules' => 'required',
            ],
            [
                'label' => __('API Key', 'my-plugin'),
                'name'  => 'api_key',
                'type'  => 'password',
                'rules' => 'required',
            ],
            [
                'label' => __('Protocol', 'my-plugin'),
                'name'  => 'protocol',
                'type'  => 'dropdown',
                'options' => [
                    ['label' => 'HTTPS', 'value' => 'https'],
                    ['label' => 'HTTP', 'value' => 'http'],
                ],
            ],
        ],
    ];

    return $providers;
});

Complete Example

Optimize MeiliSearch for e-commerce:

PHP
<?php
// Stop words for cleaner search
add_filter('celersearch_service_meilisearch_stop_words', function($words) {
    return array_merge($words, [
        // Common filler words
        'the', 'a', 'an', 'and', 'or', 'but',
        // E-commerce noise
        'buy', 'shop', 'sale', 'discount', 'cheap',
        'free', 'shipping', 'delivery',
    ]);
});

// Product synonyms
add_filter('celersearch_service_meilisearch_synonyms', function($synonyms) {
    return [
        // Size synonyms
        'xs'     => ['extra small'],
        's'      => ['small'],
        'm'      => ['medium'],
        'l'      => ['large'],
        'xl'     => ['extra large'],
        'xxl'    => ['2xl', 'double xl'],

        // Color synonyms
        'grey'   => ['gray'],
        'colour' => ['color'],

        // Category synonyms
        'tee'    => ['t-shirt', 'tshirt'],
        'pants'  => ['trousers', 'jeans'],
        'shoes'  => ['footwear', 'sneakers'],
    ];
});

// Ranking for e-commerce
add_filter('celersearch_service_meilisearch_ranking_rules', function($rules) {
    return [
        'words',
        'typo',
        'proximity',
        'attribute',
        'sort',
        'exactness',
        // E-commerce specific
        'in_stock:desc',     // In-stock first
        'is_featured:desc',  // Featured products
        'sales_count:desc',  // Bestsellers higher
    ];
});

// Typo tolerance
add_filter('celersearch_service_meilisearch_typo_tolerance', function($config) {
    return [
        'enabled' => true,
        'minWordSizeForTypos' => [
            'oneTypo'  => 4,
            'twoTypos' => 8,
        ],
        'disableOnAttributes' => [
            'sku',
            'product_id',
            'barcode',
        ],
    ];
});

// Faceting for filters
add_filter('celersearch_service_meilisearch_faceting', function($config) {
    return [
        'maxValuesPerFacet' => 500,
        'sortFacetValuesBy' => [
            'brand'    => 'alpha',
            'category' => 'alpha',
            'size'     => 'alpha',
            'color'    => 'alpha',
            'price'    => 'count',
        ],
    ];
});