Description
We have a multisite and noticed that an excessive amount of UpdateSearchIndex (ie. "Updating search index") jobs are being queued.
After some investigation I've realised that it's triggering these jobs regardless of if any searchable fields or attributes have been updated.
Please let me know if I've misunderstood anything here
Steps to reproduce
- Save an existing entry without changing any fields or attributes, and it queues a new UpdateSearchIndex job even though no searchable fields have been updated
- Save an existing entry and only change non-searchable fields, and it queues a new UpdateSearchIndex job even though no searchable fields have been updated
- Save an existing entry and only change a translatable field (searchable or otherwise), and it propagates to other sites and queues a new UpdateSearchIndex for job even though no searchable fields have been updated for those sites
Expected behavior
Only queue an UpdateSearchIndex job if the element has updated searchable fields or searchable attributes
Actual behavior
Unnecessary UpdateSearchIndex jobs are queued and clogs up the queue
Suggested solution
Perhaps add an additional check here by filtering $element->getDirtyFields() and $element->getDirtyAttributes() to $searchableDirtyFields and $searchableDirtyAttributes, and skip the indexing if they're both empty. You'd then pass $searchableDirtyFields to new UpdateSearchIndex() instead of $element->getDirtyFields(). This doesn't account for the multisite aspect though in step 3.
Workaround (WIP)
I started a solution for this using EVENT_BEFORE_UPDATE_SEARCH_INDEX but hit a wall
use Craft;
use craft\events\ElementEvent;
use craft\helpers\ElementHelper;
use craft\services\Elements;
use yii\base\Event;
// Skip indexing if none of the fields or attributes are searchable
Event::on(
Elements::class,
Elements::EVENT_BEFORE_UPDATE_SEARCH_INDEX,
function (ElementEvent $event) {
$el = $event->element;
$root = ElementHelper::rootElement($el);
$fieldLayout = $el->getFieldLayout();
if (!$fieldLayout) {
return;
}
// Check for searchable dirty fields
$dirtyFields = $el->getDirtyFields();
$searchableDirtyFields = array_filter(
$dirtyFields,
function (string $fieldHandle) use ($fieldLayout): bool {
$field = $fieldLayout->getFieldByHandle($fieldHandle);
return !$field || $field->searchable;
},
);
if (count($searchableDirtyFields)) {
return; // Allow the job to be queued even though some of the fields might not be searchable
}
// Check for searchable dirty attributes (eg. title, slug)
$dirtyAttributes = $root->getDirtyAttributes();
// Get the searchable attributes using the same logic as \craft\services\search::indexElementAttributes()
$searchableAttributes = array_flip($root::searchableAttributes());
$searchableAttributes['slug'] = true;
if ($root::hasTitles()) {
$searchableAttributes['title'] = true;
}
$searchableAttributes = array_keys($searchableAttributes);
$searchableDirtyAttributes = array_intersect($dirtyAttributes, $searchableAttributes);
if (count($searchableDirtyAttributes)) {
return; // Allow the job to be queued even though some of the attributes might not be searchable
}
// Skip indexing if none of the fields or attributes are searchable
Craft::warning('Skipping indexing of non-searchable fields or attributes', __METHOD__);
$event->isValid = false;
}
);
This mostly does the job but it doesn't work as expected when changing an attribute like the title or slug, in this case $dirtyAttributes is still empty and the job is incorrectly prevented.
This doesn't address unnecessary propagation to other sites but it does prevent those site entries from being queued, as they have no dirty searchable fields / attributes.
There might be other things I haven't considered too, any suggestions are welcome
Craft CMS version
4.5.10
PHP version
8.1
Operating system and version
No response
Database type and version
No response
Image driver and version
No response
Installed plugins and versions
Description
We have a multisite and noticed that an excessive amount of UpdateSearchIndex (ie. "Updating search index") jobs are being queued.
After some investigation I've realised that it's triggering these jobs regardless of if any searchable fields or attributes have been updated.
Please let me know if I've misunderstood anything here
Steps to reproduce
Expected behavior
Only queue an UpdateSearchIndex job if the element has updated searchable fields or searchable attributes
Actual behavior
Unnecessary UpdateSearchIndex jobs are queued and clogs up the queue
Suggested solution
Perhaps add an additional check here by filtering
$element->getDirtyFields()and$element->getDirtyAttributes()to$searchableDirtyFieldsand$searchableDirtyAttributes, and skip the indexing if they're both empty. You'd then pass$searchableDirtyFieldstonew UpdateSearchIndex()instead of$element->getDirtyFields(). This doesn't account for the multisite aspect though in step 3.Workaround (WIP)
I started a solution for this using
EVENT_BEFORE_UPDATE_SEARCH_INDEXbut hit a wallThis mostly does the job but it doesn't work as expected when changing an attribute like the title or slug, in this case
$dirtyAttributesis still empty and the job is incorrectly prevented.This doesn't address unnecessary propagation to other sites but it does prevent those site entries from being queued, as they have no dirty searchable fields / attributes.
There might be other things I haven't considered too, any suggestions are welcome
Craft CMS version
4.5.10
PHP version
8.1
Operating system and version
No response
Database type and version
No response
Image driver and version
No response
Installed plugins and versions