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
| Property | Type | Default | Description |
|---|---|---|---|
primary_key | string|null | null | Primary key attribute |
searchable_attributes | array | Fields to search in | |
filterable_attributes | array | Fields for filtering | |
sortable_attributes | array | Fields for sorting | |
distinct_attribute | string|null | null | Field for deduplication |
snippet_attributes | array | Fields for result snippets (plugin-side) | |
synonyms | IndexSynonym | Search synonyms | |
max_hits | int | 1000 | Maximum results per search |
typo_enabled | bool | true | Enable typo tolerance |
typo_min_one | int | 5 | Min word length for 1 typo |
typo_min_two | int | 9 | Min word length for 2 typos |
typo_disable_words | array | Words with no typo tolerance | |
typo_disable_fields | array | Fields with no typo tolerance | |
stop_words | array | Words to ignore in search | |
facet_max_values | int | 100 | Max facet values returned |
ranking_rules | array | Custom ranking rules | |
custom_ranking_attributes | array | Numeric tie-breakers (attribute:asc|desc), appended after the ranking rules | |
proximity_precision | string | 'byWord' | Proximity calculation method |
token_separators | array | Characters that separate tokens | |
non_token_separators | array | Characters kept in tokens | |
split_enabled | bool | true | Split long content into multiple records (plugin-side) |
split_max_size | int | 2000 | Max content size per record before splitting (plugin-side) |
embedder | array | AI/semantic search embedder config (empty = not configured) |
Getter/Setter Methods
Each property has corresponding getter and setter methods:
$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
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
$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
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
// Maximum results per search
$builder->max_hits(5000); // Default: 1000
Typo Tolerance
// 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
// Words ignored in search
$builder->stop_words([
'the', 'a', 'an', 'and', 'or', 'but',
'in', 'on', 'at', 'to', 'for',
]);
Faceting
// Maximum facet values per attribute
$builder->facet_max_values(500); // Default: 100
Ranking
// 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
// 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):
$builder->split_enabled(true); // Default: true
$builder->split_max_size(3000); // Default: 2000 (characters per record)
Embedder (AI / Semantic Search)
Configures a Meilisearch embedder for hybrid (keyword + semantic) search. The array shape is source-agnostic; v1 ships with the openAi source:
$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
// 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
// Create the IndexSettings object
$settings = $builder->build();
Usage in Custom Indices
The get_settings() method in your custom index should return an IndexSettings object:
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:
// 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:
| IndexSettings | MeiliSearch |
|---|---|
primary_key | primaryKey (set at index creation, not via updateSettings) |
searchable_attributes | searchableAttributes |
filterable_attributes | filterableAttributes |
sortable_attributes | sortableAttributes |
distinct_attribute | distinctAttribute |
synonyms | synonyms |
ranking_rules | rankingRules |
custom_ranking_attributes | appended to rankingRules after the textual rules (deduped) |
stop_words | stopWords |
typo_enabled | typoTolerance.enabled |
typo_min_one | typoTolerance.minWordSizeForTypos.oneTypo |
typo_min_two | typoTolerance.minWordSizeForTypos.twoTypos |
typo_disable_words | typoTolerance.disableOnWords |
typo_disable_fields | typoTolerance.disableOnAttributes |
facet_max_values | faceting.maxValuesPerFacet |
max_hits | pagination.maxTotalHits |
proximity_precision | proximityPrecision |
token_separators | separatorTokens |
non_token_separators | nonSeparatorTokens |
embedder | embedders.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:
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();
}
}