我看到了一些关于在wordpress搜索中包含元字段内容的问题。我的问题不是这些问题的重复。
背景我遇到的问题源于如何将元查询附加到搜索中。由于查询是附加到全局查询的,而不是单独查询metabox值,因此它由AND运算符连接。
For the security-conscious -- 请注意,在每个“LIKE‘user input’”语句中,用户的查询周围都有一个相同的nonce,但为了便于阅读,这些nonce已被删除。
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts
LEFT JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = \'stc_make\' )
WHERE 1=1
AND
(
(
( wp_posts.post_title LIKE \'ADS-B\' ) OR
( wp_posts.post_excerpt LIKE \'ADS-B\' ) OR
( wp_posts.post_content LIKE \'ADS-B\' )
)
)
AND
(
( wp_postmeta.meta_key = \'stc_make\' AND wp_postmeta.meta_value LIKE \'ADS-B\' ) OR
( wp_postmeta.meta_key = \'stc_models\' AND wp_postmeta.meta_value LIKE \'ADS-B\' ) OR
( wp_postmeta.meta_key = \'stc_desc\' AND wp_postmeta.meta_value LIKE \'ADS-B\' )
)
AND wp_posts.post_type IN (
\'post\',
\'page\',
\'attachment\',
\'employees\', /* custom post type */
\'certificates\' /* custom post type */
)
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 wp_posts.post_title LIKE \'ADS-B\' DESC, wp_posts.post_date DESC LIMIT 0, 10
这有效地防止了搜索包含来自自定义帖子类型的帖子,即使查询与元框中的内容匹配也是如此。
我解决了这个问题a solution from Naji Amer on stackexchange:
function filter_meta_query($sql){
if (!(is_admin()) && (is_search())) {
$pos = strpos($sql[\'where\'], \'AND\');
if ($pos !== false) {
$sql[\'where\'] = substr_replace($sql[\'where\'], \'OR\', $pos, strlen(\'AND\'));
}
}
return $sql;
}
add_filter(\'get_meta_sql\', \'filter_meta_query\', 10, 1);
(添加条件(!(is\\u admin()),否则将阻止您更改管理菜单中的菜单项…)
代码将“AND”(在元数据库内容的条件之前)更改为OR。This works, but only if there is a query \'s\'. 当没有查询时,定义标准wordpress查询的第一个AND语句:
WHERE 1=1
AND
(
(
( wp_posts.post_title LIKE \'ADS-B\') OR
( wp_posts.post_excerpt LIKE \'ADS-B\' ) OR
( wp_posts.post_content LIKE \'ADS-B\' )
)
)
已删除。这给我们带来了一个问题,因为剩下的查询
其中1=1或。。。(条件)
换句话说,其中true=trueOR 其他条件返回true。查询将返回wp\\U posts表中的所有post类型。
我做了一些挖掘,在网上发现了以下内容2726 和2809 属于class-wp-query.php, 分别为:
$this->request = $old_request = "SELECT $found_rows $distinct $fields FROM {$wpdb->posts} $join WHERE 1=1 $where $groupby $orderby $limits";
(第2726行)
$this->request = "SELECT $found_rows $distinct {$wpdb->posts}.ID FROM {$wpdb->posts} $join WHERE 1=1 $where $groupby $orderby $limits";
(第2809行)
据我所知,其中1=1是硬编码的。
问题是,我当前的wordpress项目包括两种自定义帖子类型,可以公开搜索(“员工”和“证书”)。我想使用插件来允许通过他们在global $wp\\U查询。
为此,我为使用该标准的每个类添加了一个函数
set_query_var(\'meta_query\', array(...))
定义应搜索的元数据库。
为什么我不只是使用自定义的书面查询,我想保留随时添加或删除插件/帖子类型的能力,这意味着不需要使用自定义SQL字符串(除非我在theme\'s functions.php中编写自己的函数来生成它)。
但是,由于“where 1=1”子句,使用默认查询生成器会给我带来问题。
到目前为止,我一直在关注的是,我还没有找到一个钩子,以便在发送最终查询之前进行修改(我更改“and”to“OR”的函数是修改元查询对象,而不是查询字符串)。
我还没有找到一种将元查询参数与默认wordpress搜索参数分组的方法,看起来是这样的:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts
LEFT JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = \'stc_make\' )
WHERE 1=1
AND
(
(
( wp_posts.post_title LIKE \'ADS-B\' ) OR
( wp_posts.post_excerpt LIKE \'ADS-B\' ) OR
( wp_posts.post_content LIKE \'ADS-B\' )
) OR (
( wp_postmeta.meta_key = \'stc_make\' AND wp_postmeta.meta_value LIKE \'ADS-B\' ) OR
( wp_postmeta.meta_key = \'stc_models\' AND wp_postmeta.meta_value LIKE \'ADS-B\' ) OR
( wp_postmeta.meta_key = \'stc_desc\' AND wp_postmeta.meta_value LIKE \'ADS-B\' )
)
)
...
我原以为我可以通过检测$wp\\u query->s何时不为空来解决这个问题,但看起来即使是已定义的查询,当它们与搜索字符串匹配时,也会返回不需要的帖子类型(例如,搜索中显示的导航菜单项,只要它们的标题与搜索匹配,即使它们不符合
IN ([... post types])
查询中的参数)。
问题:Am I going about this all wrong? 是否有更简单的方法以我想要的格式进行查询,还是应该生成多个查询并合并结果?(对于后者,我如何将结果附加到默认搜索,而不要求主题了解插件的一些信息?)
如果我能进一步澄清,请告诉我。
最合适的回答,由SO网友:amacgill 整理而成
我已经找到了解决办法。
首先,为了修复空搜索的错误结果,我需要
(!empty(get\\u query\\u var(\'s\'))
在我的插件中设置“meta\\u query”查询变量之前。
我以为解决方法是posts_request 过滤器,但仍在更换
select SQL_CALC_GET_ROWS wp_posts.ID
使用
select SQL_CALC_GET_ROWS wp_posts.*
即使我在过滤器中将*替换为“ID”,它也不会返回正确的结果。我有一种感觉,如果我进一步查看代码,我可以找到原因,但我已经到了最后期限,所以我没有费心。
相反,我意识到正确的过滤器是posts_request_ids, 在wp类查询的第2819行调用。在发送查询之前,Wordpress 4.9.8中的php。有了这个更改,代码就可以正常工作了。
自定义筛选器如下所示:
/**
* FIX QUERIES
*
* Adds metadata "where" conditions to the "where" conditions of the main query,
* by shifting a few parentheses.
*
* @param string $request The SQL query as a string
* @param object $query_instance The user\'s search as string
* @return string $request
* @author Andrew MacGillivray
*/
function fixQuery($request, $query_instance) {
if (!empty(get_query_var(\'s\'))) {
// remove line breaks so stripos and substr_replace will work
$request = trim(preg_replace(\'/\\s+/\', \' \', $request));
// If you\'re copying this, remember to replace "wp_" with your table prefix.
// Or rewrite to use $wpdb...
$replace = array(
"))) OR ( ( wp_postmeta.meta_key" => array(
"))) OR ( (" => ") ) OR ( ( ",
),
") ) AND wp_posts.post_type IN (" => ") ) ) AND wp_posts.post_type IN (",
);
foreach ($replace as $key => $value) {
$pos = stripos($request, $key);
if (is_array($value)) {
foreach ($value as $orig => $replace) {
$request = substr_replace($request, $replace, $pos, strlen($orig));
}
} else {
$request = substr_replace($request, $value, $pos, strlen($key));
}
}
}
return $request;
}
add_filter(\'posts_request_ids\', \'fixQuery\', 10, 2);
在该代码中,我们在与主查询相同的“AND”子句中包含了“OR…metadata LIKE\'\'%user query%\')”。上述筛选器查找由以下筛选器添加的“OR”子句:
/**
* In Global WP_Query, replace first "And" with "OR" to allow meta-queries (when present)
* to function properly.
*
* @see https://stackoverflow.com/questions/43734845/how-to-give-or-relation-in-tax-query-and-meta-query-wordpress
* @author StackOverflow: Naji Amer, 2018-02-09
*/
function filter_meta_query($sql){
if (!(is_admin()) && (is_search()) && !empty(get_query_var(\'s\'))) {
$pos = strpos($sql[\'where\'], \'AND\');
if ($pos !== false) {
$sql[\'where\'] = substr_replace($sql[\'where\'], \'OR\', $pos, strlen(\'AND\'));
}
}
return $sql;
}
add_filter(\'get_meta_sql\', \'filter_meta_query\', 10, 1);
添加这两个过滤器(并修改我的插件,以便只在搜索查询不为空时添加元查询)可以使一切都按预期进行。
希望这可以帮助其他人。