5

我试图让我的用户按我拥有的不同自定义字段对搜索结果进行排序。

我正在使用pre_get_posts过滤器,除了一件事外一切正常。

我遇到的问题是,当使用自定义字段进行排序时,只有设置了该自定义字段的帖子才会显示在搜索中

显然这是不可接受的,因为当用户更改排序方式时搜索结果的数量会发生变化。

相反,我想要的是具有自定义字段的帖子首先按顺序显示,然后其余帖子按日期排序显示。

这是我拥有的相关代码:

<?php
add_filter('pre_get_posts', 'h5b_search_pre_get_posts');

function h5b_search_pre_get_posts ($qry) {
    $validOrders    = array('price', 'date', 'popularity');
    $orderBy        = (isset($_GET['myorder']) and in_array($_GET['myorder'], $validOrders)) ? $_GET['myorder'] : 'price';

    if ($qry->is_main_query() and $qry->query_vars['s'] != '') {
        # This only includes the posts that have "item_price" set
        if ($orderBy == 'price') {
            $qry->set('orderby', 'meta_value_num date');
            $qry->set('order', 'ASC DESC');
            $qry->set('meta_key', 'item_price');
        }
        # This works fine and includes all posts (obviously)
        elseif ($orderBy == 'date') {
            $qry->set('orderby', 'date');
            $qry->set('order', 'DESC');
        }
    }
}

编辑:这是实际的 MySQL 查询的样子。我该如何更改它,以便它wp_postmeta.meta_value是否存在排序 - 如果不存在,则按日期排序?

SELECT 
    SQL_CALC_FOUND_ROWS wp_posts.ID 
FROM 
    wp_posts 
INNER JOIN 
    wp_postmeta 
ON 
    (wp_posts.ID = wp_postmeta.post_id) 
WHERE 
    1=1 AND 
    ((
        (wp_posts.post_title LIKE '%lorem%') OR 
        (wp_posts.post_content LIKE '%lorem%')
    )) AND 
    wp_posts.post_type IN ('post', 'page', 'attachment', 'items', 'locations') AND 
    (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private') AND 
    (wp_postmeta.meta_key = 'item_price' ) 
GROUP BY 
    wp_posts.ID 
ORDER BY 
    wp_postmeta.meta_value+0,wp_posts.post_date DESC LIMIT 0, 6

最好我会使用这些WP_Query方法来解决它,但如果必须,我可能会考虑运行我自己的 SQL。

Edit2:我觉得我需要使用类似的东西IF NOT NULL- 你会怎么做WP_Query?甚至可能吗?

编辑(再次):这是同样的问题(我认为:P):https ://wordpress.stackexchange.com/questions/28409/way-to-include-posts-both-with-without-certain-meta-key-在-args-for-wp-查询

4

5 回答 5

2

它不会很漂亮(或优化),但我认为你可以在这里使用IFor CASE

ORDER BY 
CASE wp_postmeta.meta_value WHEN IS NOT NULL THEN wp_postmeta.meta_value END ASC,
CASE wp_postmeta.meta_value WHEN IS NULL THEN wp_posts.post_date END DESC

注意:我自己没有尝试过,所以可能存在语法错误,但理论上应该可以

延伸阅读: 你可以在 ORDER BY 中添加 if 语句吗?

于 2013-06-20T22:13:27.843 回答
2

这里的问题不是数据没有按日期列排序,问题是没有meta_value返回空的数据。

您看不到未指定元数据的条目的原因是,INNER JOIN对于那些情况,它可能会被过滤掉,其中wp_postmeta.post_id is null. 将 join 更改为LEFT OUTER JOIN.

SELECT 
    SQL_CALC_FOUND_ROWS wp_posts.ID 
FROM 
    wp_posts 


LEFT OUTER JOIN

    wp_postmeta 
ON 
    (wp_posts.ID = wp_postmeta.post_id) 
WHERE 
    1=1 AND 
    ((
        (wp_posts.post_title LIKE '%lorem%') OR 
        (wp_posts.post_content LIKE '%lorem%')
    )) AND 
    wp_posts.post_type IN ('post', 'page', 'attachment', 'items', 'locations') AND 
    (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private') AND 
    (wp_postmeta.meta_key = 'item_price'  OR wp_postmeta.meta_key is NULL ) 
GROUP BY 
    wp_posts.ID 
ORDER BY 
    wp_postmeta.meta_value+0,wp_posts.post_date DESC LIMIT 0, 6

更新

更改WP_Query使用$join属性中的连接类型以设置posts_join过滤器,如Plugin API/Filter Reference/posts join中所述

另请参阅WP_Query 类参考

于 2013-06-18T09:07:57.393 回答
1

我知道,这个帖子已经有 6 个月大了,但似乎从来没有足够的答案。我只是遇到了与问题的原始作者完全相同的问题。所以这是我发现可以正常工作并返回所需结果的最后一个 SQL 查询:

SELECT SQL_CALC_FOUND_ROWS * 
FROM wp_posts 
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = 'showPostInBanner' AND mt1.meta_value='1') 
LEFT JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) 
WHERE 1=1 AND ( wp_term_relationships.term_taxonomy_id IN (34,47,51,50,68,78,82,90,97,155,227,253,285,294,314,373,425,436,452,456,521,627,667,680,694,710,730,741,751) ) 
AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID 
ORDER BY mt1.meta_value+0 DESC, wp_posts.post_date DESC LIMIT 0, 4

基于此(我的查询试图获得稍微不同的结果),您的查询应该看起来像这样才能使其工作(如果我没有包含任何新错误 - 但我的查询现在肯定有效):

SELECT SQL_CALC_FOUND_ROWS * 
FROM wp_posts 
LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = 'showPostInBanner' AND mt1.meta_value='1') 
LEFT JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) 
WHERE 1=1 AND ((
        (wp_posts.post_title LIKE '%lorem%') OR 
        (wp_posts.post_content LIKE '%lorem%')
    )) AND 
AND wp_posts.post_type IN ('post', 'page', 'attachment', 'items', 'locations') AND 
    (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private')
GROUP BY wp_posts.ID 
ORDER BY mt1.meta_value+0 DESC, wp_posts.post_date DESC LIMIT 0, 6
于 2014-01-03T08:20:15.337 回答
-1

对于 Edit2:是的,可以使用 meta_query。尝试

'meta_query' => array(
    array(
                        'key' => 'meta_key',
                        'value' => 'some value', //try to put null here 
                        'compare' => '!='
                        )
    ),
于 2013-06-20T21:59:30.437 回答
-1

我想我有一个解决方案..

使用两个 meta_key,一个所有帖子都有(如“_thumbnail_id”),以及您希望用作过滤器的 meta_key .. 所以您的参数:

$qry->set( 
'meta_query',
 array(
 'relation' => 'OR',
 array( 'key' => 'item_price', 'value' => '', 'compare' => 'EXISTS' ),
 array( 'key' => '_thumbnail_id', 'value' => '', 'compare' => 'EXISTS' )
 ));

$qry->set('orderby', 'meta_value date'); 
$qry->set('order', 'ASC DESC'); 
$qry->set('meta_key', 'item_price');
于 2013-08-22T18:12:11.060 回答