按分类术语对搜索结果进行排序

时间:2017-04-10 作者:Lucio Crusca

我正在编写一个插件,使搜索只在特定的自定义帖子类型上工作,并根据用户在其后端配置中选择的分类法对结果进行排序。

管理员最多可以选择4个自定义帖子类型的分类法:每个分类法都是必须应用于搜索结果的排序标准。然后,管理员可以选择每个分类法的术语,使文章在搜索结果中出现在其他分类法之前。第一个选定的分类法将是最重要的分类标准;其他将按顺序应用。

我需要一个大致如下的查询:

SELECT * FROM wp_posts ORDER BY choosen_terms_of_taxonomy1, choosen_terms_of_axonomy2, ...
现在让我们使用一些示例数据。我们有course 岗位类型:

+----+------------+
| ID | post_title |
+----+------------+
| 12 | Cooking    |
+----+------------+
| 13 | Surfing    |
+----+------------+
| 14 | Building   |
+----+------------+
| 15 | Hacking    |
+----+------------+
然后,我们有两种用于此自定义帖子类型的分类法。一个是place 另一个是pricetag. 这个place 分类法有以下术语:

+---------+------------+
| term_id |    slug    |
+---------+------------+
|      21 |  downtown  |
+---------+------------+
|      22 |  abroad    |
+---------+------------+
Thepricetag 分类法有以下术语:

+---------+------------+
| term_id |    slug    |
+---------+------------+
|      31 |  expensive |
+---------+------------+
|      32 |  cheap     |
+---------+------------+
最后我们有wp_term_relationships 以这种方式将课程与分类法术语联系起来:

+-----------+------------+---------+
| object_id | post_title | term_id |
+-----------+------------+---------+
|        12 | Cooking    |      21 | (downtown)
+-----------+------------+---------+
|        12 | Cooking    |      32 | (cheap)
+-----------+------------+---------+
|        13 | Surfing    |      22 |    (abroad)
+-----------+------------+---------+
|        13 | Surfing    |      31 |    (expensive)
+-----------+------------+---------+
|        14 | Building   |      21 |       (downtown)
+-----------+------------+---------+
|        14 | Building   |      31 |       (expensive)
+-----------+------------+---------+
|        15 | Hacking    |      22 |          (abroad)
+-----------+------------+---------+
|        15 | Hacking    |      32 |          (cheap)
+-----------+------------+---------+
(请注意:我知道这不是wp_term_relationships 表,但为了这个问题,我想简化一下。)

假设管理员选择了place 作为第一个用作分类标准的分类法,他选择展示downtown 课程优先(插件的管理屏幕已经完成,它已经为管理员提供了做出此类选择的UI)。

然后说管理员已经选择了pricetag 作为第二种分类法,用作排序标准expensive 首先显示的课程。请注意,作为第二个标准,它的排序优先级低于第一个标准,因此管理员希望首先选择市中心的课程,然后在市中心的课程组中,首先选择昂贵的课程。

现在,前端用户在网站上搜索所有课程,他应该按照以下确切顺序查看这些结果:

