Index Settings Reference

Configure search engine settings for your indices using IndexSettings and IndexSettingsBuilder.

IndexSettings

Data transfer object containing all index settings.

Class: CelerSearch\DataTransfer\IndexSettings

Properties

PropertyTypeDefaultDescription
primary_keystring|nullnullPrimary key attribute
searchable_attributesarrayFields to search in
filterable_attributesarrayFields for filtering
sortable_attributesarrayFields for sorting
distinct_attributestring|nullnullField for deduplication
snippet_attributesarrayFields for result snippets (plugin-side)
synonymsIndexSynonymSearch synonyms
max_hitsint1000Maximum results per search
typo_enabledbooltrueEnable typo tolerance
typo_min_oneint5Min word length for 1 typo
typo_min_twoint9Min word length for 2 typos
typo_disable_wordsarrayWords with no typo tolerance
typo_disable_fieldsarrayFields with no typo tolerance
stop_wordsarrayWords to ignore in search
facet_max_valuesint100Max facet values returned
ranking_rulesarrayCustom ranking rules
custom_ranking_attributesarrayNumeric tie-breakers (attribute:asc|desc), appended after the ranking rules
proximity_precisionstring'byWord'Proximity calculation method
token_separatorsarrayCharacters that separate tokens
non_token_separatorsarrayCharacters kept in tokens
split_enabledbooltrueSplit long content into multiple records (plugin-side)
split_max_sizeint2000Max content size per record before splitting (plugin-side)
embedderarrayAI/semantic search embedder config (empty = not configured)

Getter/Setter Methods

Each property has corresponding getter and setter methods:

PHP
$settings = new IndexSettings();

// Primary key
$settings->set_primary_key('object_id');
$key = $settings->get_primary_key();

// Searchable attributes
$settings->set_searchable_attributes(['title', 'content']);
$attrs = $settings->get_searchable_attributes();

// Filterable attributes
$settings->set_filterable_attributes(['status', 'category']);
$attrs = $settings->get_filterable_attributes();

// Sortable attributes
$settings->set_sortable_attributes(['created_at', 'price']);
$attrs = $settings->get_sortable_attributes();

// Distinct attribute
$settings->set_distinct_attribute('post_id');
$distinct = $settings->get_distinct_attribute();

// Snippet attributes
$settings->set_snippet_attributes(['content']);
$snippets = $settings->get_snippet_attributes();

// Synonyms (IndexSynonym[])
$settings->set_synonyms($synonyms);
$synonyms = $settings->get_synonyms();

// Max hits
$settings->set_max_hits(5000);
$max = $settings->get_max_hits();

// Typo tolerance
$settings->set_typo_enabled(true);
$enabled = $settings->is_typo_enabled();

$settings->set_typo_min_one(4);
$minOne = $settings->get_typo_min_one();

$settings->set_typo_min_two(8);
$minTwo = $settings->get_typo_min_two();

$settings->set_typo_disable_words(['sku', 'isbn']);
$words = $settings->get_typo_disable_words();

$settings->set_typo_disable_fields(['product_code']);
$fields = $settings->get_typo_disable_fields();

// Stop words
$settings->set_stop_words(['the', 'a', 'an']);
$stops = $settings->get_stop_words();

// Faceting
$settings->set_facet_max_values(500);
$max = $settings->get_facet_max_values();

// Ranking rules
$settings->set_ranking_rules(['words', 'typo', 'proximity']);
$rules = $settings->get_ranking_rules();

// Custom ranking (numeric tie-breakers, appended after the ranking rules)
$settings->set_custom_ranking_attributes(['total_sales:desc', 'post_date:desc']);
$custom = $settings->get_custom_ranking_attributes();

// Proximity precision
$settings->set_proximity_precision('byWord');
$precision = $settings->get_proximity_precision();

// Token separators
$settings->set_token_separators(['/', '\\']);
$seps = $settings->get_token_separators();

