Search index orphans
The Search index entries for deleted content task removes Core Search index entries whose content no longer exists. It uses core's own search.index service to clear them, not raw SQL, so the dataset rows and the word index are removed exactly the way core removes them itself.
When you need it
Core already clears a node's search entries when the node is deleted through the entity API: from the admin UI, with Drush's entity commands, or from code that calls $node->delete(). Entries are only left behind when content disappears some other way, for example:
- rows deleted directly with SQL,
- a migration rollback or import script that bypasses the entity API,
- a bulk delete that died part-way through,
- sites that deleted content before core handled this.
Those leftovers are never cleaned up by core. They keep taking space in search_index (one row per word per item, so it grows quickly) and search_dataset, and they count towards the index's word totals. On a real site with 1,662 nodes this task found 3,775 orphaned search items holding about 540,000 word-index rows.
Requirements
- The core Search module. The task is hidden entirely on sites without it.
- A usable backup location (see Backups below). Without one, the task reports unavailable and deletes nothing.
What it removes
For each configured search type, the task looks for items in search_dataset whose ID has no matching entity, checked against that entity type's own base table. For every such item it calls search.index->clear($type, $id), which removes the item's dataset rows and word-index rows in every language.
It never touches items whose content still exists, and it never removes anything by ID range or date.
Configuration
Turn the task on or off at Configuration » System » Cleanup (/admin/config/system/cleanup). It is on by default. The same page sets how often cron runs the sweep (default every 6 hours) and how many items a task may remove per run (default 1000).
Which search types are checked, and which entity type their IDs belong to, is set in configuration. The default covers core's node search:
search_index_types:
node_search: nodeIf a contributed search plugin indexes another entity type in Core Search, add it with Drush:
drush config:set drupal_cleanup.settings search_index_types.my_search_type my_entity_typeSearch types whose entity type doesn't exist, or isn't stored in SQL, are skipped. If none of the configured types is usable, the task reports unavailable: no configured search type maps to an existing SQL entity type.
Running it
Always look first:
drush cleanup:list
drush cleanup:run --task=search_index_orphans --dry-runThen run it, or leave it to cron:
drush cleanup:run --task=search_index_orphans
drush cleanup:run --task=search_index_orphans --limit=5000A large backlog is worked through over several runs, up to the per-run limit each time. Each run is logged as one summary line in the drupal_cleanup log channel.
Backups
Before anything is removed, the run saves every search_dataset and search_index row it is about to delete. The items are only cleared once that backup is safely written; if it can't be written, nothing is deleted. search_total is not backed up: it holds site-wide word totals that core recalculates on its own.
Backups are listed on the Backups tab of the settings page and with drush cleanup:backups. To undo a run:
drush cleanup:restore {backup-id} --dry-run
drush cleanup:restore {backup-id}Restoring puts the rows back and skips any that already exist, so running it twice is safe. After a restore, the orphans are back in the index exactly as they were.
Things to know
- It doesn't run when content is deleted. Core already clears the index on normal deletes, so this task only runs in the cron sweep and from Drush, as a safety net.
- It doesn't reindex anything. It only removes entries for content that is gone. Items marked for reindexing, and content that has never been indexed, are left to core's own indexing on cron.
- Search API is separate. This task only covers Core Search (
search_index/search_dataset). Search API indexes (Solr, OpenSearch, database backends) keep their own tracking and aren't touched.
Help improve this page
You can:
- Log in, click Edit, and edit this page
- Log in, click Discuss, update the Page status value, and suggest an improvement
- Log in and create a Documentation issue with your suggestion