web-dev-qa-db-ja.com

スローメディアクエリを無効にしますか?

ユーザーが管理者内で投稿を作成または更新すると、その操作が完了するまでに15〜30秒かかります。

原因はこの遅いクエリのようです。

SELECT ID
FROM wp_posts
WHERE post_type = 'attachment'
AND post_mime_type LIKE 'video%'
LIMIT 1

これは 既知のバグ であり、コアチームが取り組んでいますが、それまでの間、このクエリを無効にしたいと思います。 pre_get_postsフィルタのようなものを使ってこれを私のfunctions.phpファイルの中ですることができますか?

3
bigmike7801

WordPressのバージョンに対する解決策> = 4.7.4(4.8)

Ticket - #31071wp_enqueue_media()関数の中で3つのスローメディアクエリを上書きするための新しいフィルタのパッチを紹介する:

  • media_library_show_audio_playlist(@param bool | null)

    インラインドキュメントから: ボタンを表示するかどうか、またはオーディオファイルがメディアライブラリに存在するかどうかに基づいて決定するnull

  • media_library_show_video_playlist(@param bool | null)

    インラインドキュメントから: ボタンを表示するかどうか、またはメディアライブラリにビデオファイルが存在するかどうかに基づいて決定するnull

  • media_library_months_with_files(@param配列| null)

    インラインドキュメントより:monthおよびyearプロパティを持つオブジェクトの配列、あるいはデフォルトの振る舞いのためのnull(またはその他の任意の非配列値)。

例:

これがデモ用のプラグインです。

<?php
/**
  * Plugin Name:  Override Possible Slow Media Queries
  * Plugin URI:   https://wordpress.stackexchange.com/a/200383/26350
  */

// Always show audio button
add_filter( 'media_library_show_audio_playlist', '__return_true' );

// Always show video button
add_filter( 'media_library_show_video_playlist', '__return_true' );

// Cache media library file months with the transients API
add_filter( 'media_library_months_with_files', function( $months )
{
    // Generate file months when it's not cached or the transient has expired
    if ( false === ( $months = get_transient( 'wpse_media_library_months_with_files' ) ) )
    {
        global $wpdb;

        /**
         * Note that we want to avoid returning non-array file months,  
         * to avoid running the slow query twice.
         *
         * From the Codex for wpdb::get_results( $query, $output_type ):
         *
         * "If no matching rows are found, or if there is a 
         *  database error, the return value will be an empty array.
         *  If your $query string is empty, or you pass an invalid 
         *  $output_type, NULL will be returned."
         *
         * So it looks like we're covered, as we're not dealing with 
         * empty query or a wrong return type.
         */
        $months = $wpdb->get_results( $wpdb->prepare( "
            SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
            FROM $wpdb->posts
            WHERE post_type = %s
            ORDER BY post_date DESC
         ", 'attachment' ) );

        // Cache the results
        set_transient(
            'wpse_media_library_months_with_files',
                $months,
                12 * HOUR_IN_SECONDS   // <-- Override to your needs!
             );
    }
    return $months;
} );

たとえば、次のようにしてファイルを月単位で厳選することもできます。

$months = [
    (object) [ 'year' => 2017, 'month' => 2 ],
    (object) [ 'year' => 2017, 'month' => 1 ],
    (object) [ 'year' => 2016, 'month' => 12 ],
];

media_library_months_with_filesフィルタを使用する。

前の答え

これらのクエリはwp_enqueue_media()関数にあります。

$has_audio = $wpdb->get_var( "
            SELECT ID
            FROM $wpdb->posts
            WHERE post_type = 'attachment'
            AND post_mime_type LIKE 'audio%'
            LIMIT 1
    " );
 $has_video = $wpdb->get_var( "
            SELECT ID
            FROM $wpdb->posts
            WHERE post_type = 'attachment'
            AND post_mime_type LIKE 'video%'
            LIMIT 1
    " );
 $months = $wpdb->get_results( $wpdb->prepare( "
            SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
            FROM $wpdb->posts
            WHERE post_type = %s
            ORDER BY post_date DESC
    ", 'attachment' ) );

これらの遅いクエリを修正する1つの方法があります。

/**
 * Modify the potential slow $has_audio, $has_video and $months queries
 *
 * @link http://wordpress.stackexchange.com/a/200383/26350
 */
add_filter( 'media_upload_tabs', function( $tabs )
{
    add_filter( 'query', 'wpse_replace_months_sql' );
    add_filter( 'query', 'wpse_replace_audio_video_sql' );
    return $tabs;
} );

add_filter( 'media_view_settings', function( $settings )
{
    remove_filter( 'query', 'wpse_replace_months_sql' );
    remove_filter( 'query', 'wpse_replace_audio_video_sql' );
    return $settings;
} );

where(PHP 5.4以降):

/**
 * Use "SELECT false" for the $has_audio and $has_video queries
 */
function wpse_replace_audio_video_sql( $sql )
{
   global $wpdb;
   foreach( [ 'audio', 'video' ] as $type )
   {
      $find = "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'attachment' 
          AND post_mime_type LIKE '{$type}%' LIMIT 1";
      if( trim( preg_replace('/\s+/', ' ', $sql) ) == trim( preg_replace('/\s+/', ' ', $find) ) )
          return "SELECT false"; // <-- We could also use true here if needed
   }
   return $sql;
} 

そして

/**
 * Replace the available months query with the current month
 */
function wpse_replace_months_sql( $sql )
{
    global $wpdb;
    $find = "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
        FROM {$wpdb->posts} WHERE post_type = 'attachment' ORDER BY post_date DESC";
    if( trim( preg_replace('/\s+/', ' ', $sql) ) == trim( preg_replace('/\s+/', ' ', $find) ) )
         $sql = "SELECT YEAR( CURDATE() ) as year, MONTH( CURDATE() ) as month";
    return $sql;
}

オプションテーブルにhas_audioおよびhas_videoカウンターを作成して audio または video ファイルをアップロードまたは削除するたびにこれを更新することでこれを改良することを試みるかもしれません。

質問に記載されているTracチケットには、 提案されたインデックスがあります

ALTER TABLE $wpdb->posts ADD INDEX type_mime(post_type,post_mime_type)

それはいくらかの後押しをするかもしれません。

@ Denis-de-Bernardyも月の部分の代替クエリの例を示します。

4
birgire