如何为某些帖子创建有效的内容过滤器?

Posted

技术标签:

【中文标题】如何为某些帖子创建有效的内容过滤器?【英文标题】:How do I go about creating an efficient content filter for certain posts? 【发布时间】:2012-06-18 16:26:34 【问题描述】:

我已将此帖子标记为 WordPress,但我不完全确定它是 WordPress 特定的,因此我将其发布在 *** 而不是 WPSE 上。 解决方案不必是特定于 WordPress 的,只需 php 即可

情景 我运行 fishkeeping website 与许多热带鱼 Species ProfilesGlossary 条目。

我们的网站以我们的个人资料为导向。正如您所说,它们是网站的基础。

我希望实现的是,在每个提到另一个物种或词汇表条目的物种简介中,我可以用链接替换这些词 - 例如你会看到 here。理想情况下,我也希望这也出现在新闻、文章和博客文章中。

我们几乎有1400 species profiles1700 glossary entries。我们的物种概况通常很长,最后仅统计我们的物种概况numbered more than 1.7 million words 的信息。

我目前正在尝试什么 目前,我有一个filter.php,它的功能——我相信——可以满足我的需要。代码比较长,完整版可以找到here。

此外,在我的 WordPress 主题的 functions.php 中,我有以下内容:

# ==============================================================================================
# [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' ) );
    

正如(希望)代码的 cmets 所描述的那样,这种工作方式是 WordPress 每小时运行一个 cron 作业(这就像一个错误的 cron - 在用户点击时起作用,但这并不重要,因为时间不重要)运行上面找到的过滤器。

每小时运行一次的基本原理是,如果我们试图在保存每篇文章时运行它,那将损害作者的利益。一旦我们让客座作者参与进来,这显然不是一种可接受的方式。

问题... 几个月来,我一直在让这个过滤器可靠运行时遇到问题。我不认为问题出在过滤器本身,而在于启用过滤器的功能之一 - 即 cron 作业,或选择过滤哪些帖子的功能,或准备单词列表的功能等。过滤器。

不幸的是,诊断问题非常困难(我可以看到),这要归功于它在后台运行并且仅每小时运行一次。我一直在尝试使用 WordPress 的 update_option 函数(它基本上写入一个简单的数据库值)进行错误检查,但我运气不佳 - 老实说,我很困惑问题出在哪里。

我们最终在没有此过滤器正常工作的情况下启动了网站。有时它似乎有效,有时却无效。因此,我们现在有很多物种资料没有被正确过滤。

我想要什么... 我基本上是在寻求有关运行此过滤器的最佳方法的建议。

Cron Job 是答案吗?我可以设置一个每天运行的.php 文件,这不是问题。它如何确定需要过滤哪些帖子?它在运行时会对服务器产生什么影响?

另外,WordPress 管理页面是答案吗?如果我知道该怎么做,那么类似于页面的东西——利用 AJAX——允许我选择帖子来运行过滤器将是完美的。有一个叫AJAX Regenerate Thumbnails 的插件,它的工作原理是这样的,也许这会是最有效的?

注意事项

数据库/信息受到影响/读取/写入的大小 过滤了哪些帖子 过滤器对服务器的影响;特别是考虑到我似乎无法将 WordPress 内存限制增加到 32Mb 以上。 实际过滤器本身是否高效、有效和可靠?

这是一个相当复杂的问题,我不可避免地(因为我在此过程中被同事分心了大约 18 次)遗漏了一些细节。请随时向我询问更多信息。

提前致谢,

【问题讨论】:

您是否可以从异地访问您的 SQL 数据库?如果您担心服务器上运行的 cron,您可以执行初始脚本运行,从运行连接到数据库的 PHP CLI 脚本的计算机处理您的 150 万字。 稍微不相关:在浏览您的网站时,我注意到您的 url 结构被很好地重写了。但是,我不禁注意到分类链接都是/classification/%s 的形式,尽管是针对订单或家庭的。这是故意的吗? /classification/family/%s/classification/order/%s 似乎不会那么模棱两可了。 感谢 PhpMyCoder 的提醒,我会将其添加到我的(不断增长的!)列表中 :) 我目前不允许远程连接到我们的 mysql 数据库,但我可以暂时允许然后它从我的 wamp 安装中运行脚本。如果数据库在一段时间内不断写入,会对网站产生很大影响吗? 它可能会,尽管它的效果不如您在现场进行所有处理。您的服务器可能受到的唯一真正打击是数据库上的 UPDATE 查询。如果您在您的帖子表上运行 innodb(SHOW TABLE STATUS WHERE Name = 'table' 检查),则根本不会感觉到命中,因为它使用行级锁定。但是,如果您使用的是 MyISAM,请考虑在低流量时间运行 CLI 脚本。 我只给出了一个答案,您的代码粗略一瞥,所以如果这已经实施或建议,请原谅我,但是您是否使用停止列表来不检查数据库,例如“the "、"a" 等? 【参考方案1】:

