<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[34310] trunk: Split the comment query.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/34310">34310</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/34310","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>boonebgorges</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2015-09-18 19:27:39 +0000 (Fri, 18 Sep 2015)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Split the comment query.

`WP_Comment_Query` now fetches comments in two stages: (1) a query to get the
IDs of comments matching the query vars, and (2) a query to populate the
objects corresponding to the matched IDs. The two queries are cached
separately, so that sites with persistent object caches will continue to have
complete cache coverage for normal comment queries.

Splitting the query allows our cache strategy to be more modest and precise, as
full comment data is only stored once per comment. It also makes it possible
to introduce logic for paginated threading, which is necessary to address
certain performance problems.

See <a href="https://core.trac.wordpress.org/ticket/8071">#8071</a>.
data is only stored once per comment, instead of along with</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesclasswpcommentqueryphp">trunk/src/wp-includes/class-wp-comment-query.php</a></li>
<li><a href="#trunksrcwpincludescommentfunctionsphp">trunk/src/wp-includes/comment-functions.php</a></li>
<li><a href="#trunktestsphpunittestscommentmetaCachephp">trunk/tests/phpunit/tests/comment/metaCache.php</a></li>
<li><a href="#trunktestsphpunittestscommentqueryphp">trunk/tests/phpunit/tests/comment/query.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesclasswpcommentqueryphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-comment-query.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-comment-query.php  2015-09-18 19:27:24 UTC (rev 34309)
+++ trunk/src/wp-includes/class-wp-comment-query.php    2015-09-18 19:27:39 UTC (rev 34310)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -35,6 +35,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public $meta_query = false;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Metadata query clauses.
+        *
+        * @since 4.4.0
+        * @access protected
+        * @var array
+        */
+       protected $meta_query_clauses;
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Date query container
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 3.7.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -261,8 +270,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public function get_comments() {
</span><span class="cx" style="display: block; padding: 0 10px">                global $wpdb;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $groupby = '';
-
</del><span class="cx" style="display: block; padding: 0 10px">                 $this->parse_query();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Parse meta query
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -281,7 +288,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                // Reparse query vars, in case they were modified in a 'pre_get_comments' callback.
</span><span class="cx" style="display: block; padding: 0 10px">                $this->meta_query->parse_query_vars( $this->query_vars );
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! empty( $this->meta_query->queries ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $meta_query_clauses = $this->meta_query->get_sql( 'comment', $wpdb->comments, 'comment_ID', $this );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $this->meta_query_clauses = $this->meta_query->get_sql( 'comment', $wpdb->comments, 'comment_ID', $this );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // $args can include anything. Only use the args defined in the query_var_defaults to compute the key.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -291,13 +298,66 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $last_changed = microtime();
</span><span class="cx" style="display: block; padding: 0 10px">                        wp_cache_set( 'last_changed', $last_changed, 'comment' );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $cache_key = "get_comments:$key:$last_changed";
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $cache_key = "get_comment_ids:$key:$last_changed";
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( $cache = wp_cache_get( $cache_key, 'comment' ) ) {
-                       $this->comments = $cache;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $comment_ids = wp_cache_get( $cache_key, 'comment' );
+               if ( false === $comment_ids ) {
+                       $comment_ids = $this->get_comment_ids();
+                       wp_cache_add( $cache_key, $comment_ids, 'comment' );
+               }
+
+               // If querying for a count only, there's nothing more to do.
+               if ( $this->query_vars['count'] ) {
+                       // $comment_ids is actually a count in this case.
+                       return intval( $comment_ids );
+               }
+
+               $comment_ids = array_map( 'intval', $comment_ids );
+
+               if ( 'ids' == $this->query_vars['fields'] ) {
+                       $this->comments = $comment_ids;
</ins><span class="cx" style="display: block; padding: 0 10px">                         return $this->comments;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                _prime_comment_caches( $comment_ids, $this->query_vars['update_comment_meta_cache'] );
+
+               // Fetch full comment objects from the primed cache.
+               $_comments = array();
+               foreach ( $comment_ids as $comment_id ) {
+                       if ( $_comment = wp_cache_get( $comment_id, 'comment' ) ) {
+                               $_comments[] = $_comment;
+                       }
+               }
+
+               /**
+                * Filter the comment query results.
+                *
+                * @since 3.1.0
+                *
+                * @param array            $results  An array of comments.
+                * @param WP_Comment_Query &$this    Current instance of WP_Comment_Query, passed by reference.
+                */
+               $_comments = apply_filters_ref_array( 'the_comments', array( $_comments, &$this ) );
+
+               // Convert to WP_Comment instances
+               $comments = array_map( 'get_comment', $_comments );
+
+               $this->comments = $comments;
+               return $this->comments;
+       }
+
+       /**
+        * Used internally to get a list of comment IDs matching the query vars.
+        *
+        * @since 4.4.0
+        * @access protected
+        *
+        * @global wpdb $wpdb
+        */
+       protected function get_comment_ids() {
+               global $wpdb;
+
+               $groupby = '';
</ins><span class="cx" style="display: block; padding: 0 10px">                 $where = array();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Assemble clauses related to 'comment_approved'.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -471,14 +531,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $this->query_vars['count'] ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $fields = 'COUNT(*)';
</span><span class="cx" style="display: block; padding: 0 10px">                } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        switch ( strtolower( $this->query_vars['fields'] ) ) {
-                               case 'ids':
-                                       $fields = "$wpdb->comments.comment_ID";
-                                       break;
-                               default:
-                                       $fields = "*";
-                                       break;
-                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $fields = "$wpdb->comments.comment_ID";
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $join = '';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -625,11 +678,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $join = "JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( ! empty( $meta_query_clauses ) ) {
-                       $join .= $meta_query_clauses['join'];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( ! empty( $this->meta_query_clauses ) ) {
+                       $join .= $this->meta_query_clauses['join'];
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Strip leading 'AND'.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $where[] = preg_replace( '/^\s*AND\s*/', '', $meta_query_clauses['where'] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $where[] = preg_replace( '/^\s*AND\s*/', '', $this->meta_query_clauses['where'] );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $this->query_vars['count'] ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $groupby = "{$wpdb->comments}.comment_ID";
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -677,35 +730,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $this->request = "SELECT $fields FROM $wpdb->comments $join $where $groupby $orderby $limits";
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $this->query_vars['count'] ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        return $wpdb->get_var( $this->request );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 return intval( $wpdb->get_var( $this->request ) );
+               } else {
+                       $comment_ids = $wpdb->get_col( $this->request );
+                       return array_map( 'intval', $comment_ids );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-               if ( 'ids' == $this->query_vars['fields'] ) {
-                       $this->comments = $wpdb->get_col( $this->request );
-                       return array_map( 'intval', $this->comments );
-               }
-
-               $results = $wpdb->get_results( $this->request );
-               /**
-                * Filter the comment query results.
-                *
-                * @since 3.1.0
-                *
-                * @param array            $results  An array of comments.
-                * @param WP_Comment_Query &$this    Current instance of WP_Comment_Query, passed by reference.
-                */
-               $_comments = apply_filters_ref_array( 'the_comments', array( $results, &$this ) );
-
-               // Convert to WP_Comment instances
-               $comments = array_map( 'get_comment', $_comments );
-
-               wp_cache_add( $cache_key, $comments, 'comment' );
-               if ( '*' === $fields ) {
-                       update_comment_cache( $comments, $this->query_vars['update_comment_meta_cache'] );
-               }
-
-               $this->comments = $comments;
-               return $this->comments;
</del><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span></span></pre></div>
<a id="trunksrcwpincludescommentfunctionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/comment-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/comment-functions.php       2015-09-18 19:27:24 UTC (rev 34309)
+++ trunk/src/wp-includes/comment-functions.php 2015-09-18 19:27:39 UTC (rev 34310)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2377,6 +2377,30 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Adds any comments from the given IDs to the cache that do not already exist in cache.
+ *
+ * @since 4.4.0
+ * @access private
+ *
+ * @see update_comment_cache()
+ *
+ * @global wpdb $wpdb
+ *
+ * @param array $comment_ids       Array of comment IDs.
+ * @param bool  $update_meta_cache Optional. Whether to update the meta cache. Default true.
+ */
+function _prime_comment_caches( $comment_ids, $update_meta_cache = true ) {
+       global $wpdb;
+
+       $non_cached_ids = _get_non_cached_ids( $comment_ids, 'comment' );
+       if ( !empty( $non_cached_ids ) ) {
+               $fresh_comments = $wpdb->get_results( sprintf( "SELECT $wpdb->comments.* FROM $wpdb->comments WHERE comment_ID IN (%s)", join( ",", array_map( 'intval', $non_cached_ids ) ) ) );
+
+               update_comment_cache( $fresh_comments, $update_meta_cache );
+       }
+}
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px">  * Lazy load comment meta when inside of a `WP_Query` loop.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 4.4.0
</span></span></pre></div>
<a id="trunktestsphpunittestscommentmetaCachephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/comment/metaCache.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/comment/metaCache.php   2015-09-18 19:27:24 UTC (rev 34309)
+++ trunk/tests/phpunit/tests/comment/metaCache.php     2015-09-18 19:27:39 UTC (rev 34310)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -17,6 +17,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        update_comment_meta( $cid, 'foo', 'bar' );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Clear comment cache, just in case.
+               clean_comment_cache( $comment_ids );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $q = new WP_Comment_Query( array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'post_ID' => $p,
</span><span class="cx" style="display: block; padding: 0 10px">                ) );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -42,6 +45,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        update_comment_meta( $cid, 'foo', 'bar' );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Clear comment cache, just in case.
+               clean_comment_cache( $comment_ids );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $q = new WP_Comment_Query( array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'post_ID' => $p,
</span><span class="cx" style="display: block; padding: 0 10px">                        'update_comment_meta_cache' => true,
</span></span></pre></div>
<a id="trunktestsphpunittestscommentqueryphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/comment/query.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/comment/query.php       2015-09-18 19:27:24 UTC (rev 34309)
+++ trunk/tests/phpunit/tests/comment/query.php 2015-09-18 19:27:39 UTC (rev 34310)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1667,6 +1667,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $q1 = new WP_Comment_Query();
</span><span class="cx" style="display: block; padding: 0 10px">                $q1->query( array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'post_id' => $p,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'fields' => 'ids',
</ins><span class="cx" style="display: block; padding: 0 10px">                 ) );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $num_queries = $wpdb->num_queries;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1674,6 +1675,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $q2 = new WP_Comment_Query();
</span><span class="cx" style="display: block; padding: 0 10px">                $q2->query( array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'post_id' => $p,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'fields' => 'ids',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'foo' => 'bar',
</span><span class="cx" style="display: block; padding: 0 10px">                ) );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre>
</div>
</div>

</body>
</html>