// Non-token separators
$settings->set_non_token_separators(['#', '@']);
$nonseps = $settings->get_non_token_separators();

// Content splitting
$settings->set_split_enabled(true);
$enabled = $settings->is_split_enabled();

$settings->set_split_max_size(3000);
$size = $settings->get_split_max_size();

// Embedder (AI/semantic search)
$settings->set_embedder($embedder);
$embedder = $settings->get_embedder();

IndexSettingsBuilder

Fluent builder for constructing IndexSettings objects.

Class: CelerSearch\Builders\IndexSettingsBuilder

Basic Usage

PHP
use CelerSearch\Builders\IndexSettingsBuilder;

$settings = (new IndexSettingsBuilder())
    ->primary_key('object_id')
    ->searchable_attributes(['title', 'content', 'excerpt'])
    ->filterable_attributes(['status', 'post_type', 'taxonomies'])
    ->sortable_attributes(['created_at', 'updated_at'])
    ->build();

All Builder Methods

Attribute Configuration

PHP
$builder = new IndexSettingsBuilder();

// Primary key (unique identifier field)
$builder->primary_key('object_id');

// Searchable attributes (searched for matches)
$builder->searchable_attributes([
    'title',        // Highest priority
    'content',      // Medium priority
    'excerpt',      // Lower priority
]);

// Filterable attributes (used in filters)
$builder->filterable_attributes([
    'status',
    'post_type',
    'taxonomies.category.slug',
    'author.id',
]);

// Sortable attributes (used for sorting)
$builder->sortable_attributes([
    'created_at',
    'updated_at',
    'title',
]);

// Distinct attribute (for deduplication)
$builder->distinct_attribute('post_id');

// Snippet attributes (for result excerpts)
$builder->snippet_attributes(['content']);

Synonyms

PHP
use CelerSearch\DataTransfer\IndexSynonym;

// Each IndexSynonym maps a word to its corrections
$builder->synonyms([
    new IndexSynonym('laptop', ['notebook', 'computer']),
    new IndexSynonym('phone', ['mobile', 'smartphone']),
]);

Pagination

PHP
// Maximum results per search
$builder->max_hits(5000);  // Default: 1000

Typo Tolerance

PHP
// Enable/disable typo tolerance
$builder->typo_enabled(true);

// Minimum word length for 1 typo correction
$builder->typo_min_one(4);  // Default: 5

// Minimum word length for 2 typo corrections
$builder->typo_min_two(8);  // Default: 9

// Disable typo tolerance for specific words
$builder->typo_disable_words(['sku', 'isbn', 'upc']);

// Disable typo tolerance for specific fields
$builder->typo_disable_fields(['product_code', 'barcode']);

Stop Words

PHP
// Words ignored in search
$builder->stop_words([
    'the', 'a', 'an', 'and', 'or', 'but',
    'in', 'on', 'at', 'to', 'for',
]);

Faceting

PHP
// Maximum facet values per attribute
$builder->facet_max_values(500);  // Default: 100

Ranking

PHP
// Custom ranking rules
$builder->ranking_rules([
    'words',        // Number of matched words
    'typo',         // Typo tolerance
    'proximity',    // Word proximity
    'attribute',    // Attribute importance
    'sort',         // Custom sort
    'exactness',    // Exact matches first
    'is_featured:desc',  // Custom: featured first
    'popularity:desc',   // Custom: popular first
]);

// Custom ranking — numeric tie-breakers appended AFTER the ranking rules
// above (Algolia-style). Applied in order; deduped against existing rules.
$builder->custom_ranking_attributes([
    'total_sales:desc',   // best-sellers first
    'ratings.average_rating:desc',
]);

// Proximity precision
$builder->proximity_precision('byWord');  // or 'byAttribute'

Tokenization

PHP
// Characters that separate tokens
$builder->token_separators(['/', '\\', '|', '@']);

