web-dev-qa-db-ja.com

データベースmeta_valuesを使用してpre_get_postsまたは 'request'フックを使用して新しい投稿順を計算する

更新:私はこの記事の最後にある選択した答えの実装を確認しました。

私は、時間減衰に加えて Redditのhotness algorithm に基づくソートアルゴリズムを実装しています( Hacker News hotness algorithm のように)。

私が現在満足しているコードは、いくつかのExcelシートの試行錯誤の結果です。これがPHPの作業コードです。

<?php get_header();
$timenow = time() - 1211380200; // this is my custom Epoch time, it translates to when my first WP post was made ?>

<?php
if ( have_posts() ) : while ( have_posts() ) : the_post(); 

$hearts = get_post_meta($post->ID, '_tjnz_hearts', true);
$plays      = get_post_meta($post->ID, '_tjnz_deals_plays', true);
$downloads  = get_post_meta($post->ID, '_tjnz_deals_downloads', true);
$ups        = get_post_meta($post->ID, '_tjnz_temperature_upvotes', true);
$downs      = get_post_meta($post->ID, '_tjnz_temperature_downvotes', true);
$date       = get_post_time('U', true); // fetches the GMT postdate of post in Unix format

$score = ( ( $hearts * 2 ) + $plays + $downloads + $ups ) - $downs;
$order = log( max( abs( $score ), 1 ), 6 );
$seconds = $date - 1211380200;
if( $score > 0 ) { $sign = 1;   } elseif( $score < 0 ) { $sign = -1; } else { $sign = 0; }
$hotness = round( $order + ( ( $sign * $seconds ) / 336000 ), 7 );
$degrees = round( ( $order * ( $sign * 32 ) ) + ( ( -4 * ( $timenow - $seconds ) ) / 336000 ), 7 ); ?>
        <p>
            <?php the_title(); ?> <?php echo $post->ID; ?><br />
            <?php echo 'hearts: ' . $hearts . '<br />';
            echo 'plays: ' .$plays . '<br />';
            echo 'dls: ' .$downloads . '<br />';
            echo 'up: ' .$ups . '<br />';
            echo 'down: ' .$downs . '<br />';
            echo 'date: ' .$date . '<br /><br />';
            echo 'score: ' .$score . '<br />';
            echo 'order: ' .$order . '<br />';
            echo 'seconds: ' .$seconds . '<br />';
            echo 'timenow: ' .$timenow . '<br />';
            echo 'difference: ' .($timenow - $seconds) . '<br />';
            echo 'sign: ' .$sign . '<br /><br />';
            echo 'hotness: ' .$hotness . '<br />';
            echo 'degrees: ' .$degrees; ?><hr />
        </p>

<?php endwhile; else: ?>
<p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
<?php endif; ?>

問題

このPHPコードの問題は、WordPressクエリがすでに作成されているため、投稿を古い順に返すということです。

  • 'Hotness'スコアで投稿をソートしたい
  • 新しいWP_Queryオブジェクトやquery_posts()を使用したくないのですが、これは元のクエリを踏みにじるため、不必要なデータベースのやり取りが発生するためです。サイトはかなり混んでいて、私は今1000以上の記事をホスティングしています。
  • pre_get_postsの使い方やrequestフックの使い方がわかりません。はい、私はドキュメンテーションを読んだことがありますが、私にとっては今、それはあまりにも高度になっています。

データベース構造

PHPコードスニペットからわかるように、meta_valuesをテーブルwp_postmetaに保存しています。実際のHotnessスコアはオンザフライで計算され、データベースには保存されません。これは非常に効率的ではなく、とりわけユーザーにとってリアルタイムではないためです。

私の実際の質問

Meta_valuesは式の一部にすぎないため、クエリをmeta_valueで並べ替えることはできません。並べ替え順序として、式の結果を使用します。

どのようにメインのWordPressクエリを変更してmeta_valuesを調べ、各投稿の人気度を計算し、それらの投稿を最も人気の高いものから最も人気のないものに戻すのですか。

選択した答えの私の実装

functions.phpに以下の2つの関数を追加しました。

// add custom wp_postmeta when a new post is created
add_action( 'wp_insert_post', 'tjnz_prepare_postmeta' );
function tjnz_prepare_postmeta( $post_id ) {
    if ( !wp_is_post_revision( $post_id ) ) {
        $hotness = round( ( time() - 1211380200 ) / 336000, 7 );
        add_post_meta( $post_id, '_tjnz_hearts', 0, true );
        add_post_meta( $post_id, '_tjnz_plays', 0, true );
        add_post_meta( $post_id, '_tjnz_downloads', 0, true );
        add_post_meta( $post_id, '_tjnz_upvotes', 0.000, true );
        add_post_meta( $post_id, '_tjnz_downvotes', 0.000, true );
        add_post_meta( $post_id, '_tjnz_hotness', $hotness, true );
    }
}

