diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 4d35ff5..5a49458 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -182,12 +182,13 @@ function comment_menu_link_defaults_alter(&$links) {
 /**
  * Returns a menu title which includes the number of unapproved comments.
  *
- * @todo Move to the comment manager and replace by a entity query?
+ * @deprecated Deprecated since Drupal 8.x-dev, to be removed in Drupal 8.0.
  */
 function comment_count_unpublished() {
-  $count = db_query('SELECT COUNT(cid) FROM {comment} WHERE status = :status', array(
-    ':status' => CommentInterface::NOT_PUBLISHED,
-  ))->fetchField();
+  $count = \Drupal::entityQuery('comment')
+    ->condition('status', CommentInterface::NOT_PUBLISHED)
+    ->count()
+    ->execute();
   return t('Unapproved comments (@count)', array('@count' => $count));
 }
 
@@ -248,11 +249,11 @@ function comment_field_config_delete(FieldConfigInterface $field) {
 function comment_field_instance_config_delete(FieldInstanceConfigInterface $instance) {
   if ($instance->getType() == 'comment') {
     // Delete all comments that used by the entity bundle.
-    $comments = db_query("SELECT cid FROM {comment} WHERE entity_type = :entity_type AND field_id = :field_id", array(
-      ':entity_type' => $instance->getEntityTypeId(),
-      ':field_id' => $instance->getEntityTypeId() . '__' . $instance->getName(),
-    ))->fetchCol();
-    entity_delete_multiple('comment', $comments);
+    $cids = \Drupal::entityQuery('comment')
+      ->condition('field_id', $instance->getEntityTypeId() . '__' . $instance->getName())
+      ->condition('entity_type', $instance->getEntityTypeId())
+      ->execute();
+    entity_delete_multiple('comment', $cids);
   }
 }
 
@@ -283,80 +284,37 @@ function comment_permission() {
  * Calculates the page number for the first new comment.
  *
  * @param int $num_comments
- *   Number of comments.
- * @param int $new_replies
- *   Number of new replies.
+ *   The total number of comments that the entity has.
+ * @param int $new_comments
+ *   The number of new comments that the entity has.
  * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The first new comment entity.
+ *   The entity that has the comments.
  * @param string $field_name
- *   The field name on the entity to which comments are attached to.
+ *   The field name on the entity to which comments are attached.
  *
  * @return array|null
  *   An array "page=X" if the page number is greater than zero; NULL otherwise.
  */
-function comment_new_page_count($num_comments, $new_replies, EntityInterface $entity, $field_name = 'comment') {
+function comment_new_page_count($num_comments, $new_comments, EntityInterface $entity, $field_name = 'comment') {
   $instance = \Drupal::service('field.info')->getInstance($entity->getEntityTypeId(), $entity->bundle(), $field_name);
-  $mode = $instance->getSetting('default_mode');
   $comments_per_page = $instance->getSetting('per_page');
-  $pagenum = NULL;
-  $flat = $mode == COMMENT_MODE_FLAT ? TRUE : FALSE;
+
   if ($num_comments <= $comments_per_page) {
     // Only one page of comments.
     $pageno = 0;
   }
-  elseif ($flat) {
+  elseif ($instance->getSetting('default_mode') == COMMENT_MODE_FLAT) {
     // Flat comments.
-    $count = $num_comments - $new_replies;
+    $count = $num_comments - $new_comments;
     $pageno = $count / $comments_per_page;
   }
   else {
-    // Threaded comments: we build a query with a subquery to find the first
-    // thread with a new comment.
-
-    // 1. Find all the threads with a new comment.
-    $unread_threads_query = db_select('comment')
-      ->fields('comment', array('thread'))
-      ->condition('entity_id', $entity->id())
-      ->condition('entity_type', $entity->getEntityTypeId())
-      ->condition('field_id', $entity->getEntityTypeId() . '__' . $field_name)
-      ->condition('status', CommentInterface::PUBLISHED)
-      ->orderBy('created', 'DESC')
-      ->orderBy('cid', 'DESC')
-      ->range(0, $new_replies);
-
-    // 2. Find the first thread.
-    $first_thread_query = db_select($unread_threads_query, 'thread');
-    $first_thread_query->addExpression('SUBSTRING(thread, 1, (LENGTH(thread) - 1))', 'torder');
-    $first_thread = $first_thread_query
-      ->fields('thread', array('thread'))
-      ->orderBy('torder')
-      ->range(0, 1)
-      ->execute()
-      ->fetchField();
-
-    // Remove the final '/'.
-    $first_thread = substr($first_thread, 0, -1);
-
-    // Find the number of the first comment of the first unread thread.
-    $count = db_query('SELECT COUNT(*) FROM {comment} WHERE entity_id = :entity_id
-                      AND entity_type = :entity_type
-                      AND field_id = :field_id
-                      AND status = :status AND SUBSTRING(thread, 1, (LENGTH(thread) - 1)) < :thread', array(
-      ':status' => CommentInterface::PUBLISHED,
-      ':entity_id' => $entity->id(),
-      ':field_id' => $entity->getEntityTypeId() . '__' . $field_name,
-      ':entity_type' => $entity->getEntityTypeId(),
-      ':thread' => $first_thread,
-    ))->fetchField();
-
-    $pageno = $count / $comments_per_page;
-  }
-
-  if ($pageno >= 1) {
-    $pagenum = array('page' => intval($pageno));
+    // Threaded comments.
+    $pageno = \Drupal::entityManager()->getStorage('comment')
+      ->getThreadedNewCommentPageNr($entity, $new_comments, $comments_per_page, $field_name);
   }
 
-  return $pagenum;
+  return $pageno ? array('page' => $pageno) : NULL;
 }
 
 /**
@@ -586,107 +544,13 @@ function comment_add(EntityInterface $entity, $field_name = 'comment', $pid = NU
  * @return int[]
  *   An array of the IDs of the comment to be displayed.
  *
- * To display threaded comments in the correct order we keep a 'thread' field
- * and order by that value. This field keeps this data in
- * a way which is easy to update and convenient to use.
- *
- * A "thread" value starts at "1". If we add a child (A) to this comment,
- * we assign it a "thread" = "1.1". A child of (A) will have "1.1.1". Next
- * brother of (A) will get "1.2". Next brother of the parent of (A) will get
- * "2" and so on.
- *
- * First of all note that the thread field stores the depth of the comment:
- * depth 0 will be "X", depth 1 "X.X", depth 2 "X.X.X", etc.
- *
- * Now to get the ordering right, consider this example:
- *
- * 1
- * 1.1
- * 1.1.1
- * 1.2
- * 2
- *
- * If we "ORDER BY thread ASC" we get the above result, and this is the
- * natural order sorted by time. However, if we "ORDER BY thread DESC"
- * we get:
- *
- * 2
- * 1.2
- * 1.1.1
- * 1.1
- * 1
- *
- * Clearly, this is not a natural way to see a thread, and users will get
- * confused. The natural order to show a thread by time desc would be:
- *
- * 2
- * 1
- * 1.2
- * 1.1
- * 1.1.1
- *
- * which is what we already did before the standard pager patch. To achieve
- * this we simply add a "/" at the end of each "thread" value. This way, the
- * thread fields will look like this:
- *
- * 1/
- * 1.1/
- * 1.1.1/
- * 1.2/
- * 2/
- *
- * we add "/" since this char is, in ASCII, higher than every number, so if
- * now we "ORDER BY thread DESC" we get the correct order. However this would
- * spoil the reverse ordering, "ORDER BY thread ASC" -- here, we do not need
- * to consider the trailing "/" so we use a substring only.
+ * @deprecated Deprecated since Drupal 8.x-dev, to be removed in Drupal 8.0.
+ *   Use \Drupal::entityManager()->getStorage('comment')->loadThread() instead,
+ *   which returns objects instead of IDs.
  */
 function comment_get_thread(EntityInterface $entity, $field_name, $mode, $comments_per_page, $pager_id = 0) {
-  $query = db_select('comment', 'c')
-    ->extend('Drupal\Core\Database\Query\PagerSelectExtender');
-  if ($pager_id) {
-    $query->element($pager_id);
-  }
-  $query->addField('c', 'cid');
-  $query
-    ->condition('c.entity_id', $entity->id())
-    ->condition('c.entity_type', $entity->getEntityTypeId())
-    ->condition('c.field_id', $entity->getEntityTypeId() . '__' . $field_name)
-    ->addTag('entity_access')
-    ->addTag('comment_filter')
-    ->addMetaData('base_table', 'comment')
-    ->addMetaData('entity', $entity)
-    ->addMetaData('field_name', $field_name)
-    ->limit($comments_per_page);
-
-  $count_query = db_select('comment', 'c');
-  $count_query->addExpression('COUNT(*)');
-  $count_query
-    ->condition('c.entity_id', $entity->id())
-    ->condition('c.entity_type', $entity->getEntityTypeId())
-    ->condition('c.field_id', $entity->getEntityTypeId() . '__' . $field_name)
-    ->addTag('entity_access')
-    ->addTag('comment_filter')
-    ->addMetaData('base_table', 'comment')
-    ->addMetaData('entity', $entity)
-    ->addMetaData('field_name', $field_name);
-
-  if (!user_access('administer comments')) {
-    $query->condition('c.status', CommentInterface::PUBLISHED);
-    $count_query->condition('c.status', CommentInterface::PUBLISHED);
-  }
-  if ($mode == COMMENT_MODE_FLAT) {
-    $query->orderBy('c.cid', 'ASC');
-  }
-  else {
-    // See comment above. Analysis reveals that this doesn't cost too
-    // much. It scales much much better than having the whole comment
-    // structure.
-    $query->addExpression('SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))', 'torder');
-    $query->orderBy('torder', 'ASC');
-  }
-
-  $query->setCountQuery($count_query);
-  return $query->execute()->fetchCol();
+  $nodes = \Drupal::entityManager()->getStorage('comment')->loadThread($entity, $field_name, $mode, $comments_per_page, $pager_id);
+  return array_keys($nodes);
 }
 
 /**
@@ -898,12 +762,10 @@ function comment_entity_predelete(EntityInterface $entity) {
   // entity type that has an integer ID, $entity->id() might be a string
   // containing a number), and then cast it to an integer when querying.
   if ($entity->getEntityType()->isFieldable() && is_numeric($entity->id())) {
-    $cids = db_select('comment', 'c')
-      ->fields('c', array('cid'))
+    $cids = \Drupal::entityQuery('comment')
       ->condition('entity_id', (int) $entity->id())
       ->condition('entity_type', $entity->getEntityTypeId())
-      ->execute()
-      ->fetchCol();
+      ->execute();
     entity_delete_multiple('comment', $cids);
     \Drupal::service('comment.statistics')->delete($entity);
   }
@@ -949,8 +811,9 @@ function comment_node_update_index(EntityInterface $node, $langcode) {
       $instance = \Drupal::service('field.info')->getInstance('node', $node->getType(), $field_name);
       $mode = $instance->getSetting('default_mode');
       $comments_per_page = $instance->getSetting('per_page');
-      if ($node->get($field_name)->status && $cids = comment_get_thread($node, $field_name, $mode, $comments_per_page)) {
-        $comments = entity_load_multiple('comment', $cids);
+      if ($node->get($field_name)->status
+          && $comments = \Drupal::entityManager()->getStorage('comment')
+               ->loadThread($node, $field_name, $mode, $comments_per_page)) {
         comment_prepare_thread($comments);
         $build = comment_view_multiple($comments);
         $return .= drupal_render($build);
@@ -1029,7 +892,10 @@ function comment_user_cancel($edit, $account, $method) {
  * Implements hook_user_predelete().
  */
 function comment_user_predelete($account) {
-  $cids = db_query('SELECT c.cid FROM {comment} c WHERE uid = :uid', array(':uid' => $account->id()))->fetchCol();
+  $cids = \Drupal::entityQuery('comment')
+    ->condition('uid', $account->id())
+    ->condition('status', CommentInterface::PUBLISHED)
+    ->execute();
   entity_delete_multiple('comment', $cids);
 }
 
@@ -1106,19 +972,19 @@ function comment_num_new($entity_id, $entity_type, $field_name = NULL, $timestam
     $timestamp = ($timestamp > HISTORY_READ_LIMIT ? $timestamp : HISTORY_READ_LIMIT);
 
     // Use the timestamp to retrieve the number of new comments.
-    $query = db_select('comment', 'c');
-    $query->addExpression('COUNT(cid)');
-    $query->condition('c.entity_type', $entity_type)
-      ->condition('c.entity_id', $entity_id)
-      ->condition('c.status', CommentInterface::PUBLISHED)
-      ->condition('c.created', $timestamp, '>');
+    $query = \Drupal::entityQuery('comment')
+      ->condition('entity_type', $entity_type)
+      ->condition('entity_id', $entity_id)
+      ->condition('created', $timestamp, '>')
+      ->condition('status', CommentInterface::PUBLISHED);
+
     if ($field_name) {
       // Limit to a particular field.
-      $query->condition('c.field_id', $entity_type . '__' . $field_name);
+      $query->condition('field_id', $entity_type . '__' . $field_name);
     }
 
-    return $query->execute()
-      ->fetchField();
+    return $query->count()
+      ->execute();
   }
   else {
     return FALSE;
@@ -1140,34 +1006,13 @@ function comment_num_new($entity_id, $entity_type, $field_name = NULL, $timestam
  * @return int
  *   The display ordinal for the comment.
  *
- * @see comment_get_display_page()
- * @see field_info_instance().
+ * @deprecated Deprecated since Drupal 8.x-dev, to be removed in Drupal 8.0.
+ *   Use \Drupal::entityManager()->getStorage('comment')->getDisplayOrdinal()
+ *   instead. (Note 2nd argument is different).
  */
 function comment_get_display_ordinal($cid, $instance) {
-  // Count how many comments (c1) are before $cid (c2) in display order. This is
-  // the 0-based display ordinal.
-  $query = db_select('comment', 'c1');
-  $query->innerJoin('comment', 'c2', 'c2.entity_id = c1.entity_id AND c2.entity_type = c1.entity_type AND c2.field_id = c1.field_id');
-  $query->addExpression('COUNT(*)', 'count');
-  $query->condition('c2.cid', $cid);
-  if (!user_access('administer comments')) {
-    $query->condition('c1.status', CommentInterface::PUBLISHED);
-  }
-
-  if ($instance->getSetting('default_mode') == COMMENT_MODE_FLAT) {
-    // For flat comments, cid is used for ordering comments due to
-    // unpredictable behavior with timestamp, so we make the same assumption
-    // here.
-    $query->condition('c1.cid', $cid, '<');
-  }
-  else {
-    // For threaded comments, the c.thread column is used for ordering. We can
-    // use the sorting code for comparison, but must remove the trailing slash.
-    // See CommentViewBuilder.
-    $query->where('SUBSTRING(c1.thread, 1, (LENGTH(c1.thread) -1)) < SUBSTRING(c2.thread, 1, (LENGTH(c2.thread) -1))');
-  }
-
-  return $query->execute()->fetchField();
+  return \Drupal::entityManager()->getStorage('comment')
+    ->getDisplayOrdinal($cid, $instance->getSetting('default_mode'));
 }
 
 /**
diff --git a/core/modules/comment/lib/Drupal/comment/CommentStorage.php b/core/modules/comment/lib/Drupal/comment/CommentStorage.php
index f59a2be..7f79673 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentStorage.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentStorage.php
@@ -92,7 +92,7 @@ public function updateEntityStatistics(CommentInterface $comment) {
   /**
    * {@inheritdoc}
    */
-  public function getMaxThread(EntityInterface $comment) {
+  public function getMaxThread(CommentInterface $comment) {
     $query = $this->database->select('comment', 'c')
       ->condition('entity_id', $comment->getCommentedEntityId())
       ->condition('field_id', $comment->getFieldId())
@@ -105,7 +105,7 @@ public function getMaxThread(EntityInterface $comment) {
   /**
    * {@inheritdoc}
    */
-  public function getMaxThreadPerThread(EntityInterface $comment) {
+  public function getMaxThreadPerThread(CommentInterface $comment) {
     $query = $this->database->select('comment', 'c')
       ->condition('entity_id', $comment->getCommentedEntityId())
       ->condition('field_id', $comment->getFieldId())
@@ -119,6 +119,36 @@ public function getMaxThreadPerThread(EntityInterface $comment) {
   /**
    * {@inheritdoc}
    */
+  public function getDisplayOrdinal($cid, $comment_mode) {
+    // Count how many comments (c1) are before $cid (c2) in display order. This
+    // is the 0-based display ordinal.
+    $query = $this->database->select('comment', 'c1');
+    $query->innerJoin('comment', 'c2', 'c2.entity_id = c1.entity_id AND c2.entity_type = c1.entity_type AND c2.field_id = c1.field_id');
+    $query->addExpression('COUNT(*)', 'count');
+    $query->condition('c2.cid', $cid);
+    if (!user_access('administer comments')) {
+      $query->condition('c1.status', CommentInterface::PUBLISHED);
+    }
+
+    if ($comment_mode == COMMENT_MODE_FLAT) {
+      // For rendering flat comments, cid is used for ordering comments due to
+      // unpredictable behavior with timestamp, so we make the same assumption
+      // here.
+      $query->condition('c1.cid', $cid, '<');
+    }
+    else {
+      // For threaded comments, the c.thread column is used for ordering. We can
+      // use the sorting code for comparison, but must remove the trailing
+      // slash.
+      $query->where('SUBSTRING(c1.thread, 1, (LENGTH(c1.thread) - 1)) < SUBSTRING(c2.thread, 1, (LENGTH(c2.thread) - 1))');
+    }
+
+    return $query->execute()->fetchField();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getChildCids(array $comments) {
     return $this->database->select('comment', 'c')
       ->fields('c', array('cid'))
@@ -127,4 +157,169 @@ public function getChildCids(array $comments) {
       ->fetchCol();
   }
 
+  /**
+   * {@inheritdoc}
+   *
+   * To display threaded comments in the correct order we keep a 'thread' field
+   * and order by that value. This field keeps this data in
+   * a way which is easy to update and convenient to use.
+   *
+   * A "thread" value starts at "1". If we add a child (A) to this comment,
+   * we assign it a "thread" = "1.1". A child of (A) will have "1.1.1". Next
+   * brother of (A) will get "1.2". Next brother of the parent of (A) will get
+   * "2" and so on.
+   *
+   * First of all note that the thread field stores the depth of the comment:
+   * depth 0 will be "X", depth 1 "X.X", depth 2 "X.X.X", etc.
+   *
+   * Now to get the ordering right, consider this example:
+   *
+   * 1
+   * 1.1
+   * 1.1.1
+   * 1.2
+   * 2
+   *
+   * If we "ORDER BY thread ASC" we get the above result, and this is the
+   * natural order sorted by time. However, if we "ORDER BY thread DESC"
+   * we get:
+   *
+   * 2
+   * 1.2
+   * 1.1.1
+   * 1.1
+   * 1
+   *
+   * Clearly, this is not a natural way to see a thread, and users will get
+   * confused. The natural order to show a thread by time desc would be:
+   *
+   * 2
+   * 1
+   * 1.2
+   * 1.1
+   * 1.1.1
+   *
+   * which is what we already did before the standard pager patch. To achieve
+   * this we simply add a "/" at the end of each "thread" value. This way, the
+   * thread fields will look like this:
+   *
+   * 1/
+   * 1.1/
+   * 1.1.1/
+   * 1.2/
+   * 2/
+   *
+   * we add "/" since this char is, in ASCII, higher than every number, so if
+   * now we "ORDER BY thread DESC" we get the correct order. However this would
+   * spoil the reverse ordering, "ORDER BY thread ASC" -- here, we do not need
+   * to consider the trailing "/" so we use a substring only.
+   */
+  public function loadThread(EntityInterface $entity, $field_name, $mode, $comments_per_page = 0, $pager_id = 0) {
+    $query = $this->database->select('comment', 'c');
+    if ($comments_per_page > 0) {
+      $query = $query->extend('Drupal\Core\Database\Query\PagerSelectExtender')
+        ->limit($comments_per_page);
+      if ($pager_id) {
+        $query->element($pager_id);
+      }
+    }
+    $query->addField('c', 'cid');
+    $query
+      ->condition('c.entity_id', $entity->id())
+      ->condition('c.entity_type', $entity->getEntityTypeId())
+      ->condition('c.field_id', $entity->getEntityTypeId() . '__' . $field_name)
+      ->addTag('entity_access')
+      ->addTag('comment_filter')
+      ->addMetaData('base_table', 'comment')
+      ->addMetaData('entity', $entity)
+      ->addMetaData('field_name', $field_name);
+
+    $count_query = $this->database->select('comment', 'c');
+    $count_query->addExpression('COUNT(*)');
+    $count_query
+      ->condition('c.entity_id', $entity->id())
+      ->condition('c.entity_type', $entity->getEntityTypeId())
+      ->condition('c.field_id', $entity->getEntityTypeId() . '__' . $field_name)
+      ->addTag('entity_access')
+      ->addTag('comment_filter')
+      ->addMetaData('base_table', 'comment')
+      ->addMetaData('entity', $entity)
+      ->addMetaData('field_name', $field_name);
+
+    if (!\Drupal::currentUser()->hasPermission('administer comments')) {
+      $query->condition('c.status', CommentInterface::PUBLISHED);
+      $count_query->condition('c.status', CommentInterface::PUBLISHED);
+    }
+    if ($mode == COMMENT_MODE_FLAT) {
+      $query->orderBy('c.cid', 'ASC');
+    }
+    else {
+      // See comment above. Analysis reveals that this doesn't cost too
+      // much. It scales much much better than having the whole comment
+      // structure.
+      $query->addExpression('SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))', 'torder');
+      $query->orderBy('torder', 'ASC');
+    }
+
+    $query->setCountQuery($count_query);
+    $cids = $query->execute()->fetchCol();
+
+    $comments = array();
+    if ($cids) {
+      $comments = $this->loadMultiple($cids);
+    }
+
+    return $comments;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCommentingUserIds(EntityInterface $entity) {
+    return $this->database->query('SELECT DISTINCT c.uid FROM {comment} c WHERE entity_id = :id AND entity_type=:type', array(':id' => $entity->id(), ':type' => $entity->getEntityTypeId()))->fetchCol();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getThreadedNewCommentPageNr(EntityInterface $entity, $new_replies, $comments_per_page, $field_name) {
+    // 1. Find all the threads with a new comment.
+    $unread_threads_query = $this->database->select('comment')
+      ->fields('comment', array('thread'))
+      ->condition('entity_id', $entity->id())
+      ->condition('entity_type', $entity->getEntityTypeId())
+      ->condition('field_id', $entity->getEntityTypeId() . '__' . $field_name)
+      ->condition('status', CommentInterface::PUBLISHED)
+      ->orderBy('created', 'DESC')
+      ->orderBy('cid', 'DESC')
+      ->range(0, $new_replies);
+
+    // 2. Find the first thread.
+    $first_thread_query = $this->database->select($unread_threads_query, 'thread');
+    $first_thread_query->addExpression('SUBSTRING(thread, 1, (LENGTH(thread) - 1))', 'torder');
+    $first_thread = $first_thread_query
+      ->fields('thread', array('thread'))
+      ->orderBy('torder')
+      ->range(0, 1)
+      ->execute()
+      ->fetchField();
+
+    // Remove the final '/'.
+    $first_thread = substr($first_thread, 0, -1);
+
+    // Find the number of the first comment of the first unread thread.
+    $count = $this->database->query('SELECT COUNT(*) FROM {comment} WHERE entity_id = :entity_id
+                      AND entity_type = :entity_type
+                      AND field_id = :field_id
+                      AND status = :status AND SUBSTRING(thread, 1, (LENGTH(thread) - 1)) < :thread', array(
+      ':status' => CommentInterface::PUBLISHED,
+      ':entity_id' => $entity->id(),
+      ':field_id' => $entity->getEntityTypeId() . '__' . $field_name,
+      ':entity_type' => $entity->getEntityTypeId(),
+      ':thread' => $first_thread,
+    ))->fetchField();
+
+    return floor($count / $comments_per_page);
+  }
+
 }
diff --git a/core/modules/comment/lib/Drupal/comment/CommentStorageInterface.php b/core/modules/comment/lib/Drupal/comment/CommentStorageInterface.php
index c1587a5..f2c0cb5 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentStorageInterface.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentStorageInterface.php
@@ -16,54 +16,102 @@
 interface CommentStorageInterface extends EntityStorageInterface {
 
   /**
-   * Get the maximum encoded thread value for the top level comments.
+   * Gets the maximum encoded thread value for the top level comments.
    *
-   * @param EntityInterface $comment
+   * @param \Drupal\comment\CommentInterface $comment
    *   A comment entity.
    *
    * @return string
    *   The maximum encoded thread value among the top level comments of the
    *   node $comment belongs to.
    */
-  public function getMaxThread(EntityInterface $comment);
+  public function getMaxThread(CommentInterface $comment);
 
   /**
-   * Get the maximum encoded thread value for the children of this comment.
+   * Gets the maximum encoded thread value for the children of this comment.
    *
-   * @param EntityInterface $comment
+   * @param \Drupal\comment\CommentInterface $comment
    *   A comment entity.
    *
    * @return string
    *   The maximum encoded thread value among all replies of $comment.
    */
-  public function getMaxThreadPerThread(EntityInterface $comment);
+  public function getMaxThreadPerThread(CommentInterface $comment);
+
+  /**
+   * Gets the 0-based display ordinal for a comment (i.e. the number of comments
+   * which should be displayed before the given comment).
+   *
+   * @param int $cid
+   *   The comment ID.
+   * @param int $comment_mode
+   *   Comment mode (COMMENT_MODE_FLAT or COMMENT_MODE_THREADED).
+   *
+   * @return int
+   *   The display ordinal for the comment.
+   */
+  public function getDisplayOrdinal($cid, $comment_mode);
 
   /**
    * Gets the comment ids of the passed comment entities' children.
    *
-   * @param array $comments
+   * @param \Drupal\comment\CommentInterface[] $comments
    *   An array of comment entities keyed by their ids.
+   *
    * @return array
-   *   The entity ids of the passed comment entities' children as an array.
+   *   Array of entity ids of the passed comment entities' children.
    */
   public function getChildCids(array $comments);
 
   /**
-   * Updates the comment statistics for a given node.
+   * Retrieves comments for a thread, sorted in order suitable for display: most
+   * recent comments first.
    *
-   * The {comment_entity_statistics} table has the following fields:
-   * - last_comment_timestamp: The timestamp of the last comment for the entity,
-   *   or the entity created timestamp if no comments exist for the entity.
-   * - last_comment_name: The name of the anonymous poster for the last comment.
-   * - last_comment_uid: The user ID of the poster for the last comment for
-   *   this entity, or the entity author's user ID if no comments exist for the
-   *   entity.
-   * - comment_count: The total number of approved/published comments on this
-   *   entity.
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity whose comment(s) needs rendering.
+   * @param string $field_name
+   *   The field_name whose comment(s) needs rendering.
+   * @param int $mode
+   *   The comment display mode; COMMENT_MODE_FLAT or COMMENT_MODE_THREADED.
+   * @param int $comments_per_page
+   *   (optional) The amount of comments to display per page.
+   *   Defaults to 0, which means show all comments.
+   * @param int $pager_id
+   *   (optional) Pager id to use in case of multiple pagers on the one page.
+   *   Defaults to 0; is only used when $comments_per_page is greater than zero.
    *
-   * @param \Drupal\comment\CommentInterface $comment
-   *   The comment being saved.
+   * @return array
+   *   Ordered array of comment objects.
+   */
+  public function loadThread(EntityInterface $entity, $field_name, $mode, $comments_per_page = 0, $pager_id = 0);
+
+  /**
+   * Retrieves UIDs of users that commented on a given node.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $node
+   *   User object.
+   *
+   * @return int[]
+   *   Array of user ids.
+   */
+  public function getCommentingUserIds(EntityInterface $node);
+
+  /**
+   * Gets the page number (location) of the first unread comment, if displaying
+   * as a threaded list.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $node
+   *   Node object.
+   * @param int $new_replies
+   *   Number of new replies.
+   * @param int $comments_per_page
+   *   Number of comments per page.
+   * @param string $field_name
+   *   The field name on the entity to which comments are attached.
+   *
+   * @return int
+   *   The page number (0-based).
    */
-  public function updateEntityStatistics(CommentInterface $comment);
+  public function getThreadedNewCommentPageNr(EntityInterface $node, $new_replies, $comments_per_page, $field_name);
 
 }
diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php b/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php
index 19dd00f..809aa5d 100644
--- a/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php
+++ b/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php
@@ -137,8 +137,9 @@ public function viewElements(FieldItemListInterface $items) {
         $this->currentUser->hasPermission('administer comments'))) {
         $mode = $comment_settings['default_mode'];
         $comments_per_page = $comment_settings['per_page'];
-        if ($cids = comment_get_thread($entity, $field_name, $mode, $comments_per_page, $this->getSetting('pager_id'))) {
-          $comments = $this->storage->loadMultiple($cids);
+        $comments = \Drupal::entityManager()->getStorage('comment')
+          ->loadThread($entity, $field_name, $mode, $comments_per_page, $this->getSetting('pager_id'));
+        if ($comments) {
           comment_prepare_thread($comments);
           $build = $this->viewBuilder->viewMultiple($comments);
           $build['pager']['#theme'] = 'pager';
diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/Menu/LocalTask/UnapprovedComments.php b/core/modules/comment/lib/Drupal/comment/Plugin/Menu/LocalTask/UnapprovedComments.php
index c7a1ff7..6063fa6 100644
--- a/core/modules/comment/lib/Drupal/comment/Plugin/Menu/LocalTask/UnapprovedComments.php
+++ b/core/modules/comment/lib/Drupal/comment/Plugin/Menu/LocalTask/UnapprovedComments.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\comment\Plugin\Menu\LocalTask;
 
+use Drupal\comment\CommentInterface;
 use Drupal\Core\Menu\LocalTaskDefault;
 
 /**
@@ -18,7 +19,11 @@ class UnapprovedComments extends LocalTaskDefault {
    * {@inheritdoc}
    */
   public function getTitle() {
-    return comment_count_unpublished();
+    $count = \Drupal::entityQuery('comment')
+      ->condition('status', CommentInterface::NOT_PUBLISHED)
+      ->count()
+      ->execute();
+    return t('Unapproved comments (@count)', array('@count' => $count));
   }
 
 }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
index 139bf73..4c6cdb9 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
@@ -116,16 +116,13 @@ function testCommentLanguage() {
         $this->drupalPostForm(NULL, $edit, t('Save'));
 
         // Check that comment language matches the current content language.
-        $cid = db_select('comment', 'c')
-          ->fields('c', array('cid'))
-          ->condition('entity_id', $node->id())
-          ->condition('entity_type', 'node')
+        $cids = \Drupal::entityQuery('comment')
+          ->condition('entity_id', (int) $node->id())
           ->condition('field_id', 'node__comment')
-          ->orderBy('cid', 'DESC')
+          ->sort('cid', 'DESC')
           ->range(0, 1)
-          ->execute()
-          ->fetchField();
-        $comment = comment_load($cid);
+          ->execute();
+        $comment = comment_load(reset($cids));
         $args = array('%node_language' => $node_langcode, '%comment_language' => $comment->langcode->value, '%langcode' => $langcode);
         $this->assertEqual($comment->langcode->value, $langcode, format_string('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args));
         $this->assertEqual($comment->comment_body->value, $comment_values[$node_langcode][$langcode], 'Comment body correctly stored.');
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php
index cf7af82..a5c5048 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php
@@ -159,7 +159,7 @@ function setEnvironment(array $info) {
         $this->comment = $comment;
       }
       else {
-        $cids = db_query("SELECT cid FROM {comment}")->fetchCol();
+        $cids = \Drupal::entityQuery('comment')->execute();
         entity_delete_multiple('comment', $cids);
         unset($this->comment);
       }
diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module
index 96aecbf..bb93d27 100644
--- a/core/modules/tracker/tracker.module
+++ b/core/modules/tracker/tracker.module
@@ -38,6 +38,8 @@ function tracker_help($path, $arg) {
  * 'tracker.index_nid' is set to ((the last node ID that was indexed) - 1) and
  * used to select the nodes to be processed. If there are no remaining nodes to
  * process, 'tracker.index_nid' will be 0.
+ * This process does not run regularly on live sites, rather it updates tracking
+ * info once on an existing site just after the tracker module was installed.
  */
 function tracker_cron() {
   $state = \Drupal::state();
@@ -82,24 +84,25 @@ function tracker_cron() {
         ))
         ->execute();
 
-      $query = db_select('comment', 'c', array('target' => 'slave'));
-      // Force PostgreSQL to do an implicit cast by adding 0.
-      $query->addExpression('0 + :changed', 'changed', array(':changed' => $changed));
-      $query->addField('c', 'status', 'published');
-      $query->addField('c', 'entity_id', 'nid');
-      $query
-        ->distinct()
-        ->fields('c', array('uid'))
-        ->condition('c.entity_id', $row->nid)
-        ->condition('c.entity_type', 'node')
-        ->condition('c.uid', $row->uid, '<>')
-        ->condition('c.status', CommentInterface::PUBLISHED);
+      $node = \Drupal::entityManager()->getStorage('node')->load($row->nid);
+      $uids = \Drupal::entityManager()->getStorage('comment')->getCommentingUserIds($node);
 
       // Insert the user-level data for the commenters (except if a commenter
       // is the node's author).
-      db_insert('tracker_user')
-        ->from($query)
-        ->execute();
+      if ($uids) {
+        $query = db_insert('tracker_user');
+        foreach ($uids as $uid) {
+          if ($uid != $row->uid) {
+            $query->fields(array(
+              'uid' => $uid,
+              'nid' => $row->nid,
+              'published' => CommentInterface::PUBLISHED,
+              'changed' => $changed,
+            ));
+          }
+        }
+        $query->execute();
+      }
 
       // Note that we have indexed at least one node.
       $last_nid = $row->nid;
@@ -284,12 +287,21 @@ function _tracker_calculate_changed($nid) {
   // @todo This should be actually filtering on the desired language and just
   //   fall back to the default language.
   $changed = db_query('SELECT changed FROM {node_field_data} WHERE nid = :nid AND default_langcode = 1 ORDER BY changed DESC', array(':nid' => $nid), array('target' => 'slave'))->fetchField();
-  $latest_comment = db_query_range("SELECT cid, changed FROM {comment} WHERE entity_type = 'node' AND entity_id = :nid AND status = :status ORDER BY changed DESC", 0, 1, array(
-    ':nid' => $nid,
-    ':status' => CommentInterface::PUBLISHED,
-  ), array('target' => 'slave'))->fetchObject();
-  if ($latest_comment && $latest_comment->changed > $changed) {
-    $changed = $latest_comment->changed;
+
+  $latest_comment_cid = \Drupal::entityQuery('comment')
+    ->condition('entity_id', $nid)
+    ->condition('entity_type', 'node')
+    ->condition('status', CommentInterface::PUBLISHED)
+    ->sort('changed', 'DESC')
+    ->range(0, 1)
+    ->execute();
+  $latest_comment_cid = reset($latest_comment_cid);
+
+  if ($latest_comment_cid) {
+    $latest_comment = \Drupal::entityManager()->getStorage('comment')->load($latest_comment_cid);
+    if ($latest_comment && $latest_comment->getChangedTime() > $changed) {
+      $changed = $latest_comment->getChangedTime();
+    }
   }
   return $changed;
 }
@@ -317,11 +329,15 @@ function _tracker_remove($nid, $uid = NULL, $changed = NULL) {
     // Or if they have commented on the node.
     if (!$keep_subscription) {
       // Check if the user has commented at least once on the given nid.
-      $keep_subscription = db_query_range("SELECT COUNT(*) FROM {comment} WHERE entity_type = 'node' AND entity_id = :nid AND uid = :uid AND status = :status", 0, 1, array(
-        ':nid' => $nid,
-        ':uid' => $uid,
-        ':status' => CommentInterface::PUBLISHED,
-      ))->fetchField();
+
+      $keep_subscription = \Drupal::entityQuery('comment')
+        ->condition('status', CommentInterface::PUBLISHED)
+        ->condition('uid', $uid)
+        ->condition('entity_id', $nid)
+        ->condition('entity_type', 'node')
+        ->range(0, 1)
+        ->count()
+        ->execute();
     }
 
     // If we haven't found a reason to keep the user's subscription, delete it.
