WordPressには、カスタム・フィールド値(メタ・キー)をSQLの条件にしてクエリが実行できるんですが、WordPress5.3から、そのメタ・キーSQLクエリの比較演算子が増えました。LIKE演算子以外にも多くをサポートします。
これで普通のSQLと変わらないくらいのクエリが実行できます。
開発者向けの話です。テーマ・プラグインを作っている人は要チェック。
WP5.3が対応する比較演算子
WP5.1からWP_Query, WP_Meta_Queryでメタ・キーの比較にLIKEが使えるようになりました。
(デフォルトはイコール(=))
メタ・キーはカスタムフィールド名のことで、管理画面の編集ページのカスタマイズをしている人にはおなじみのやつです。
WP5.3ではその比較演算子が大幅に増えました。メタ・キーのクエリはコンプリートしています。
演算子 | WP5.2まで | WP5.3以降 |
---|---|---|
= (デフォルト) | ○ | ○ |
!= | ✕ | ○ |
IN | ✕ | ○ |
NOT IN | ✕ | ○ |
LIKE | ○ | ○ |
NOT LIKE | ✕ | ○ |
RLIKE | ✕ | ○ |
REGEXP | ✕ | ○ |
NOT REGEXP | ✕ | ○ |
EXISTS | ✕ | ○ |
NOT EXISTS | ✕ | ○ |
メタ情報は、コメント、ユーザー、カテゴリ・タグなどのターム、サイト情報にもありますが、公式ガイドでは、投稿・固定ページ(カスタムも)のWP_Queryと、その中で使うメタクエリ(WP_Meta_Query)しか触れてません。
大元のWP_Meta_Queryに変更があるので使えるのでしょう。『そのへんは応用で分かるでしょ?』ってことなのかもしれません。
投稿情報は基本的な情報しかありません。
(タイトル、コンテンツ、日時など。)
それ以外の情報はカスタムフィールドに設定してメタ情報として保存します。
(ほかのコメント情報なども同じ。)
WP_Meta_QueryはSQLを作成するクラスで実行まではしません。
WP_Queryなどメタ情報をもつもののクエリに、メタ検索のSQLを追加するヘルパーです。
メタ・キーのクエリ
WP_Queryを使ってメタ・キーのクエリを実行してみましょう。
指定するパラメータはシングル指定と複数指定のふたつあります。
シングル | 複数 | |
---|---|---|
meta_compare_key | compare_key | 既存 |
meta_type_key | type_key | WP5.3で追加 |
$args = [ 'meta_key' => 'test', 'meta_compare_key => 'test', 'meta_type_key => 'BINARY', ]; | $args = [ 'meta_query' => [ [ 'key' => 'test', 'compare_key => 'test', 'type_key => 'BINARY', ], ], ]; |
まずは元々あったcompare_keyで実行します。
function test_query( $args ) {
$query = new WP_Query( $args );
echo '<p>-----------------------------------------------</p>';
echo 'sql: ' . $query->request . PHP_EOL;
echo '<br><br>';
echo 'count: ' . $query->found_posts . PHP_EOL;
echo '<br><br>';
}
$args = [
'post_type' => 'post',
];
test_query( $args );
$args = [
'post_type' => 'post',
'meta_query' => [
[
'key' => 'views', // カスタムフィールド名
'compare_key' => 'EXISTS', // 比較演算子
],
],
];
test_query( $args );
-----------------------------------------------
sql:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
WHERE 1=1
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'future'
OR wp_posts.post_status = 'draft'
OR wp_posts.post_status = 'pending'
OR wp_posts.post_status = 'private'
)
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10
count: 404
-----------------------------------------------
sql:
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_postmeta.meta_key = 'views' )
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'future'
OR wp_posts.post_status = 'draft'
OR wp_posts.post_status = 'pending'
OR wp_posts.post_status = 'private'
)
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10
count: 293
(SQLはあとで整形)
検索SQLにメタテーブルが加わっているのが分かります。検索結果の件数もちがいますね?
正規表現の条件指定
正規表現を使うときはcompare_keyだけではできません。
正規表現の 比較演算子 |
---|
REGEXP (RLIKE) |
NOT REGEXP |
これらの演算子は、MySQL / MariaDBの仕様で大文字と小文字を区別できないからです。
MySQL / MariaDBでは、BINARYに変換(キャスト)すれば大文字・小文字も比較できます。そのためのパラメータがtype_keyです。
type_keyは比較文字列の型を指定します。
サンプルを実行してみましょう。
function test_query( $args ) {
$query = new WP_Query( $args );
echo '<p>-----------------------------------------------</p>';
echo 'sql: ' . $query->request . PHP_EOL;
echo '<br><br>';
echo 'count: ' . $query->found_posts . PHP_EOL;
echo '<br><br>';
}
/**
* 大文字・小文字の区別なし
*/
$args = [
'post_type' => 'post',
'meta_query' => [
[
'key' => 'views', // カスタムフィールド名
'compare_key' => 'REGEXP', // 比較演算子
],
],
];
test_query( $args );
$args = [
'post_type' => 'post',
'meta_query' => [
[
'key' => 'Views',
'compare_key' => 'REGEXP',
],
],
];
test_query( $args );
/**
* 大文字・小文字の区別あり
*/
$args = [
'post_type' => 'post',
'meta_query' => [
[
'key' => 'views',
'compare_key' => 'REGEXP',
'type_key' => 'BINARY',
],
],
];
test_query( $args );
$args = [
'post_type' => 'post',
'meta_query' => [
[
'key' => 'Views',
'compare_key' => 'REGEXP',
'type_key' => 'BINARY',
],
],
];
test_query( $args );
-----------------------------------------------
sql:
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_postmeta.meta_key REGEXP 'views' )
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'future'
OR wp_posts.post_status = 'draft'
OR wp_posts.post_status = 'pending'
OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10
count: 293
-----------------------------------------------
sql:
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_postmeta.meta_key REGEXP 'Views' )
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'future'
OR wp_posts.post_status = 'draft'
OR wp_posts.post_status = 'pending'
OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10
count: 293
-----------------------------------------------
sql:
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_postmeta.meta_key REGEXP BINARY 'views' )
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'future'
OR wp_posts.post_status = 'draft'
OR wp_posts.post_status = 'pending'
OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10
count: 293
-----------------------------------------------
sql:
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_postmeta.meta_key REGEXP BINARY 'Views' )
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'future'
OR wp_posts.post_status = 'draft'
OR wp_posts.post_status = 'pending'
OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10
count: 0
(SQLはあとで整形)
最後のクエリは大文字が入って、BINARYを使っているので0件になりました。
今回はテスト用なので正規表現は完全一致を使いました。正規表現にはいろいろなやり方があります。
ちょっと豆知識
比較演算子のEXIST, NOT EXISTは内部で演算子を変換します。
変換前 | 変換後 |
---|---|
EXIST | = |
NOT EXIST | != |
その他のメタクエリ
公式ガイドでは、WP_QueryとWP_Meta_Queryしか触れてません。
WP_Meta_Queryが対応しているので、その他のメタクエリはどうなのか実験してみました。
シングル 指定 | 複数 指定 | |
タクソノミー (カテゴリ、タグなど) WP_Term_Query | ○ | ○ |
コメント WP_Comment_Query | ○ | ○ |
ユーザー WP_User_Query | ○ | ○ |
サイト WP_Site_Query | ○ | ○ |
やっぱり使えるようですね?
実験のソースコードです。まず、この関数を作りました。
function test_query( $type, $args ) {
$query = null;
if ( 'term' === $type ) {
$query = new WP_Term_Query( $args );
} else if ( 'comment' === $type ) {
$query = new WP_Comment_Query( $args );
} else if ( 'user' === $type ) {
$query = new WP_User_Query( $args );
} else if ( 'site' === $type ) {
$query = new WP_Site_Query( $args );
}
echo 'sql: ' . $query->request;
echo '<br><br>';
}
メタテーブルが使われているかが分かればいいので、結果にSQLを出力します。
検索結果が0件になっても気にしません。必須パラメータは適当なのでヒットしないはずだから。
(そもそも実験用のデモ環境なのでデータ自体適当。)
これからどんどんサンプルを実行していきます。結果のSQLはあとで整形しました。
タクソノミー(カテゴリ、タグなど)
echo '<p>--------- タクソノミー - シングル指定 ----------</p>';
$args = [
'taxonomy' => 'category',
'meta_key' => 'test-key',
'meta_compare_key' => 'REGEXP',
'meta_type_key' => 'BINARY',
];
test_query( 'term', $args );
echo '<p>--------- タクソノミー - 複数指定 ----------</p>';
$args = [
'taxonomy' => 'category',
'meta_query' => [
[
'key' => 'test-key',
'compare_key' => 'REGEXP',
'type_key' => 'BINARY',
],
],
];
test_query( 'term', $args );
--------- タクソノミー - シングル指定 ----------
sql:
SELECT DISTINCT t.*, tt.*
FROM wp_terms AS t
INNER JOIN wp_termmeta
ON ( t.term_id = wp_termmeta.term_id )
INNER JOIN wp_term_taxonomy AS tt
ON t.term_id = tt.term_id
WHERE tt.taxonomy IN ('category')
AND ( wp_termmeta.meta_key REGEXP BINARY 'test-key' )
ORDER BY t.term_order ASC
--------- タクソノミー - 複数指定 ----------
sql:
SELECT DISTINCT t.*, tt.*
FROM wp_terms AS t
INNER JOIN wp_termmeta
ON ( t.term_id = wp_termmeta.term_id )
INNER JOIN wp_term_taxonomy AS tt
ON t.term_id = tt.term_id
WHERE tt.taxonomy IN ('category')
AND ( wp_termmeta.meta_key REGEXP BINARY 'test-key' )
ORDER BY t.term_order ASC
wp_termmetaテーブルが使われてます。(wp_は環境により異なる。)
コメント
echo '<p>--------- コメント - シングル指定 ----------</p>';
$args = [
'meta_key' => 'test-key',
'meta_compare_key' => 'REGEXP',
'meta_type_key' => 'BINARY',
];
test_query( 'comment', $args );
echo '<p>--------- コメント - 複数指定 ----------</p>';
$args = [
'meta_query' => [
[
'key' => 'test-key',
'compare_key' => 'REGEXP',
'type_key' => 'BINARY',
],
],
];
test_query( 'comment', $args );
--------- コメント - シングル指定 ----------
sql:
SELECT wp_comments.comment_ID
FROM wp_comments INNER JOIN wp_commentmeta
ON ( wp_comments.comment_ID = wp_commentmeta.comment_id )
WHERE ( ( comment_approved = '0' OR comment_approved = '1' ) )
AND ( wp_commentmeta.meta_key REGEXP BINARY 'test-key' )
GROUP BY wp_comments.comment_ID
ORDER BY wp_comments.comment_date_gmt DESC
--------- コメント - 複数指定 ----------
sql:
SELECT wp_comments.comment_ID
FROM wp_comments INNER JOIN wp_commentmeta
ON ( wp_comments.comment_ID = wp_commentmeta.comment_id )
WHERE ( ( comment_approved = '0' OR comment_approved = '1' ) )
AND ( wp_commentmeta.meta_key REGEXP BINARY 'test-key' )
GROUP BY wp_comments.comment_ID
ORDER BY wp_comments.comment_date_gmt DESC
wp_commentmetaテーブルが使われてます。
ユーザー
echo '<p>--------- ユーザー - シングル指定 ----------</p>';
$args = [
'meta_key' => 'test-key',
'meta_compare_key' => 'REGEXP',
'meta_type_key' => 'BINARY',
];
test_query( 'user', $args );
echo '<p>--------- ユーザー - 複数指定 ----------</p>';
$args = [
'meta_query' => [
[
'key' => 'test-key',
'compare_key' => 'REGEXP',
'type_key' => 'BINARY',
],
],
];
test_query( 'user', $args );
--------- ユーザー - シングル指定 ----------
sql:
SELECT SQL_CALC_FOUND_ROWS wp_users.*
FROM wp_users INNER JOIN wp_usermeta
ON ( wp_users.ID = wp_usermeta.user_id )
INNER JOIN wp_usermeta AS mt1
ON ( wp_users.ID = mt1.user_id )
WHERE 1=1
AND ( ( ( wp_usermeta.meta_key REGEXP BINARY 'test-key' )
AND ( mt1.meta_key = 'wp_capabilities' ) ) )
ORDER BY user_login ASC
--------- ユーザー - 複数指定 ----------
sql:
SELECT SQL_CALC_FOUND_ROWS wp_users.*
FROM wp_users INNER JOIN wp_usermeta
ON ( wp_users.ID = wp_usermeta.user_id )
INNER JOIN wp_usermeta AS mt1
ON ( wp_users.ID = mt1.user_id )
WHERE 1=1
AND ( ( ( wp_usermeta.meta_key REGEXP BINARY 'test-key' )
AND ( mt1.meta_key = 'wp_capabilities' ) ) )
ORDER BY user_login ASC
wp_usermetaテーブルが使われてます。
サイト
echo '<p>--------- サイト - シングル指定 ----------</p>';
$args = [
'ID' => 1,
'meta_key' => 'test-key',
'meta_compare_key' => 'REGEXP',
'meta_type_key' => 'BINARY',
];
test_query( 'site', $args );
echo '<p>--------- サイト - 複数指定 ----------</p>';
$args = [
'ID' => 1,
'meta_query' => [
[
'key' => 'test-key',
'compare_key' => 'REGEXP',
'type_key' => 'BINARY',
],
],
];
test_query( 'site', $args );
--------- サイト - シングル指定 ----------
sql:
SELECT wp_blogs.blog_id
FROM wp_blogs INNER JOIN wp_blogmeta
ON ( wp_blogs.blog_id = wp_blogmeta.blog_id )
WHERE wp_blogs.blog_id = 1
AND ( wp_blogmeta.meta_key REGEXP BINARY 'test-key' )
GROUP BY wp_blogs.blog_id
ORDER BY wp_blogs.blog_id ASC
LIMIT 100
--------- サイト - 複数指定 ----------
sql:
SELECT wp_blogs.blog_id
FROM wp_blogs INNER JOIN wp_blogmeta
ON ( wp_blogs.blog_id = wp_blogmeta.blog_id )
WHERE wp_blogs.blog_id = 1
AND ( wp_blogmeta.meta_key REGEXP BINARY 'test-key' )
GROUP BY wp_blogs.blog_id
ORDER BY wp_blogs.blog_id ASC
LIMIT 100
wp_blogmetaテーブルが使われてます。