web-dev-qa-db-fra.com

get_adjacent_post alternative sur une très grande base de données

J'utilise get_next_post et get_previous_post (de la même catégorie) pour afficher la vignette et un lien vers les publications respectives de mon modèle unique, mais c'est dans une très grande base de données que mon serveur a du mal à passer à travers le temps.

Il existe 49 984 publications sur le site, entre les publications, les brouillons et les pièces jointes:

mysql> select post_status, count(1) from wp_posts group by post_status;
+-------------+----------+
| post_status | count(1) |
+-------------+----------+
| auto-draft  |        1 |
| draft       |      269 |
| inherit     |    38656 |
| private     |        5 |
| publish     |    11053 |
+-------------+----------+
5 rows in set (0,07 sec)

Je peux comprendre pourquoi get_previous_post nécessite autant de ressources à exécuter. Après tout, il doit comparer tous les messages correspondants par date pour déterminer le prochain ou le précédent:

De $wdbp->queries, juste pour récupérer le post précédent:

Question:

SELECT p.id
FROM wp_posts AS p 
  INNER JOIN wp_term_relationships AS tr ON p.ID = tr.object_id 
  INNER JOIN wp_term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id 
    AND tt.taxonomy = 'category' AND tt.term_id IN (5,7,14474) 
WHERE
  p.post_date < '2013-04-11 12:15:30' AND p.post_type = 'post' AND
  p.post_status = 'publish' AND tt.taxonomy = 'category'

ORDER BY p.post_date DESC LIMIT 1

De:

  require('wp-blog-header.php'),
  require_once('wp-includes/template-loader.php'),
  include('/themes/extra/single.php'),
  get_previous_post, get_adjacent_post

Prise:

111.7ms

Je sais que ce n'est pas habituellement un travail difficile, mais dans mon cas, c'est le cas.

Existe-t-il un autre moyen d'obtenir le message précédent et le message suivant?

Edit:

Comme le souligne s_ha_dum , avec la condition $in_same_cat, c’est la source du problème, en effectuant deux jointures très lourdes avec wp_term_relationships et wp_term_taxonomy. La solution serait de ne pas rechercher les publications dans la même catégorie. Malheureusement, je dois utiliser cette condition.

Solution edit:

Voici une solution Gist avec toscho plus un correctif mineur et deux nouvelles fonctions: get_fast_previous_post et get_fast_next_post pour obtenir les objets post.

5
davidmh

Je ne vois pas comment rendre la requête plus rapide, mais nous pouvons mettre en cache le résultat.

Malheureusement, il n'y a pas de crochet pour contourner next_post_link() et previous_post_link(), nous devons donc remplacer ces fonctions par des fonctions personnalisées.

L'exemple de code suivant utilise un post meta field pour stocker le résultat. Il pourrait y avoir des effets secondaires - j'ai essayé de couvrir la plupart des cas, mais j'ai peut-être oublié quelque chose.

<?php # -*- coding: utf-8 -*-
namespace WPSE;
/* Plugin Name: Fast Next/Prev Post Links
 * Plugin URI:  https://wordpress.stackexchange.com/q/101435/
 */

add_action(
    'fast_prev_post_link',
    __NAMESPACE__ . '\fast_prev_post_link',
    10,
    4
);
add_action(
    'fast_next_post_link',
    __NAMESPACE__ . '\fast_next_post_link',
    10,
    4
);
add_action(
    'transition_post_status',
    __NAMESPACE__ . '\delete_fast_adjacent_meta',
    10,
    3
);


/**
 * Print previous post link.
 *
 * @param unknown_type $format
 * @param unknown_type $link
 * @param unknown_type $in_same_cat
 * @param unknown_type $excluded_categories
 */
function fast_prev_post_link(
    $format              = '&laquo; %link',
    $link                = '%title',
    $in_same_cat         = FALSE,
    $excluded_categories = ''
    )
{
    empty ( $format ) && $format = '%link &raquo;';
    fast_adjacent_post_link(
        $format,
        $link,
        $in_same_cat,
        $excluded_categories,
        TRUE
    );

}
/**
 * Print next post link.
 *
 * @param  string $format
 * @param  string $link
 * @param  bool $in_same_cat
 * @param  array|string $excluded_categories
 * @return void
 */
function fast_next_post_link(
    $format              = '%link &raquo;',
    $link                = '%title',
    $in_same_cat         = FALSE,
    $excluded_categories = ''
    )
{
    empty ( $format ) && $format = '%link &raquo;';

    fast_adjacent_post_link(
        $format,
        $link,
        $in_same_cat,
        $excluded_categories,
        FALSE
    );
}