建筑课程(因为它在市中心而且很贵)

  • 烹饪课程(因为它在市中心而且便宜)
  • 冲浪课程(因为它在国外而且很贵)
  • 黑客课程(因为它在国外而且便宜)
    1. 我的问题是写正确的JOINORDER BY Wordpress查询对象中的子句。我已经上瘾了posts_clauses 下面是我生成的SQL查询:

      SELECT SQL_CALC_FOUND_ROWS  wp_posts.* FROM wp_posts 
        LEFT JOIN (wp_term_relationships, wp_term_taxonomy, wp_terms) 
          ON (wp_term_relationships.object_id = wp_posts.ID 
              AND wp_term_taxonomy.term_taxonomy_id = wp_term_relationships.term_taxonomy_id
              AND wp_terms.term_id = wp_term_taxonomy.term_id) 
        WHERE 1=1 AND (((wp_posts.post_title LIKE \'%%\') OR (wp_posts.post_excerpt LIKE \'%%\') OR (wp_posts.post_content LIKE \'%%\')))
                  AND wp_posts.post_type IN 
                      (\'post\', \'page\', \'attachment\', \'course\') 
                  AND (wp_posts.post_status = \'publish\' OR wp_posts.post_author = 1 AND wp_posts.post_status = \'private\') 
                  AND wp_posts.post_type=\'course\' 
        ORDER BY (wp_terms.slug LIKE \'downtown\' AND wp_term_taxonomy.taxonomy LIKE \'place\') DESC,
                 (wp_terms.slug LIKE \'abroad\' AND wp_term_taxonomy.taxonomy LIKE \'place\') DESC,
                 (wp_terms.slug LIKE \'expensive\' AND wp_term_taxonomy.taxonomy LIKE \'pricetag\') DESC,
                 (wp_terms.slug LIKE \'cheap\' AND wp_term_taxonomy.taxonomy LIKE \'pricetag\') DESC,
                 wp_posts.post_title LIKE \'%%\' DESC, wp_posts.post_date DESC
       LIMIT 0, 300
      
      但是,此查询至少有两个问题:

      它返回两倍的结果,我不明白为什么,因为左连接不应该在指定的表之间产生笛卡尔积,所以我不清楚结果的排序顺序(似乎只是post_date DESC), 但很明显,这不是我所期望的

    我尝试通过删除Wordpress生成的子句来简化查询:

    SELECT wp_posts.* FROM wp_posts 
      LEFT JOIN (wp_term_relationships, wp_term_taxonomy, wp_terms) 
         ON (wp_term_relationships.object_id = wp_posts.ID 
            AND wp_term_taxonomy.term_taxonomy_id = wp_term_relationships.term_taxonomy_id
            AND wp_terms.term_id = wp_term_taxonomy.term_id) 
      WHERE 1=1 AND wp_posts.post_type=\'course\' 
      ORDER BY (wp_terms.slug LIKE \'downtown\' AND wp_term_taxonomy.taxonomy LIKE \'place\') DESC,
           (wp_terms.slug LIKE \'abroad\' AND wp_term_taxonomy.taxonomy LIKE \'place\') DESC,
           (wp_terms.slug LIKE \'expensive\' AND wp_term_taxonomy.taxonomy LIKE \'pricetag\') DESC,
           (wp_terms.slug LIKE \'cheap\' AND wp_term_taxonomy.taxonomy LIKE \'pricetag\') DESC
    
    这一个有完全相同的问题,但它更容易理解,并且它返回的数据好像没有ORDER BY 根本就在那里。

    你能帮帮我吗?

    2 个回复
    最合适的回答,由SO网友:J.D. 整理而成

    不幸的是,尽管WP_Query 支持\'tax_query\' arg,它不支持基于post术语的订购。因此,您需要像现在这样修改查询SQL。但是,您正在构建ORDER BY 子句不正确,这就是为什么它是按post_date. 您需要做的是使用CASE 声明如下:

    CASE 
        WHEN (wp_terms.slug LIKE \'downtown\' AND wp_term_taxonomy.taxonomy LIKE \'place\') THEN 1
        WHEN (wp_terms.slug LIKE \'abroad\' AND wp_term_taxonomy.taxonomy LIKE \'place\') THEN 0
    END
    
    这将根据您分配给每个条款的优先级排序(1, 0, 等等,优先级越高,除非您使用ASC 而不是DESC 用于订购)。

    因为要独立地对这两个分类法排序,所以需要有两个连接和两个case语句。(例如,请参见下文。)

    您还需要引起GROUP BY 在post ID上,为避免重复结果:

        $clauses[\'groupby\'] = \'wptests_posts.ID\';
    
    因此,您的最终查询结果如下所示(格式为便于阅读):

       SELECT SQL_CALC_FOUND_ROWS  wptests_posts.ID FROM wptests_posts 
                LEFT JOIN (
                    wptests_term_relationships tr_place,
                    wptests_term_taxonomy tt_place,
                    wptests_terms t_place
                ) ON (
                    tr_place.object_id = wptests_posts.ID 
                    AND tt_place.term_taxonomy_id = tr_place.term_taxonomy_id
                    AND tt_place.taxonomy = \'place\'
                    AND t_place.term_id = tt_place.term_id
                ) 
    
                LEFT JOIN (
                    wptests_term_relationships tr_pricetag,
                    wptests_term_taxonomy tt_pricetag,
                    wptests_terms t_pricetag
                ) ON (
                    tr_pricetag.object_id = wptests_posts.ID 
                    AND tt_pricetag.term_taxonomy_id = tr_pricetag.term_taxonomy_id
                    AND tt_pricetag.taxonomy = \'pricetag\'
                    AND t_pricetag.term_id = tt_pricetag.term_id
                ) 
       WHERE 1=1  AND wptests_posts.post_type = \'course\' AND (wptests_posts.post_status = \'publish\')
       GROUP BY wptests_posts.ID
       ORDER BY 
            (CASE 
                WHEN (t_place.slug LIKE \'downtown\') THEN 1
                WHEN (t_place.slug LIKE \'abroad\') THEN 0
            END) DESC, (CASE
                WHEN (t_pricetag.slug LIKE \'expensive\') THEN 1
                WHEN (t_pricetag.slug LIKE \'cheap\') THEN 0
            END) DESC,
            wptests_posts.post_date DESC
       LIMIT 0, 10
    
    下面是一个PHPUnit测试示例,演示了这一点,包括用于生成联接和ORDERBY的示例代码(用于生成上述查询):

    class My_Test extends WP_UnitTestCase {
    
        public function test() {
    
            // Create the post type.
            register_post_type( \'course\' );
    
            // Create the posts.
            $cooking_post_id = $this->factory->post->create(
                array( \'post_title\' => \'Cooking\', \'post_type\' => \'course\' )
            );
            $surfing_post_id = $this->factory->post->create(
                array( \'post_title\' => \'Surfing\', \'post_type\' => \'course\' )
            );
            $building_post_id = $this->factory->post->create(
                array( \'post_title\' => \'Building\', \'post_type\' => \'course\' )
            );
            $hacking_post_id = $this->factory->post->create(
                array( \'post_title\' => \'Hacking\', \'post_type\' => \'course\' )
            );
    
            // Create the taxonomies.
            register_taxonomy( \'place\', \'course\' );
            register_taxonomy( \'pricetag\', \'course\' );
    
            // Create the terms.
            $downtown_term_id = wp_create_term( \'downtown\', \'place\' );
            $abroad_term_id = wp_create_term( \'abroad\', \'place\' );
    
            $expensive_term_id = wp_create_term( \'expensive\', \'pricetag\' );
            $cheap_term_id = wp_create_term( \'cheap\', \'pricetag\' );
    
            // Give the terms to the correct posts.
            wp_add_object_terms( $cooking_post_id, $downtown_term_id, \'place\' );
            wp_add_object_terms( $cooking_post_id, $cheap_term_id, \'pricetag\' );
    
            wp_add_object_terms( $surfing_post_id, $abroad_term_id, \'place\' );
            wp_add_object_terms( $surfing_post_id, $expensive_term_id, \'pricetag\' );
    
            wp_add_object_terms( $building_post_id, $downtown_term_id, \'place\' );
            wp_add_object_terms( $building_post_id, $expensive_term_id, \'pricetag\' );
    
            wp_add_object_terms( $hacking_post_id, $abroad_term_id, \'place\' );
            wp_add_object_terms( $hacking_post_id, $cheap_term_id, \'pricetag\' );
    
            $query = new WP_Query(
                array(
                    \'fields\'    => \'ids\',
                    \'post_type\' => \'course\',
                )
            );
    
            add_filter( \'posts_clauses\', array( $this, \'filter_post_clauses\' ) );
    
            $results = $query->get_posts();
    
            $this->assertSame(
                array(
                    $building_post_id,
                    $cooking_post_id,
                    $surfing_post_id,
                    $hacking_post_id,
                )
                , $results
            );
        }
    
        public function filter_post_clauses( $clauses ) {
    
            global $wpdb;
    
            $clauses[\'orderby\'] = "
                (CASE 
                    WHEN (t_place.slug LIKE \'downtown\') THEN 1
                    WHEN (t_place.slug LIKE \'abroad\') THEN 0
                END) DESC, (CASE
                    WHEN (t_pricetag.slug LIKE \'expensive\') THEN 1
                    WHEN (t_pricetag.slug LIKE \'cheap\') THEN 0
                END) DESC,
                " . $clauses[\'orderby\'];
    
            foreach ( array( \'place\', \'pricetag\' ) as $taxonomy ) {
    
                // Instead of interpolating directly here, you should use $wpdb->prepare() for $taxonomy.
                $clauses[\'join\'] .= "
                    LEFT JOIN (
                        $wpdb->term_relationships tr_$taxonomy,
                        $wpdb->term_taxonomy tt_$taxonomy,
                        $wpdb->terms t_$taxonomy
                    ) ON (
                        tr_$taxonomy.object_id = $wpdb->posts.ID 
                        AND tt_$taxonomy.term_taxonomy_id = tr_$taxonomy.term_taxonomy_id
                        AND tt_$taxonomy.taxonomy = \'$taxonomy\'
                        AND t_$taxonomy.term_id = tt_$taxonomy.term_id
                    ) 
                    ";
            }
    
            $clauses[\'groupby\'] = \'wptests_posts.ID\';
    
            return $clauses;
        }
    }
    

    SO网友:Abhishek Pandey

    您可以使用tax_query 用于分类,即。

      $args = array(
                posts_per_page => -1,
                post_type => \'your post type\',
                tax_query\' => array(
                \'relation\' => \'OR\',
                 array(
                      \'taxonomy\' => \'your custom taxonomy\',
                      \'field\'    => \'slug\',
                      \'terms\'    => $_REQUEST[your post requested],
                     ),
                  array(
                        \'taxonomy\' => \'your 2nd custom taxonomy\',
                        \'field\'    => \'slug\',
                        \'terms\'    => $_REQUEST[your post requested],
                    ),
                ),
            ):
    
           $query = new WP_Query( $args );
    

    相关推荐

    高级WP查询占用了SQL服务器

    我最近为一位房地产经纪人的客户开发了一个网站。项目中包含一个名为listing. 此帖子类型用于他们的所有列表,其他信息存储在postmeta 表通过使用Advanced Custom Fields Pro.在网站的前端,我开发了一个过滤栏,用户可以通过几个过滤器进行选择并提交表单,然后生成一个复杂的WP\\u查询以返回匹配的列表。很简单!虽然大多数搜索都工作得很好,但一些更复杂的搜索将生成占用线程的SQL查询。当数据库需要刷新堆积的所有内容时,服务器基本上会冻结,直到有人终止进程。这在所有环境中都会发生