Configuration

Below is an example of Dexter's default settings. Add it to your ExpressionEngine system/user/config/config.php file. You do not have to copy the entire array. Array keys not found in your config.php file will fall back to the default values. Your config.php file should look similar to this:

<?php

$config['dexter'] = [
    'provider' => 'meilisearch',
    ...
];

The full default config.

<?php

/**
 * DO NOT EDIT THIS FILE DIRECTLY.
 *
 * Copy this file to your /config directory and rename it to dexter.php
 * to override the default settings. You can remove any settings you don't want to change,
 * and they will fall back to the defaults defined here.
 */

return [
    // Choose which provider you're using.
    'provider' => 'meilisearch', // or "algolia"

    // Then set the required config values for it.
    'algolia' => [
        'appId' => '',
        'apiKey' => '',
        'mode' => 'keywordSearch', // or neuralSearch if using the Premium or Elevate plans.
    ],
    'meilisearch' => [
        'url' => '',
        'appKey' => '',
    ],

    // A prefix will be added to each index based on the environment. If you're using the .env file, you can
    // set this to $_ENV['ENVIRONMENT'] to read from the .env file, or override this in your config/dexter.php file
    // with a hard-coded value, or another custom ENV variable. You can also leave it blank to use no prefix.
    'env' => 'test',

    // Add a suffix to the end of all indices. Useful for multilingual sites.
    // If you are using Publisher this value should be left blanks as it will automatically be updated.
    // Can be a string or a callable function. For example:
    /*
    'suffix' => function (...$args) {
        @todo Determine the current language and return its short name.
        return '_' . $lang->getShortName();
    },
    */
    'suffix' => '',

    // Add the group name handle to be used to construct a menu a hierarchical menus, specifically
    // designed to render output necessary for Algolia's React HierarchicalMenu component
    // https://www.algolia.com/doc/api-reference/widgets/hierarchical-menu/react/
    // The category custom field must also be included in the customFields array.
    'categoryMenuGroups' => [],

    // Any other category groups. Will index with the group name as the object key, and each
    // assigned category within the group with its ID and name.
    'categoryGroups' => [],

    // Similar to categoryGroups where you add the group ID to this config array, but will index
    // as a flat list of categories in an array, regardless of the group.
    'categories' => [],

    // A list of field names to be indexed. If nothing is defined, everything will be indexed.
    'indexableFields' => [
        //'entries' => [
        //    'title',
        //    'contentBlocks', // Index contentBlocks and all nested fields.
        //    'contentBlocks.*.fields.heading', // Index just the heading field of each content block.
        //],

        // The above will apply to all entries. If you want different fields for specific types, use the following format:
        //'entries[typeHandle]' => [
        //],

        //'categories' => [
        //],

        //'categories[groupHandle]' => [
        //],

        //'files' => [
        //    'url',
        //    'attribution',
        //    'imageCaption',
        //    'contentBlocks.*.fields.richText',
        //],

        //'files[volumeHandle]' => [
        //],

        //'users' => [
        //],

        //'users[groupHandle]' => [
        //],
    ],

    // Required - only entries within the defined types will be indexed. Files from defined volumes can
    // also be indexed. In the example below, the numerical value at the end is the channel ID or upload directory ID.
    // Since channel names can change, and upload directories don't have short names, Dexter uses their respective IDs.
    // Dexter cannot target specific subdirectories within an upload destination.
    'indices' => [
        'entries' => [
            //'page' => 'pages',
            //'productPages' => 'pages',
            //'blog' => 'blog',
        ],
        'files' => [
            //'digitalOcean' => 'files',
        ],
        'categories' => [
            //'callSigns' => 'call_signs',
            //'fruits' => 'fruits',
        ],
        'users' => [
        ],
    ],

    // Adds a __full_text property when indexing entries, which is a conglomeration of all text found in individual
    // fields being indexed. Useful for full-text searching and required if you are using the FileDescribePipeline.
    'includeFullText' => true,

    // Custom pipelines for additional customization.
    // Need to be properly namespaced, but can be loaded from anywhere.
    'entryPipelines' => [
        // MyCustomPipeline::class,
        // \BoldMinded\DexterPipelines\Pipelines\ExamplePipeline::class,
    ],

    // Pipelines applied only when indexing categories.
    'categoryPipelines' => [
        // MyCustomCategoryPipeline::class,
    ],

    // Pipelies applied only when indexing files.
    'filePipelines' => [
        // MyCustomFilePipeline::class,
        // \BoldMinded\Dexter\Service\Pipeline\FileDescribePipeline::class,
    ],

    'userPipelines' => [
        // MyCustomMemberPipeline::class,
    ],

    // If indexing files, and a pdf, txt, csv, or other text-based file is uploaded it will try to scrape the contents
    // of the file and add it to the __full_text property.
    'parseDocumentContents' =>  [
        'enabled' => true,
        'describePrompt' => 'Summarize this text.',
        'maxPages' => 5, // Limit the number of pages to parse from a text file.
        'maxWords' => 1000, // Limit the number of words to parse from a text file.
        'categoryGroupHandle' => '',

        // If true, the source file description and categories will be created with the AI response.
        // If parseDocumentContents.enabled is true, you generally want to keep this enabled as well.
        'createDescription' => true,
        'createCategories' => true,

        // If true, any updates to the file will fetch a new AI response and update the source file accordingly.
        // Leave false if you want to manually manage the description and categories after it is initially created.
        'replaceDescription' => false,
        'replaceCategories' => false,
    ],

    // If indexing image files (jpg, png, or gif) it will send the image to OpenAI to describe the contents
    // of the file and add it to the __full_text property.
    'parseImageContents' =>  [
        'enabled' => true,
        'describePrompt' => 'Describe this image in detail, including any text that may be present.',
        'categoryGroupHandle' => '',

        // If true, the source file description and categories will be created with the AI response.
        // If parseImageContents.enabled is true, you generally want to keep this enabled as well.
        'createDescription' => true,
        'createCategories' => true,

        // If true, any updates to the file will fetch a new AI response and update the source file accordingly.
        // Leave false if you want to manually manage the description and categories after it is initially created.
        'replaceDescription' => false,
        'replaceCategories' => false,

        // The field handle of a Plain Text field to store the description. Use dot notation for nested fields.
        'descriptionFieldHandle' => '',
        'categoriesFieldHandle' => '',
    ],

    'aiProvider' => 'openAi',
    'embeddingProvider' => 'openAi',
    'openAi' => [
        'key' => $_ENV['OPENAI_API_KEY'] ?? '',
        'embedModel' => 'text-embedding-3-small',
        'model' => 'gpt-4o',
        'temperature' => 1.0, // The temperature of the response, 0.0 to 2.0.
        'frequencyPenalty' => 0.0, // The frequency penalty of the response,
        'presencePenalty' => 0.0, // The presence penalty of the response.
        'maxTokens' => 300, // The maximum number of tokens to return in the response.
    ],

    // If you are using Meilisearch, you can enable AI/vector-based searching. Using this feature requires
    // includeFullText to be enabled. When content is indexed, it will use the __full_text property to generate
    // embeddings/vectors for each object. This will allow you to search for similar content based on the text
    // in the __full_text property. You must also include the following filter in your search queries:
    // "hybrid": {
    //     "embedder": "fullText"
    // }
    // https://www.meilisearch.com/docs/learn/ai_powered_search/getting_started_with_ai_search
    'enableContextSearch' => false,

    // If true, Dexter will use the defined aiProvider to further filter the search results.
    // This will not add additional results to the list, it will only perform an additional search filtering
    // the initial results based on the query, which can take into account advanced logic such as "NOT" and "OR" operators.
    // enableContextSearch must be enabled
    'enableAdvancedSearch' => false,

    // This is the property name that is used for the index primaryKey. In most cases the default "objectID" is sufficient.
    // https://www.meilisearch.com/docs/learn/getting_started/primary_key
    // If in the event you want to change the primary key name (you can set it to "entry_id" if you want), you can
    // set the config value to a callable function. By default, the values of this field will be "entry_123" or "file_456",
    // depending on if your indexing entries or files.
    // For example:
    /*
    'primaryKey' => function (string $indexName, array $values) {
        return ''; // do something fancy here
    },
    */
    'primaryKey' => 'objectID',

    // Highly recommended if you are using the ?ACT endpoint to perform searches, either via an Ajax
    // or server side curl request. You must pass a valid CSRF_TOKEN value along with the search request.
    // An example POST body:
    //    {
    //        "index": "test_files",
    //        "query": "small birds",
    //        "csrf_token": "[token string here]",
    //        "filters": {
    //            "limit": 6,
    //            "hybrid": {
    //                "embedder": "fullText"
    //            }
    //        }
    //    }
    'secureSearch' => true,

    // Only entries or files with the following statuses will be indexed
    'statuses' => [
        'enabled',
        'live',
    ],

    // This is a default list of stop words that will be removed from the __full_text property.
    // You can use the dexter_remove_stop_words extension hook to modify this list at runtime (ideal for multilingual sites),
    // or you can set your own list of stop words in the config.
    'stopWords' => [
        'a', 'about', 'above', 'after', 'again', 'against', 'all', 'am', 'an', 'and', 'any', 'are',
        'as', 'at', 'be', 'because', 'been', 'before', 'being', 'below', 'between', 'both', 'but',
        'by', 'could', 'did', 'do', 'does', 'doing', 'down', 'during', 'each', 'few', 'for', 'from',
        'further', 'had', 'has', 'have', 'having', 'he', 'her', 'here', 'hers', 'herself', 'him',
        'himself', 'his', 'how', 'i', 'if', 'in', 'into', 'is', 'it', 'its', 'itself', 'let\'s',
        'me', 'more', 'most', 'my', 'myself', 'nor', 'of', 'on', 'once', 'only', 'or', 'other',
        'ought', 'our', 'ours', 'ourselves', 'out', 'over', 'own', 'same', 'she', 'should', 'so',
        'some', 'such', 'than', 'that', 'the', 'their', 'theirs', 'them', 'themselves', 'then',
        'there', 'these', 'they', 'this', 'those', 'through', 'to', 'too', 'under', 'until', 'up',
        'very', 'was', 'we', 'were', 'what', 'when', 'where', 'which', 'while', 'who', 'whom',
        'why', 'will', 'with', 'would', 'you', 'your', 'yours', 'yourself', 'yourselves',
    ],

    // Highly recommended to use the queue if you need to batch re-index hundreds or thousands of entries.
    'useQueue' => true,
];

Last updated

Was this helpful?