Я отметил этот пост как WordPress, но я не совсем уверен, что он специфичен для WordPress, поэтому я отправляю его на StackOverflow, а не в WPSE. Решение не обязательно должно быть специфичным для WordPress, просто PHP .
Сценарий
Я управляю веб-сайтом по рыбному хозяйству с помощью ряда Species Profiles
и Glossary
тропической рыбы.
Наш сайт ориентирован на наши профили. Они, как вы можете это назвать, хлеб и масло сайта.
Я надеюсь, что в каждом видовом профиле, в котором упоминается другой вид или глоссарий, я могу заменить эти слова ссылкой – например, вы увидите здесь . В идеале, мне также хотелось бы, чтобы это происходило в новостях, статьях и сообщениях в блогах.
У нас есть почти 1400 species profiles
и 1700 glossary entries
. Наши профили видов часто длинные и, по последним подсчетам, только наши виды видов numbered more than 1.7 million words
информации.
Что я сейчас пытаюсь
В настоящее время у меня есть filter.php
с функцией, которая, я считаю, делает то, что мне нужно. Код довольно длинный, и его можно найти здесь полностью.
Кроме того, в моем functions.php
темы WordPress у меня есть следующее:
# ============================================================================================== # [Filter] # # Every hour, using WP_Cron, `my_updated_posts` is checked. If there are new Post IDs in there, # it will run a filter on all of the post's content. The filter will search for Glossary terms # and scientific species names. If found, it will replace those names with links including a # pop-up. include "filter.php"; # ============================================================================================== # When saving a post (new or edited), check to make sure it isn't a revision then add its ID # to `my_updated_posts`. add_action( 'save_post', 'my_set_content_filter' ); function my_set_content_filter( $post_id ) { if ( !wp_is_post_revision( $post_id ) ) { $post_type = get_post_type( $post_id ); if ( $post_type == "species" || ( $post_type == "post" && in_category( "articles", $post_id ) ) || ( $post_type == "post" && in_category( "blogs", $post_id ) ) ) { //get the previous value $ids = get_option( 'my_updated_posts' ); //add new value if necessary if( !in_array( $post_id, $ids ) ) { $ids[] = $post_id; update_option( 'my_updated_posts', $ids ); } } } } # ============================================================================================== # Add the filter to WP_Cron. add_action( 'my_filter_posts_content', 'my_filter_content' ); if( !wp_next_scheduled( 'my_filter_posts_content' ) ) { wp_schedule_event( time(), 'hourly', 'my_filter_posts_content' ); } # ============================================================================================== # Run the filter. function my_filter_content() { //check to see if posts need to be parsed if ( !get_option( 'my_updated_posts' ) ) return false; //parse posts $ids = get_option( 'my_updated_posts' ); update_option( 'error_check', $ids ); foreach( $ids as $v ) { if ( get_post_status( $v ) == 'publish' ) run_filter( $v ); update_option( 'error_check', "filter has run at least once" ); } //make sure no values have been added while loop was running $id_recheck = get_option( 'my_updated_posts' ); my_close_out_filter( $ids, $id_recheck ); //once all options, including any added during the running of what could be a long cronjob are done, remove the value and close out delete_option( 'my_updated_posts' ); update_option( 'error_check', 'working m8' ); return true; } # ============================================================================================== # A "difference" function to make sure no new posts have been added to `my_updated_posts` whilst # the potentially time-consuming filter was running. function my_close_out_filter( $beginning_array, $end_array ) { $diff = array_diff( $beginning_array, $end_array ); if( !empty ( $diff ) ) { foreach( $diff as $v ) { run_filter( $v ); } } my_close_out_filter( $end_array, get_option( 'my_updated_posts' ) ); }
То, как это работает, как (надеюсь), описывается комментариями кода, заключается в том, что каждый час WordPress управляет заданием cron (которое подобно ложному cron – работает с пользовательскими хитами, но это не имеет особого значения, поскольку время не является важный), который запускает фильтр, найденный выше.
Обоснование его работы на почасовой основе состояло в том, что если бы мы попытались запустить его, когда каждый пост был сохранен, это было бы в ущерб автору. Как только мы приглашаем приглашенных авторов, это, очевидно, не является приемлемым способом для этого.
Проблема…
В течение нескольких месяцев у меня возникали проблемы с надежной работой этого фильтра. Я не считаю, что проблема связана с самим фильтром, но с одной из функций, которые позволяют фильтровать, то есть задание cron, или функцию, которая выбирает, какие сообщения фильтруются, или функцию, которая подготавливает списки слов и т. Д. Для фильтр.
К сожалению, диагностировать проблему довольно сложно (что я вижу), благодаря чему она работает в фоновом режиме и только на почасовой основе. Я пытаюсь использовать функцию update_option
WordPress (которая в основном записывает простое значение базы данных) для проверки ошибок, но мне не повезло – и, честно говоря, я довольно смущен относительно того, где проблема ,
Мы закончили тем, что веб-сайт работал без фильтра. Иногда это работает, иногда это не так. В результате у нас теперь имеется довольно много профилей видов, которые неправильно фильтруются.
Что мне понравится …
Я в основном ищу совет по наилучшему способу запускать этот фильтр.
Ответ на вопрос Cron? Я могу настроить файл .php
который запускается каждый день, это не будет проблемой. Как определить, какие сообщения нужно отфильтровать? Какое влияние это окажет на сервер в момент его запуска?
В качестве альтернативы, это страница администратора WordPress? Если бы я знал, как это сделать, то что-то вроде строки страницы, использующей AJAX, которая позволяла мне выбирать столбы для запуска фильтра, была бы идеальной. Есть плагин под названием AJAX Regenerate Thumbnails
который работает так, может быть, это будет наиболее эффективным?
Соображения
Это довольно сложный вопрос, и я неизбежно (поскольку я был отвлечен примерно 18 раз коллегами в этом процессе) не учитывал некоторые детали. Пожалуйста, не стесняйтесь исследовать меня для получения дополнительной информации.
Заранее спасибо,
Сделайте это, когда профиль создан.
Попробуйте изменить весь процесс. Вместо проверки содержимого для слов проверьте слова для слов контента.
Вы должны быть в состоянии легко сохранить это менее 1 секунды, даже когда вы переходите на 100 000 слов, которые вы проверяете. Я сделал именно это, не кэшируя списки слов, для байесовского фильтра раньше.
С меньшим списком, даже если он жадный и собирает слова, которые не соответствуют «клоуну», он поймает «клоун-лоуч», в результате меньший список должен содержать от нескольких до нескольких десятков слов со ссылками. Который не займет времени вообще, чтобы найти и заменить над куском текста.
Вышеупомянутое не отражает вашу озабоченность более старыми профилями. Вы точно не знаете, сколько их есть, просто есть много текста и что он находится на 1400-331 (оба элемента). Это более старое содержимое, которое вы могли бы сделать, основываясь на популярности, если у вас есть информация. Или по дате введено, новее сначала. Независимо от того, лучший способ сделать это – написать скрипт, который приостанавливает ограничение времени на PHP и просто выполняет пакетный запуск загрузки / процесса / сохранения на всех постах. Если каждый занимает около 1 секунды (возможно, гораздо меньше, но хуже), вы говорите 3100 секунд, что составляет немногим меньше часа.