// Characters kept as part of tokens
$builder->non_token_separators(['#', '+', '-']);

Content Splitting

Long content can be split into multiple records per item (handled by the plugin, not the engine):

PHP
$builder->split_enabled(true);   // Default: true
$builder->split_max_size(3000);  // Default: 2000 (characters per record)

Configures a Meilisearch embedder for hybrid (keyword + semantic) search. The array shape is source-agnostic; v1 ships with the openAi source:

PHP
$builder->embedder([
    'enabled'        => true,
    'source'         => 'openAi',
    'semantic_ratio' => 0.5,   // 0.0 = pure keyword, 1.0 = pure semantic
    'configs'        => [
        'openAi' => [
            'model'                       => 'text-embedding-3-small', // or text-embedding-3-large
            'dimensions'                  => 1536,
            // Guarded products template — only reference fields the index type
            // emits (posts: title/excerpt/content; products: post_title/
            // short_description/content). Leave unset to use the index default.
            'document_template'           => "{% if doc.post_title %}{{ doc.post_title }}\n{% endif %}{% if doc.content %}{{ doc.content }}{% endif %}",
            'document_template_max_bytes' => 3000,
            'api_key'                     => 'sk-...',
        ],
    ],
]);

When enabled, the MeiliSearch service maps this to the engine's embedders.default payload on push_settings(), and search requests automatically get hybrid parameters using semantic_ratio. An unset/empty document_template resolves to the index type's own default (see Document Templates). Additional sources can be registered via the celersearch_embedder_sources filter; the final engine payload can be adjusted via celersearch_service_meilisearch_embedders, and the per-query hybrid params via celersearch_hybrid_search_params.

Loading from Stored Config

PHP
// Load settings from database-stored array (admin UI overrides)
$stored = [
    'typo_enabled' => true,
    'typo_min_one' => 4,
    'max_hits' => 2000,
    'stop_words' => ['the', 'a'],
];

$settings = (new IndexSettingsBuilder())
    ->searchable_attributes(['title', 'content'])
    ->from_stored($stored)
    ->build();

Inside a BaseIndex subclass, call ->from_stored($this->get_stored_settings()) so admin-saved settings override your defaults. Empty stored attribute lists fall back to the defaults your index declared.

Building the Object

PHP
// Create the IndexSettings object
$settings = $builder->build();

Usage in Custom Indices

The get_settings() method in your custom index should return an IndexSettings object:

PHP
use CelerSearch\Indices\BaseIndex;
use CelerSearch\Builders\IndexSettingsBuilder;
use CelerSearch\DataTransfer\IndexSettings;

class EventsIndex extends BaseIndex {

    public function get_settings(): IndexSettings {
        return (new IndexSettingsBuilder())
            ->primary_key('object_id')
            ->searchable_attributes([
                'title',
                'description',
                'venue_name',
                'organizer',
            ])
            ->filterable_attributes([
                'status',
                'event_type',
                'venue_city',
                'taxonomies.event_category.slug',
                'start_date',
                'is_featured',
            ])
            ->sortable_attributes([
                'start_date',
                'created_at',
                'title',
            ])
            ->distinct_attribute('event_id')
            ->typo_enabled(true)
            ->typo_min_one(4)
            ->typo_disable_fields(['event_code'])
            ->stop_words(['event', 'conference', 'meeting'])
            ->ranking_rules([
                'words',
                'typo',
                'proximity',
                'attribute',
                'sort',
                'exactness',
                'is_featured:desc',
                'start_date:asc',
            ])
            ->max_hits(2000)
            ->from_stored($this->get_stored_settings())
            ->build();
    }
}

Customizing via Filters

The built-in indices pass their IndexSettingsBuilder (not the built object) through a filter before calling build(), so use the builder's fluent methods:

