Этап первый. Попробуем через SQL.
1. Сначала выберем список идентификаторов тэгов для поста по ID
SELECT t.term_id FROM `wp_terms` AS t JOIN wp_term_taxonomy AS tt ON tt.term_id = t.term_id JOIN wp_term_relationships AS tr ON tt.term_taxonomy_id = tr.term_taxonomy_id WHERE tr.object_id = 1346 AND tt.taxonomy = 'post_tag'
здесь 1346 — идентификатор поста, для которого ищем совпадения
2. Затем посчитаем количество пересечений тэгов со списком
SELECT count(*) FROM wp_term_relationships AS tr JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy = 'post_tag' AND tr.object_id = 1346 AND tt.term_id IN (154, 155, 156, 157, 158)
здесь явным способом задан список идентификаторов
3. Попробуем выбрать записи, у которых максимальное пересечение по тэгам
SELECT ID, post_title, ( SELECT count(*) FROM wp_term_relationships AS tr JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy = 'post_tag' AND tr.object_id = ID AND tt.term_id IN (154, 155, 156, 157, 158) ) AS rating FROM `wp_posts` WHERE post_type = 'post' AND ID <> 1346 AND post_status = 'publish' ORDER BY rating DESC LIMIT 5
естественно, отсекаем из выборки оригинальный пост.
Для результирущего запроса подставляем самый первый запрос на место списка идентификаторов в последнем. Получается весьма громоздко.
SELECT ID, post_title, ( SELECT count(*) FROM wp_term_relationships AS tr JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy = 'post_tag' AND tr.object_id = ID AND tt.term_id IN (SELECT t.term_id FROM `wp_terms` AS t JOIN wp_term_taxonomy AS tt ON tt.term_id = t.term_id JOIN wp_term_relationships AS tr ON tt.term_taxonomy_id = tr.term_taxonomy_id WHERE tr.object_id = 1346 AND tt.taxonomy = 'post_tag') ) AS rating FROM `wp_posts` WHERE post_type = 'post' AND ID <> 1346 AND post_status = 'publish' ORDER BY rating DESC LIMIT 5
Действительно, выдаёт список максимально пересекающихся по тэгам постов.
Единственная проблема — скорость работы. На базе из 2500 записей и примерно 3500 тэгов выполняется порядка двух секунд. Это уже неприемлимо, а предполагаются гораздо большие объёмы данных.
Как вариант, можно рассчитывать нужное значение фоном и кэшировать в отдельной таблице. Но при изменениях базы придётся всё пересчитывать, это тоже займёт массу времени.

Не так уж и часто ты изменяешь базу так, что надо таблицу похожести тэгов обновлять.
Как вариант можно при добавлении/изменении тэгов толкать фоновую задачу которая будет складывать всё во временную таблицу, потом грохать основную, и временную переименовывать в неё.
Таблица меняется несколько раз в день, добавляется порядка 5-10 записей.
Допустим, в базе порядка 50000 записей. Даже если скорость при этом не упадёт, что маловероятно, то эта фоновая задача будет работать больше суток.