/**
 * Display adjacent post link.
 *
 * Slightly changed copy of adjacent_post_link().
 * Unfortunately, WP mixes retrieving data and display. :(
 *
 * Can be either next post link or previous.
 *
 * @param  string       $format              Link anchor format.
 * @param  string       $link                Link permalink format.
 * @param  bool         $in_same_cat         Whether link should be in a same
 *                                           category.
 * @param  array|string $excluded_categories Array or comma-separated list of
 *                                           excluded category IDs.
 * @param  bool         $previous            Default is true. Whether to display
 *                                           link to previous or next post.
 * @return void
 */
function fast_adjacent_post_link(
    $format,
    $link,
    $in_same_cat         = FALSE,
    $excluded_categories = '',
    $previous            = TRUE
    )
{
    if ( $previous && is_attachment() )
        $post = get_post( get_post()->post_parent );
    else // the only real change
        $post = get_fast_adjacent_post(
            $in_same_cat,
            $excluded_categories,
            $previous
        );

    if ( ! $post ) {
        $output = '';
    } else {
        $title = $post->post_title;

        if ( empty( $post->post_title ) )
            $title = $previous ? __( 'Previous Post' ) : __( 'Next Post' );

        $title = apply_filters( 'the_title', $title, $post->ID );
        $date = mysql2date( get_option( 'date_format' ), $post->post_date );
        $rel = $previous ? 'prev' : 'next';

        $string = '<a href="' . get_permalink( $post ) . '" rel="'.$rel.'">';
        $inlink = str_replace( '%title', $title, $link );
        $inlink = str_replace( '%date', $date, $inlink );
        $inlink = $string . $inlink . '</a>';

        $output = str_replace( '%link', $inlink, $format );
    }

    $adjacent = $previous ? 'previous' : 'next';

    echo apply_filters( "{$adjacent}_post_link", $output, $format, $link, $post );
}

/**
 * Get next or previous post from post meta.
 *
 * @param bool         $in_same_cat
 * @param string|array $excluded_categories
 * @param bool         $previous
 * @param object       $post
 * @return object|NULL Either the found post object or NULL
 */
function get_fast_adjacent_post(
    $in_same_cat         = FALSE,
    $excluded_categories = array(),
    $previous            = TRUE,
    $post                = NULL
)
{
    if ( ! $post = get_post( $post ) )
        return;

    $excluded_categories = prepare_excluded_categories( $excluded_categories );

    $type     = $previous ? 'prev' : 'next';
    $cat_hash = empty ( $excluded_categories ) ? 0 : join( '-', $excluded_categories );
    $hash     = (int) $in_same_cat . "-$cat_hash";

    $meta = (array) get_post_meta( $post->ID, "_fast_{$type}_post", TRUE );

    if ( isset ( $meta[ $hash ] ) )
        return get_post( $meta[ $hash ] );

    $ad_post = get_adjacent_post( $in_same_cat, $excluded_categories, $previous );

    if ( ! $ad_post )
        return;

    $meta[ $hash ] = $ad_post->ID;
    update_post_meta( $post->ID, "_fast_{$type}_post", $meta );

    return $ad_post;
}

/**
 * Prepare categories sent as string.
 *
 * @param  string|array $cats
 * @return array
 */
function prepare_excluded_categories( $cats )
{
    if ( empty ( $cats ) or is_array( $cats ) )
        return array();

    $cats = explode( ',', $cats );
    $cats = array_map( 'trim', $excluded_categories );
    $cats = array_map( 'intval', $excluded_categories );

    return $cats;
}

/**
 * Deletes post meta values for the current post and all posts referencing it.
 *
 * @wp-hook transition_post_status
 * @param   string $new_status
 * @param   string $old_status
 * @param   object $post
 * @return  void
 */
function delete_fast_adjacent_meta( $new_status, $old_status, $post )
{
    $prev = (array) get_post_meta( $post->ID, '_fast_prev_post', TRUE );

    if ( ! empty ( $prev ) )
    {
        foreach ( $prev as $post_id )
            delete_post_meta( $post_id, '_fast_next_post' );
    }

    $next = (array) get_post_meta( $post->ID, '_fast_next_post', TRUE );

    if ( ! empty ( $next ) )
    {
        foreach ( $next as $post_id )
            delete_post_meta( $post_id, '_fast_prev_post' );
    }

    delete_post_meta( $post->ID, '_fast_prev_post' );
    delete_post_meta( $post->ID, '_fast_next_post' );
}

Pour utiliser ces fonctions, ajoutez ceci au thème:

do_action( 'fast_prev_post_link' );
do_action( 'fast_next_post_link' );
5
fuxia