在创建配置文件时执行。

尝试反转整个过程。与其检查内容中的单词,不如检查单词中的内容。

    将内容帖子拆分为单词(在空间上) 删除重复项、数据库中最小单词大小的单词、大于最大单词大小的单词以及您保留的“常用单词”列表中的单词。 检查每个表,如果您的某些表包含带空格的短语,请进行 %text% 搜索,否则进行直接匹配(快得多),如果问题确实很大,甚至构建哈希表。 (我会以 PHP 数组的形式执行此操作并以某种方式缓存结果,没有意义重新发明***) 使用现在大大缩小的列表创建链接。

即使您要检查的字数达到 100,000 个,您也应该能够轻松地将其控制在 1 秒以内。我之前已经为贝叶斯过滤器做了这个,没有缓存单词列表。

使用较小的列表,即使它很贪心并收集与“小丑”不匹配的单词也会抓住“小丑泥鳅”,生成的较小列表应该只有几到几十个带有链接的单词。这将不需要任何时间来查找和替换一大段文本。

以上内容并没有真正解决您对旧配置文件的担忧。您没有确切地说有多少,只是有很多文本,并且放在 1400 到 3100(两个项目)上。如果您有信息,您可以根据受欢迎程度来做这些较旧的内容。或在输入的日期,最新的在前。无论如何,最好的方法是编写一个脚本来暂停 PHP 的时间限制,并在所有帖子上批量运行加载/处理/保存。如果每个人需要大约 1 秒(可能要少得多,但最坏的情况),那么您说的是 3100 秒,也就是不到一个小时。

【讨论】:

很棒的帖子,+1。我今天早上一直在尝试实施您的解决方案,但遇到了一个我怀疑可能会减慢速度的问题。我将要搜索的一些术语包括 P. denisoniiS. daemon - 物种名称的缩写。因此," "explode 将不起作用。你能推荐一个替代品吗?我无法想象为所有搜索词搜索每个字段(即distributionhabitat)会很有效? 如果数据库中没有缩写形式,我不知道您是否能够检测到缩写版本。也就是说,如果在数据库中你有“小丑泥鳅”,但在文本中你有“C.泥鳅”,我看不出有任何方法可以有效地处理这个问题,除非你在数据库中有一个字段(对于物种)短名称版本。但如果你因为“C”而担心。似乎有问题,请记住您正在抛出一个长度阈值以下的单词,并且 LIKE 搜索中的“%Loach%”将匹配“Clown Loach”,它可能在短字段中有“C. Loach”,因此你会得到你的匹配。 嗯,想知道我是否必须回到绘图板上。鱼类的种类都采用以下两种格式之一:Satanoperca daemonS. daemon,如果我的解释正确的话,这将很难/不可能有效地运行您的想法? 不是真的@dunc,您需要做的就是在您的物种表中包含一个附加字段,其中包含较短的形式。 “daemon”,因为这在空间上被破坏了,所以将加载名称中带有 daemon 的所有鱼。然后,当您执行 str_replace() 时,对长名称和短名称都执行此操作。您希望查询至少返回所需的鱼,而不是整个鱼表。打破空间并执行 %daemon% LIKE 全文搜索将返回所需的项目。 啊,当然,我想我现在和你在一起了。将尝试它 - 再次感谢。

以上是关于如何为某些帖子创建有效的内容过滤器?的主要内容,如果未能解决你的问题,请参考以下文章

如何为某些 HTML 页面部分的屏幕阅读器设置优先级?

如何为某些帖子类型禁用古腾堡/块编辑器?

如何为网络爬虫设置有效的 cron 作业

如何为 QTableWidget 创建过滤器?

SharePoint 2010 - 如何为列表项打印InfoPath表单

如何为 Django 对象创建自定义过滤器标签?