PHP
// Available: celersearch_posts_index_settings,
//            celersearch_products_index_settings,
//            celersearch_orders_index_settings
add_filter('celersearch_posts_index_settings', function($builder, $index) {
    return $builder
        ->stop_words(['read', 'more', 'click', 'here'])
        ->max_hits(5000);
}, 10, 2);

To adjust the final engine payload instead, use the service-level filters (celersearch_service_settings, celersearch_service_meilisearch_*) — see Service hooks.


Provider Setting Mappings

IndexSettings properties are mapped to provider-specific settings by each service's build_settings(). Below is the MeiliSearch mapping:

IndexSettingsMeiliSearch
primary_keyprimaryKey (set at index creation, not via updateSettings)
searchable_attributessearchableAttributes
filterable_attributesfilterableAttributes
sortable_attributessortableAttributes
distinct_attributedistinctAttribute
synonymssynonyms
ranking_rulesrankingRules
custom_ranking_attributesappended to rankingRules after the textual rules (deduped)
stop_wordsstopWords
typo_enabledtypoTolerance.enabled
typo_min_onetypoTolerance.minWordSizeForTypos.oneTypo
typo_min_twotypoTolerance.minWordSizeForTypos.twoTypos
typo_disable_wordstypoTolerance.disableOnWords
typo_disable_fieldstypoTolerance.disableOnAttributes
facet_max_valuesfaceting.maxValuesPerFacet
max_hitspagination.maxTotalHits
proximity_precisionproximityPrecision
token_separatorsseparatorTokens
non_token_separatorsnonSeparatorTokens
embedderembedders.default (when enabled)

snippet_attributes, split_enabled, and split_max_size are plugin-side settings — they are not pushed to the engine.

Required filterable attributes

build_settings() always unions $index->required_filterable_attributes() (default ['status']) into filterableAttributes, so the plugin's own query filters keep working even if an admin curates them out.


Complete Example

E-commerce product index with comprehensive settings:

PHP
use CelerSearch\Indices\BaseIndex;
use CelerSearch\Builders\IndexSettingsBuilder;
use CelerSearch\DataTransfer\IndexSettings;

class StoreProductsIndex extends BaseIndex {

    public function get_settings(): IndexSettings {
        return (new IndexSettingsBuilder())
            // Core settings
            ->primary_key('object_id')
            ->distinct_attribute('product_id')

            // Search configuration
            ->searchable_attributes([
                'name',           // Highest priority
                'short_description',
                'description',
                'sku',
                'categories',
                'tags',
                'attributes',
            ])

            // Filter & sort configuration
            ->filterable_attributes([
                'status',
                'stock_status',
                'is_on_sale',
                'is_featured',
                'price',
                'average_rating',
                'taxonomies.product_cat.slug',
                'taxonomies.product_tag.slug',
                'attributes.pa_color',
                'attributes.pa_size',
            ])
            ->sortable_attributes([
                'price',
                'average_rating',
                'sales_count',
                'created_at',
                'name',
            ])

            // Typo tolerance
            ->typo_enabled(true)
            ->typo_min_one(4)
            ->typo_min_two(8)
            ->typo_disable_words(['sku'])
            ->typo_disable_fields(['sku', 'product_code'])

            // Stop words (e-commerce noise)
            ->stop_words([
                'buy', 'shop', 'sale', 'discount',
                'free', 'shipping', 'delivery',
            ])

            // Ranking (e-commerce optimized)
            ->ranking_rules([
                'words',
                'typo',
                'proximity',
                'attribute',
                'sort',
                'exactness',
                'in_stock:desc',      // In-stock first
                'is_featured:desc',   // Featured products
                'sales_count:desc',   // Bestsellers
            ])

            // Pagination & faceting
            ->max_hits(10000)
            ->facet_max_values(500)

            // Tokenization
            ->proximity_precision('byWord')
            ->token_separators(['/', '-'])
            ->non_token_separators(['#', '+'])

            // Admin-saved overrides
            ->from_stored($this->get_stored_settings())

            ->build();
    }
}