// build an array of Hotness stats for post
function tjnz_temperature( $tjnz_post_id, $tjnz_timenow ) {
    $hearts     = get_post_meta($tjnz_post_id, '_tjnz_hearts', true);
    $plays      = get_post_meta($tjnz_post_id, '_tjnz_plays', true);
    $downloads  = get_post_meta($tjnz_post_id, '_tjnz_downloads', true);
    $ups        = get_post_meta($tjnz_post_id, '_tjnz_upvotes', true);
    $downs      = get_post_meta($tjnz_post_id, '_tjnz_downvotes', true);
    $hotness    = get_post_meta($tjnz_post_id, '_tjnz_hotness', true);
    $date       = get_post_time('U', true);
    $score      = $hearts + $downloads + $ups - $downs;
    $log        = log( max( abs( $score ), 1 ), 6 );
    $seconds    = $date - 1211380200;
    if( $score >= 0 ) { 
        $sign = 1;
    } else { 
        $sign = -1;
    }
    $degrees    = round( ( $log * ( $sign * 32 ) ) + ( ( -9.6 * ( $tjnz_timenow - $seconds ) ) / 336000 ) + 10, 7 );
    //      round to 7 digits    positive/negative    -2.4 degrees/day      realtime post age       +10 free degrees

    return array(
        'hearts'    => $hearts,
        'plays'     => $plays,
        'downloads' => $downloads,
        'ups'       => $ups,
        'downs'     => $downs,
        'score'     => $score,
        'hotness'   => $hotness,
        'degrees'   => $degrees
    );
}

_tjnz_hotnessは現在、すべての公開された投稿のmeta_valueになっており、ユーザーが投稿に賛成、反対票、ダウンロード、またはお気に入り(お気に入り)を追加するたびに更新されます。 _tjnz_hotnessの値は、最初は投稿の公開時刻(GMT)に基づいており、scoreは0です。公開時刻は、投稿のUnix Epochタイムスタンプから、私の個人的なEpoch(ブログが開始された日付)を引いたものです。その数が何であっても、実際には関係ありません。単に_tjnz_hotnessの値を低くするだけです。私のブログを立ち上げた直後に投稿された投稿は0に近い値になります。

_tjnz_degreesはページが読み込まれるたびに計算されます。このページは、ページがロードされたときのdegrees値を計算するために関数tjnz_temperature()を使用します。投稿からすべてのメタ情報を取得し、それを使って投稿の現在の気温を計算します。

hotnessdegreesの主な違いは、実際の投稿のソートにはhotnessが使用されることです。この値を増減すると、基本的にタイムライン上の投稿が他の投稿と比べてオフセットされます。 degreesの値はこれに基づいていますが、投稿のリアルタイムの年齢を考慮に入れています。

基本的に、これが意味するのは、投稿のhotnessが変化しない場合、degreesの値は実際には低下し始めるということです(1時間あたり0.1度、1日あたり2.4)。

log base 6の設定もそれを可能にして、投稿を 'hot'に保つためにますます多くの投票が必要となるようにします。最終的には、それがもはや新しい投稿から勝つことができなくなるポイントがあるでしょう。私の式では、投稿が必然的に「コールド」になるまでに約5〜7日かかります。

私のカスタムページでは、私は以下のように投稿をリストしています(これはデバッグ出力です。実際の投稿内容はまだ表示されていません)。

<?php
/*
Template Name: Hot
*/
get_header();
$timenow = time() - 1211380200; 
$hot_query = new WP_Query(
    array(
        'post_status' => 'publish',
        'post_type' => 'post',
        'meta_key' => '_tjnz_hotness',
        'posts_per_page' => 30,
        'orderby' => 'meta_value_num',
        'order' => 'DESC'
    )
);

if ( $hot_query->have_posts() ) : while ( $hot_query->have_posts() ) : $hot_query->the_post(); 
    $tjnz_temperature = tjnz_temperature( $post->ID, $timenow );
?>
    <?php the_title(); ?> <?php echo $post->ID; ?><br />
    <?php echo 'hearts: ' . $tjnz_temperature['hearts'] . '<br />';
    echo 'plays: ' . $tjnz_temperature['plays'] . '<br />';
    echo 'dls: ' . $tjnz_temperature['downloads'] . '<br />';
    echo 'up: ' . $tjnz_temperature['ups'] . '<br />';
    echo 'down: ' . $tjnz_temperature['downs'] . '<br />';
    echo 'score: ' . $tjnz_temperature['score'] . '<br />';
    echo 'hotness: ' . $tjnz_temperature['hotness'] . '<br />';
    echo 'degrees: ' . $tjnz_temperature['degrees']; ?><hr />

<?php endwhile; else : ?>
    <p>Oops, Post Not Found!</p>

<?php endif; get_footer(); ?>
2
Marc Dingena

先日、私はこのようなものを作る方法を考えていました、私はあなたがsave_postsフィルタを必要とするだろうという理由で、あらゆる投票、保存または更新で更新される投稿メタ値としてhottnesを保存することをお勧めしますpre_get_postsとメタクエリのような順序で投稿を並べる

$query->set('meta_key'=>'hottnes');
$query->set('orderby'=>'meta_value_num');
$query->set('order'=>'DESC');

お役に立てれば。

2
Poxtron