meta_query 搜索具有中间名首字母的名称 - PHP,WordPress
meta_query search names when they have middle initials - PHP, WordPress
我必须构建一个搜索表单,在其中按全名(名字 + 中间名首字母 + 姓氏)搜索公司成员。所有名字都采用这种形式:John B. Doe.
以下代码适用于这些情况:John, John B., Doe, B., John B. Doe,但查询搜索不起作用:John Doe
.
if (isset($_POST['search']) && $_POST['search'] != -1) {
$args['meta_query']['name_search']['key'] = "_full_name";
$args['meta_query']['name_search']['compare'] = "LIKE";
$args['meta_query']['name_search']['value'] = $_POST['search'];
}
我应该如何改进查询才能同时使用:名字和姓氏 (John Doe) 以及名字、姓氏加上中间名首字母 (John B. Doe)?
我可以建议以下解决方案:
$search = $_POST['search'];
$words = explode( $search, ' ' );
$words = array_filter(
$words,
function ( $word ) {
return ! empty( trim( $word ) );
}
);
foreach ( $words as $index => $word ) {
$query_name = "name_search_$index";
$args['meta_query'][ $query_name ] = [
'key' => '_full_name',
'value' => $word,
'compare' => 'LIKE',
];
}
代码解释:
- 我将搜索短语拆分为单词。单词是任何符号序列
空格之间
- 我删除了空格。如果您的搜索字符串是
John Doe
或 John Doe
或 John Doe
搜索结果应该相同。
- 我构建了多个查询。每个查询对应一个单词。如果您搜索 John Doe,查询将由 2 个查询构建。查询次数和字数一样多。
此解决方案存在的问题:
- 太多的词就是太多的查询。包含 LIKE 的查询太多太慢。
- 搜索
John Doe
和 Doe John
会得到相同的结果。单词的顺序对结果没有影响。
我建议仅将此解决方案用作临时解决方案。
好的解决方案 - 使用全文搜索插件或编写全文搜索解决方案
你自己。您可以在 MySQL 官方页面上找到有关全文搜索的更多详细信息:
https://dev.mysql.com/doc/refman/8.0/en/fulltext-search.html
首先,如果只有 WordPress 不转义 value
中的 %
字符(即搜索关键字),您可以简单地将搜索关键字中的空格替换为 %
,因此你会得到一个像 meta_value LIKE '%John%Doe%'
这样的子句,它会匹配 John B. Doe
.
所以因为搜索关键字中的 %
被转义(这是一件好事,顺便说一句),那么你可以改用 REGEXP
(正则表达式搜索)然后替换空格.*
相当于 LIKE
子句中的 %
。
工作示例
在您的代码中替换为:
$args['meta_query']['name_search']['compare'] = "LIKE";
$args['meta_query']['name_search']['value'] = $_POST['search'];
有了这个:
// Build the regular expression pattern.
$list = preg_split( '/ +/', trim( $_POST['search'] ) );
$regex = implode( '.*', array_map( 'preg_quote', $list ) );
// Then set 'compare' to REGEXP and 'value' to the above pattern.
$args['meta_query']['name_search']['compare'] = 'REGEXP';
$args['meta_query']['name_search']['value'] = $regex;
在 WordPress 5.7.2 中尝试和测试,但请注意 MySQL reference manual.
中的“非多字节安全”警告
备用解决方案(等同于上述解决方案)
如果你想使用 LIKE
而不是 REGEXP
,但又不想以对同一个键的潜在大量元查询结束,那么你可以:
使用三个搜索字段,即名字、姓氏和中间名首字母,以及三个元子句,每个搜索字段一个,但都将 key
设置为 _full_name
。例如
/*
* Assuming your form contains these:
<input name="first_name">
<input name="last_name">
<input name="middle_initial">
*/
$name_search = array(
'relation' => 'AND',
);
// * okay, this still uses 3 clauses, but it always would be just 3 clauses
foreach ( array( 'first_name', 'last_name', 'middle_initial' ) as $name ) {
if ( ! empty( $_POST[ $name ] ) ) {
$name_search[ $name ] = array(
'key' => '_full_name',
'value' => sanitize_text_field( $_POST[ $name ] ),
'compare' => 'LIKE',
);
}
}
$args['meta_query']['name_search'] = $name_search;
我必须构建一个搜索表单,在其中按全名(名字 + 中间名首字母 + 姓氏)搜索公司成员。所有名字都采用这种形式:John B. Doe.
以下代码适用于这些情况:John, John B., Doe, B., John B. Doe,但查询搜索不起作用:John Doe
.
if (isset($_POST['search']) && $_POST['search'] != -1) {
$args['meta_query']['name_search']['key'] = "_full_name";
$args['meta_query']['name_search']['compare'] = "LIKE";
$args['meta_query']['name_search']['value'] = $_POST['search'];
}
我应该如何改进查询才能同时使用:名字和姓氏 (John Doe) 以及名字、姓氏加上中间名首字母 (John B. Doe)?
我可以建议以下解决方案:
$search = $_POST['search'];
$words = explode( $search, ' ' );
$words = array_filter(
$words,
function ( $word ) {
return ! empty( trim( $word ) );
}
);
foreach ( $words as $index => $word ) {
$query_name = "name_search_$index";
$args['meta_query'][ $query_name ] = [
'key' => '_full_name',
'value' => $word,
'compare' => 'LIKE',
];
}
代码解释:
- 我将搜索短语拆分为单词。单词是任何符号序列 空格之间
- 我删除了空格。如果您的搜索字符串是
John Doe
或John Doe
或John Doe
搜索结果应该相同。 - 我构建了多个查询。每个查询对应一个单词。如果您搜索 John Doe,查询将由 2 个查询构建。查询次数和字数一样多。
此解决方案存在的问题:
- 太多的词就是太多的查询。包含 LIKE 的查询太多太慢。
- 搜索
John Doe
和Doe John
会得到相同的结果。单词的顺序对结果没有影响。
我建议仅将此解决方案用作临时解决方案。 好的解决方案 - 使用全文搜索插件或编写全文搜索解决方案 你自己。您可以在 MySQL 官方页面上找到有关全文搜索的更多详细信息: https://dev.mysql.com/doc/refman/8.0/en/fulltext-search.html
首先,如果只有 WordPress 不转义 value
中的 %
字符(即搜索关键字),您可以简单地将搜索关键字中的空格替换为 %
,因此你会得到一个像 meta_value LIKE '%John%Doe%'
这样的子句,它会匹配 John B. Doe
.
所以因为搜索关键字中的 %
被转义(这是一件好事,顺便说一句),那么你可以改用 REGEXP
(正则表达式搜索)然后替换空格.*
相当于 LIKE
子句中的 %
。
工作示例
在您的代码中替换为:
$args['meta_query']['name_search']['compare'] = "LIKE";
$args['meta_query']['name_search']['value'] = $_POST['search'];
有了这个:
// Build the regular expression pattern.
$list = preg_split( '/ +/', trim( $_POST['search'] ) );
$regex = implode( '.*', array_map( 'preg_quote', $list ) );
// Then set 'compare' to REGEXP and 'value' to the above pattern.
$args['meta_query']['name_search']['compare'] = 'REGEXP';
$args['meta_query']['name_search']['value'] = $regex;
在 WordPress 5.7.2 中尝试和测试,但请注意 MySQL reference manual.
中的“非多字节安全”警告备用解决方案(等同于上述解决方案)
如果你想使用 LIKE
而不是 REGEXP
,但又不想以对同一个键的潜在大量元查询结束,那么你可以:
使用三个搜索字段,即名字、姓氏和中间名首字母,以及三个元子句,每个搜索字段一个,但都将 key
设置为 _full_name
。例如
/*
* Assuming your form contains these:
<input name="first_name">
<input name="last_name">
<input name="middle_initial">
*/
$name_search = array(
'relation' => 'AND',
);
// * okay, this still uses 3 clauses, but it always would be just 3 clauses
foreach ( array( 'first_name', 'last_name', 'middle_initial' ) as $name ) {
if ( ! empty( $_POST[ $name ] ) ) {
$name_search[ $name ] = array(
'key' => '_full_name',
'value' => sanitize_text_field( $_POST[ $name ] ),
'compare' => 'LIKE',
);
}
}
$args['meta_query']['name_search'] = $name_search;