我正在尝试允许我的用户按照我的不同自定义字段对搜索结果进行排序。
我正在使用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-in-args-for-wp-query
答案 0 :(得分:2)
这里的问题不是数据没有按日期列排序,问题是没有返回空meta_value
的数据。
您没有看到未指定元数据的条目的原因是INNER JOIN
可能会针对wp_postmeta.post_id is null
的情况过滤掉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
属性设置{{1}}过滤器,如Plugin API/Filter Reference/posts join
答案 1 :(得分:2)
它不会漂亮(或优化),但我认为你可以在这里使用IF
或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
注意:我自己没有尝试过,所以可能存在语法错误,但理论上应该有效
答案 2 :(得分:0)
我想我有一个解决方案..
使用两个meta_key,一个所有帖子都有(如“_thumbnail_id”), 和你希望用作过滤器的meta_key .. 所以你的args:
$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');
答案 3 :(得分:0)
我知道,这个帖子已经有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
答案 4 :(得分:-1)
对于Edit2: 是的,可以使用meta_query。试试
'meta_query' => array(
array(
'key' => 'meta_key',
'value' => 'some value', //try to put null here
'compare' => '!='
)
),