diff --git a/core/lib/Drupal/Core/Entity/Query/ConditionAggregateBase.php b/core/lib/Drupal/Core/Entity/Query/ConditionAggregateBase.php
new file mode 100644
index 0000000..ce9f1c7
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Query/ConditionAggregateBase.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Query\ConditionAggregateBase.
+ */
+
+namespace Drupal\Core\Entity\Query;
+
+abstract class ConditionAggregateBase extends ConditionFundamentals implements ConditionAggregateInterface {
+
+  /**
+   * Implements \Drupal\Core\Entity\Query\ConditionAggregateInterface::condition().
+   */
+  public function condition($field, $function = NULL, $value = NULL, $operator = NULL, $langcode = NULL) {
+    $this->conditions[] = array(
+      'field' => $field,
+      'function' => $function,
+      'value' => $value,
+      'operator' => $operator,
+      'langcode' => $langcode,
+    );
+
+    return $this;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Query/ConditionInterface.php b/core/lib/Drupal/Core/Entity/Query/ConditionAggregateInterface.php
similarity index 70%
copy from core/lib/Drupal/Core/Entity/Query/ConditionInterface.php
copy to core/lib/Drupal/Core/Entity/Query/ConditionAggregateInterface.php
index d55cced..b3eb64b 100644
--- a/core/lib/Drupal/Core/Entity/Query/ConditionInterface.php
+++ b/core/lib/Drupal/Core/Entity/Query/ConditionAggregateInterface.php
@@ -2,15 +2,15 @@
 
 /**
  * @file
- * Contains \Drupal\Core\Entity\ConditionInterface.
+ * Contains \Drupal\Core\Entity\Query\ConditionAggregateInterface.
  */
 
 namespace Drupal\Core\Entity\Query;
 
 /**
- * Defines the entity query condition interface.
+ * Defines aggregated entity query conditions.
  */
-interface ConditionInterface {
+interface ConditionAggregateInterface {
 
   /**
    * Gets the current conjunction.
@@ -32,14 +32,17 @@ public function count();
   /**
    * Adds a condition.
    *
-   * @param string $field
+   * @param string|ConditionAggregateInterface $field
+   * @param string $function
    * @param mixed $value
    * @param string $operator
    * @param string $langcode
-   * @return ConditionInterface
+   *
+   * @return \Drupal\Core\Entity\Query\ConditionAggregateInterface
+   *   The called object.
    * @see \Drupal\Core\Entity\Query\QueryInterface::condition()
    */
-  public function condition($field, $value = NULL, $operator = NULL, $langcode = NULL);
+  public function condition($field, $function = NULL, $value = NULL, $operator = NULL, $langcode = NULL);
 
   /**
    * Queries for the existence of a field.
@@ -49,16 +52,16 @@ public function condition($field, $value = NULL, $operator = NULL, $langcode = N
    * @return ConditionInterface
    * @see \Drupal\Core\Entity\Query\QueryInterface::exists()
    */
-  public function exists($field, $langcode = NULL);
+  public function exists($field, $function, $langcode = NULL);
 
   /**
-   * Queries for the existence of a field.
+   * Queries for the nonexistence of a field.
    *
    * @param string $field
    * @return ConditionInterface;
    * @see \Drupal\Core\Entity\Query\QueryInterface::notexists()
    */
-  public function notExists($field, $langcode = NULL);
+  public function notExists($field, $function, $langcode = NULL);
 
   /**
    * Gets a complete list of all conditions in this conditional clause.
@@ -77,4 +80,5 @@ public function &conditions();
    *   The query object this conditional clause belongs to.
    */
   public function compile($query);
+
 }
diff --git a/core/lib/Drupal/Core/Entity/Query/ConditionBase.php b/core/lib/Drupal/Core/Entity/Query/ConditionBase.php
index 2c428a4..4ef756b 100644
--- a/core/lib/Drupal/Core/Entity/Query/ConditionBase.php
+++ b/core/lib/Drupal/Core/Entity/Query/ConditionBase.php
@@ -7,41 +7,8 @@
 
 namespace Drupal\Core\Entity\Query;
 
-/**
- * Common code for all implementations of the entity query condition interface.
- */
-abstract class ConditionBase implements ConditionInterface {
 
-  /**
-   * Array of conditions.
-   *
-   * @var array
-   */
-  protected $conditions = array();
-
-  /**
-   * Constructs a Condition object.
-   *
-   * @param string $conjunction
-   *   The operator to use to combine conditions: 'AND' or 'OR'.
-   */
-  public function __construct($conjunction = 'AND') {
-    $this->conjunction = $conjunction;
-  }
-
-  /**
-   * Implements \Drupal\Core\Entity\Query\ConditionInterface::getConjunction().
-   */
-  public function getConjunction() {
-    return $this->conjunction;
-  }
-
-  /**
-   * Implements \Countable::count().
-   */
-  public function count() {
-    return count($this->conditions) - 1;
-  }
+abstract class ConditionBase extends ConditionFundamentals implements ConditionInterface {
 
   /**
    * Implements \Drupal\Core\Entity\Query\ConditionInterface::compile().
@@ -56,23 +23,4 @@ public function condition($field, $value = NULL, $operator = NULL, $langcode = N
 
     return $this;
   }
-
-  /**
-   * Implements \Drupal\Core\Entity\Query\ConditionInterface::conditions().
-   */
-  public function &conditions() {
-    return $this->conditions;
-  }
-
-  /**
-   * Makes sure condition groups are cloned as well.
-   */
-  function __clone() {
-    foreach ($this->conditions as $key => $condition) {
-      if ($condition['field'] instanceOf ConditionInterface) {
-        $this->conditions[$key]['field'] = clone($condition['field']);
-      }
-    }
-  }
-
-}
+}
\ No newline at end of file
diff --git a/core/lib/Drupal/Core/Entity/Query/ConditionBase.php b/core/lib/Drupal/Core/Entity/Query/ConditionFundamentals.php
similarity index 70%
copy from core/lib/Drupal/Core/Entity/Query/ConditionBase.php
copy to core/lib/Drupal/Core/Entity/Query/ConditionFundamentals.php
index 2c428a4..90cb2a0 100644
--- a/core/lib/Drupal/Core/Entity/Query/ConditionBase.php
+++ b/core/lib/Drupal/Core/Entity/Query/ConditionFundamentals.php
@@ -2,15 +2,15 @@
 
 /**
  * @file
- * Contains \Drupal\Core\Entity\Query\ConditionBase.
+ * Contains \Drupal\Core\Entity\Query\ConditionFundamentals.
  */
 
 namespace Drupal\Core\Entity\Query;
 
 /**
- * Common code for all implementations of the entity query condition interface.
+ * Common code for all implementations of the entity query condition interfaces.
  */
-abstract class ConditionBase implements ConditionInterface {
+abstract class ConditionFundamentals {
 
   /**
    * Array of conditions.
@@ -44,20 +44,6 @@ public function count() {
   }
 
   /**
-   * Implements \Drupal\Core\Entity\Query\ConditionInterface::compile().
-   */
-  public function condition($field, $value = NULL, $operator = NULL, $langcode = NULL) {
-    $this->conditions[] = array(
-      'field' => $field,
-      'value' => $value,
-      'operator' => $operator,
-      'langcode' => $langcode,
-    );
-
-    return $this;
-  }
-
-  /**
    * Implements \Drupal\Core\Entity\Query\ConditionInterface::conditions().
    */
   public function &conditions() {
@@ -65,9 +51,11 @@ public function &conditions() {
   }
 
   /**
+   * Implements the magic __clone function.
+   *
    * Makes sure condition groups are cloned as well.
    */
-  function __clone() {
+  public function __clone() {
     foreach ($this->conditions as $key => $condition) {
       if ($condition['field'] instanceOf ConditionInterface) {
         $this->conditions[$key]['field'] = clone($condition['field']);
diff --git a/core/lib/Drupal/Core/Entity/Query/ConditionInterface.php b/core/lib/Drupal/Core/Entity/Query/ConditionInterface.php
index d55cced..ab5bf82 100644
--- a/core/lib/Drupal/Core/Entity/Query/ConditionInterface.php
+++ b/core/lib/Drupal/Core/Entity/Query/ConditionInterface.php
@@ -32,7 +32,7 @@ public function count();
   /**
    * Adds a condition.
    *
-   * @param string $field
+   * @param string|ConditionInterface $field
    * @param mixed $value
    * @param string $operator
    * @param string $langcode
diff --git a/core/lib/Drupal/Core/Entity/Query/QueryAggregateInterface.php b/core/lib/Drupal/Core/Entity/Query/QueryAggregateInterface.php
new file mode 100644
index 0000000..b23f0f6
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Query/QueryAggregateInterface.php
@@ -0,0 +1,161 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Query\QueryAggregateInterface.
+ */
+
+namespace Drupal\Core\Entity\Query;
+
+/**
+ * Defines a interface for aggregated entity queries.
+ */
+interface QueryAggregateInterface extends QueryInterface {
+
+  /**
+   * Specify a field and a function to aggregate on. Available functions:
+   * SUM,AVG,MIN,MAX.COUNT.
+   *
+   * @todo What about GROUP_CONCAT support?
+   *
+   * @param string $field
+   *   The name of the field to aggregate by.
+   * @param string $function
+   *   The aggregation function, for example COUNT or MIN.
+   * @param string $langcode
+   *   (optional) The language code.
+   * @param string $alias
+   *   (optional) The key that will be used on the resultset.
+   *
+   * @return \Drupal\Core\Entity\Query\QueryAggregateInterface
+   *   The called object.
+   */
+  public function aggregate($field, $function, $langcode = NULL, &$alias = NULL);
+
+  /**
+   * Specify the field to group on.
+   *
+   * @param string $field
+   *   The name of the field to group by.
+   *
+   * @return \Drupal\Core\Entity\Query\QueryAggregateInterface
+   *   The called object.
+   */
+  public function groupBy($field);
+
+  /**
+   * Set a condition for an aggregated value.
+   *
+   * @param string $field
+   *   The name of the field to aggregate by.
+   * @param string $function
+   *   The aggregation function, for example COUNT or MIN.
+   * @param mixed $value
+   *   The actual value of the field.
+   *
+   * @param $operator
+   *   Possible values:
+   *   - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS',
+   *     'ENDS_WITH': These operators expect $value to be a literal of the
+   *     same type as the column.
+   *   - 'IN', 'NOT IN': These operators expect $value to be an array of
+   *     literals of the same type as the column.
+   *   - 'BETWEEN': This operator expects $value to be an array of two literals
+   *     of the same type as the column.
+   *
+   * @param string $langcode
+   *   (optional) The language code.
+   *
+   * @return \Drupal\Core\Entity\Query\QueryAggregateInterface
+   *   The called object.
+   *
+   * @see \Drupal\Core\Entity\Query\QueryInterface::condition().
+   */
+  public function conditionAggregate($field, $function = NULL, $value = NULL, $operator = '=', $langcode = NULL);
+
+  /**
+   * Queries for the existence of a field.
+   *
+   * @param string $field
+   *   The name of the field.
+   * @param string $function
+   *   The aggregate function. This is only marked optional for interface
+   *   compatibility, it is illegal to leave it out.
+   * @param $langcode
+   *   (optional) The language code.
+   *
+   * @return \Drupal\Core\Entity\Query\QueryAggregateInterface
+   *   The called object.
+   */
+  public function exists($field, $function = NULL, $langcode = NULL);
+
+  /**
+   * Queries for the nonexistence of a field.
+   *
+   * @param string $field.
+   *   The name of a field.
+   * @param string $function
+   *   The aggregate function. This is only marked optional for interface
+   *   compatibility, it is illegal to leave it out.
+   * @param string $langcode
+   *   (optional) The language code.
+   *
+   * @return \Drupal\Core\Entity\Query\QueryAggregateInterface
+   *   The called object.
+   */
+  public function notExists($field, $function = NULL, $langcode = NULL);
+
+  /**
+   * Creates an object holding a group of conditions.
+   *
+   * See andConditionAggregateGroup() and orConditionAggregateGroup() for more.
+   *
+   * @param $conjunction
+   *   - AND (default): this is the equivalent of andConditionAggregateGroup().
+   *   - OR: this is the equivalent of andConditionAggregateGroup().
+   *
+   * return ConditionInterface
+   *   An object holding a group of conditions.
+   */
+  public function conditionAggregateGroupFactory($conjunction = 'AND');
+
+  /**
+   * Sorts by an aggregated value.
+   *
+   * @param $field
+   *   The name of a field.
+   * @param string $function
+   *   The aggregate function. This is only marked optional for interface
+   *   compatibility, it is illegal to leave it out.
+   * @param string $direction
+   *   The order of sorting, either DESC for descending of ASC for ascending.
+   * @param $langcode
+   *   (optional) The language code.
+   *
+   * @return \Drupal\Core\Entity\Query\QueryAggregateInterface
+   *   The called object.
+   */
+  public function sortAggregate($field, $function, $direction = 'ASC', $langcode = NULL);
+
+  /**
+   * Executes the aggregate query.
+   *
+   * @return array
+   *   A list of result row arrays. Each result row contains the aggregate
+   *   results as keys and also the groupBy columns as keys:
+   * @code
+   * $result = $query
+   *   ->aggregate('nid', 'count')
+   *   ->condition('status', 1)
+   *   ->groupby('type')
+   *   ->executeAggregate();
+   * @endcode
+   * Will return:
+   * @code
+   * $result[0] = array('count_nid' => 3, 'type' => 'page');
+   * $result[1] = array('count_nid' => 1, 'type' => 'poll');
+   * $result[2] = array('count_nid' => 4, 'type' => 'story');
+   * @endcode
+   */
+  public function execute();
+}
diff --git a/core/lib/Drupal/Core/Entity/Query/QueryBase.php b/core/lib/Drupal/Core/Entity/Query/QueryBase.php
index 77dc07d..1197881 100644
--- a/core/lib/Drupal/Core/Entity/Query/QueryBase.php
+++ b/core/lib/Drupal/Core/Entity/Query/QueryBase.php
@@ -12,7 +12,7 @@
 /**
  * The base entity query class.
  */
-abstract class QueryBase implements QueryInterface {
+abstract class QueryBase {
 
   /**
    * The entity type this query runs against.
@@ -22,7 +22,7 @@
   protected $entityType;
 
   /**
-   * The sort data.
+   * The list of sorts.
    *
    * @var array
    */
@@ -43,6 +43,34 @@
   protected $condition;
 
   /**
+   * The list of aggregate expressions.
+   *
+   * @var array
+   */
+  protected $aggregate = array();
+
+  /**
+   * The list of columns to group on.
+   *
+   * @var array
+   */
+  protected $groupBy = array();
+
+  /**
+   * Aggregate Conditions
+   *
+   * @var ConditionAggregateInterface
+   */
+  protected $conditionAggregate;
+
+  /**
+   * The list of sorts over the aggregate results.
+   *
+   * @var array
+   */
+  protected $sortAggregate = array();
+
+  /**
    * The query range.
    *
    * @var array
@@ -50,6 +78,20 @@
   protected $range = array();
 
   /**
+   * The query metadata for alter purposes.
+   *
+   * @var array
+   */
+  protected $alterMetaData;
+
+  /**
+   * The query tags.
+   *
+   * @var array
+   */
+  protected $alterTags;
+
+  /**
    * Whether access check is requested or not. Defaults to TRUE.
    *
    * @var bool
@@ -81,6 +123,9 @@ public function __construct($entity_type, $conjunction) {
     $this->entityType = $entity_type;
     $this->conjunction = $conjunction;
     $this->condition = $this->conditionGroupFactory($conjunction);
+    if (method_exists($this, 'conditionAggregateGroupFactory')) {
+      $this->conditionAggregate = $this->conditionAggregateGroupFactory($conjunction);
+    }
   }
 
   /**
@@ -142,8 +187,9 @@ public function orConditionGroup() {
   /**
    * Implements \Drupal\Core\Entity\Query\QueryInterface::sort().
    */
-  public function sort($property, $direction = 'ASC', $langcode = NULL) {
-    $this->sort[$property] = array(
+  public function sort($field, $direction = 'ASC', $langcode = NULL) {
+    $this->sort[] = array(
+      'field' => $field,
       'direction' => $direction,
       'langcode' => $langcode,
     );
@@ -283,4 +329,80 @@ public function addMetaData($key, $object) {
   public function getMetaData($key) {
     return isset($this->alterMetaData[$key]) ? $this->alterMetaData[$key] : NULL;
   }
+
+  /**
+   * Implements \Drupal\Core\Entity\Query\QueryAggregateInterface::aggregate()
+   */
+  public function aggregate($field, $function, $langcode = NULL, &$alias = NULL) {
+    if (!isset($alias)) {
+      $alias = $this->getAlias($field, $function);
+    }
+
+    $this->aggregate[$alias] = array(
+      'field' => $field,
+      'function' => $function,
+      'alias' => $alias,
+      'langcode' => $langcode,
+    );
+
+    return $this;
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\Query\QueryAggregateInterface::conditionAggregate().
+   */
+  public function conditionAggregate($field, $function = NULL, $value = NULL, $operator = '=', $langcode = NULL) {
+    $this->aggregate($field, $function, $langcode);
+    $this->conditionAggregate->condition($field, $function, $value, $operator, $langcode);
+
+    return $this;
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\Query\QueryAggregateInterface::sortAggregate().
+   */
+  public function sortAggregate($field, $function, $direction = 'ASC', $langcode = NULL) {
+    $alias = $this->getAlias($field, $function);
+
+    $this->sortAggregate[$alias] = array(
+      'field' => $field,
+      'function' => $function,
+      'direction' => $direction,
+      'langcode' => $langcode,
+    );
+    $this->aggregate($field, $function, $langcode, $alias);
+
+    return $this;
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\Query\QueryAggregateInterface::execute().
+   */
+  public function groupBy($field, $langcode = NULL) {
+    $this->groupBy[] = array(
+      'field' => $field,
+      'langcode' => $langcode,
+    );
+
+    return $this;
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\Query\QueryAggregateInterface::conditioNGroupFactory()
+   */
+  abstract function conditionGroupFactory($conjunction = 'AND');
+
+  /**
+   * Helper generating an alias.
+   *
+   * @param string $field
+   *   The
+   *
+   * @return string
+   *   The alias for the field.
+   */
+  protected function getAlias($field, $function) {
+    return strtolower($field . '_'. $function);
+  }
+
 }
diff --git a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Condition.php b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Condition.php
index a0d6d6a..30ac520 100644
--- a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Condition.php
+++ b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Condition.php
@@ -56,6 +56,11 @@ public function notExists($field, $langcode = NULL) {
     return $this->condition($field, NULL, 'IS NULL', $langcode);
   }
 
+  /**
+   * Translates the string operators to SQL equivalents.
+   *
+   * @param array $condition
+   */
   protected function translateCondition(&$condition) {
     switch ($condition['operator']) {
       case 'STARTS_WITH':
diff --git a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Condition.php b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/ConditionAggregate.php
similarity index 64%
copy from core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Condition.php
copy to core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/ConditionAggregate.php
index a0d6d6a..fb8881a 100644
--- a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Condition.php
+++ b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/ConditionAggregate.php
@@ -2,17 +2,23 @@
 
 /**
  * @file
- * Definition of Drupal\field_sql_storage\Query\ConditionSql.
+ * Contains \Drupal\field_sql_storage\Query\ConditionAggregate.
  */
 
 namespace Drupal\field_sql_storage\Entity;
 
-use Drupal\Core\Entity\Query\ConditionBase;
-use Drupal\Core\Entity\Query\ConditionInterface;
 use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\Core\Database\Query\Condition as SqlCondition;
+use Drupal\Core\Entity\Query\ConditionAggregateBase;
+use Drupal\Core\Entity\Query\ConditionAggregateInterface;
 
-class Condition extends ConditionBase {
+class ConditionAggregate extends ConditionAggregateBase {
+
+  /**
+   * Conjuction.
+   *
+   * @var string
+   */
+  protected $conjuction = '';
 
   /**
    * Implements Drupal\Core\Entity\Query\ConditionInterface::compile().
@@ -26,8 +32,8 @@ public function compile($conditionContainer) {
     $sqlQuery = $conditionContainer instanceof SelectInterface ? $conditionContainer : $conditionContainer->sqlQuery;
     $tables = new Tables($sqlQuery);
     foreach ($this->conditions as $condition) {
-      if ($condition['field'] instanceOf ConditionInterface) {
-        $sqlCondition = new SqlCondition($condition['field']->getConjunction());
+      if ($condition['field'] instanceOf ConditionAggregateInterface) {
+        $sqlCondition = new Condition($condition['field']->getConjunction());
         // Add the SQL query to the object before calling this method again.
         $sqlCondition->sqlQuery = $sqlQuery;
         $condition['field']->compile($sqlCondition);
@@ -37,7 +43,9 @@ public function compile($conditionContainer) {
         $type = strtoupper($this->conjunction) == 'OR' || $condition['operator'] == 'IS NULL' ? 'LEFT' : 'INNER';
         $this->translateCondition($condition);
         $field = $tables->addField($condition['field'], $type, $condition['langcode']);
-        $conditionContainer->condition($field, $condition['value'], $condition['operator']);
+        $function = $condition['function'];
+        $placeholder = ':db_placeholder_' . $conditionContainer->nextPlaceholder();
+        $conditionContainer->having("$function($field) {$condition['operator']} $placeholder", array($placeholder => $condition['value']));
       }
     }
   }
@@ -45,17 +53,22 @@ public function compile($conditionContainer) {
   /**
    * Implements Drupal\Core\Entity\Query\ConditionInterface::exists().
    */
-  public function exists($field, $langcode = NULL) {
-    return $this->condition($field, NULL, 'IS NOT NULL', $langcode);
+  public function exists($field, $function, $langcode = NULL) {
+    return $this->condition($field, $function, NULL, 'IS NOT NULL', $langcode);
   }
 
   /**
    * Implements Drupal\Core\Entity\Query\ConditionInterface::notExists().
    */
-  public function notExists($field, $langcode = NULL) {
-    return $this->condition($field, NULL, 'IS NULL', $langcode);
+  public function notExists($field, $function, $langcode = NULL) {
+    return $this->condition($field, $function, NULL, 'IS NULL', $langcode);
   }
 
+  /**
+   * Translates the string operators to SQL equivalents.
+   *
+   * @param array $condition
+   */
   protected function translateCondition(&$condition) {
     switch ($condition['operator']) {
       case 'STARTS_WITH':
diff --git a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Query.php b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Query.php
index 41091a6..bbfe5d0 100644
--- a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Query.php
+++ b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Query.php
@@ -9,11 +9,46 @@
 
 use Drupal\Core\Entity\Query\QueryBase;
 use Drupal\Core\Entity\Query\QueryException;
+use Drupal\Core\Entity\Query\QueryInterface;
 
 /**
  * The SQL storage entity query class.
  */
-class Query extends QueryBase {
+class Query extends QueryBase implements QueryInterface {
+
+  /**
+   * Contains the entity info for the entity type of that query.
+   *
+   * @var array
+   *
+   * @see \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityInfo;
+
+  /**
+   * The build sql select query.
+   *
+   * @var \Drupal\Core\Database\Query\SelectInterface
+   */
+  protected $sqlQuery;
+
+  /**
+   * An array of fields keyed by the field alias. Each entry correlates to the
+   * arguments of \Drupal\Core\Database\Query\SelectInterface::addField(), so
+   * the first one is the table alias, the second one the field and the last
+   * one optional the field alias.
+   *
+   * @var array
+   */
+  protected $sqlFields = array();
+
+  /**
+   * An array of strings added as to the group by, keyed by the string to avoid
+   * duplicates.
+   *
+   * @var array
+   */
+  protected $sqlGroupBy = array();
 
   /**
    * @var \Drupal\Core\Database\Connection
@@ -45,94 +80,125 @@ public function conditionGroupFactory($conjunction = 'AND') {
    * Implements Drupal\Core\Entity\Query\QueryInterface::execute().
    */
   public function execute() {
+    return $this
+      ->prepare()
+      ->compile()
+      ->addSort()
+      ->finish()
+      ->result();
+  }
+
+  /**
+   * @return \Drupal\field_sql_storage\Entity\Query
+   * @throws \Drupal\Core\Entity\Query\QueryException
+   */
+  protected function prepare() {
     $entity_type = $this->entityType;
+
     // @todo change to a method call once http://drupal.org/node/1892462 is in.
-    $entity_info = entity_get_info($entity_type);
-    if (!isset($entity_info['base_table'])) {
+    $this->entityInfo = entity_get_info($entity_type);
+    if (!isset($this->entityInfo['base_table'])) {
       throw new QueryException("No base table, invalid query.");
     }
-    $base_table = $entity_info['base_table'];
+    $base_table = $this->entityInfo['base_table'];
     $simple_query = TRUE;
-    if (isset($entity_info['data_table'])) {
+    if (isset($this->entityInfo['data_table'])) {
       $simple_query = FALSE;
     }
-    $sqlQuery = $this->connection->select($base_table, 'base_table', array('conjunction' => $this->conjunction));
-    $sqlQuery->addMetaData('entity_type', $entity_type);
-    $id_field = $entity_info['entity_keys']['id'];
-    $fields[$id_field] = TRUE;
-    if (empty($entity_info['entity_keys']['revision'])) {
-      // Add the key field for fetchAllKeyed(). When there is no revision
-      // support, this is the entity key.
-      $sqlQuery->addField('base_table', $entity_info['entity_keys']['id']);
+    $this->sqlQuery = $this->connection->select($base_table, 'base_table', array('conjunction' => $this->conjunction));
+    $this->sqlQuery->addMetaData('entity_type', $entity_type);
+    $id_field = $this->entityInfo['entity_keys']['id'];
+    // Add the key field for fetchAllKeyed().
+    if (empty($this->entityInfo['entity_keys']['revision'])) {
+      // When there is no revision support, the key field is the entity key.
+      $this->sqlFields["base_table.$id_field"] = array('base_table', $id_field);
+      // Now add the value column for fetchAllKeyed(). This is always the
+      // entity id.
+      $this->sqlFields["base_table.$id_field" . '_1'] = array('base_table', $id_field);
     }
     else {
-      // Add the key field for fetchAllKeyed(). When there is revision
-      // support, this is the revision key.
-      $revision_field = $entity_info['entity_keys']['revision'];
-      $fields[$revision_field] = TRUE;
-      $sqlQuery->addField('base_table', $revision_field);
-    }
-    // Now add the value column for fetchAllKeyed(). This is always the
-    // entity id.
-    $sqlQuery->addField('base_table', $id_field);
+      // When there is revision support, the key field is the revision key.
+      $revision_field = $this->entityInfo['entity_keys']['revision'];
+      $this->sqlFields["base_table.$revision_field"] = array('base_table', $revision_field);
+      // Now add the value column for fetchAllKeyed(). This is always the
+      // entity id.
+      $this->sqlFields["base_table.$id_field"] = array('base_table', $id_field);
+    }
     if ($this->accessCheck) {
-      $sqlQuery->addTag($entity_type . '_access');
+      $this->sqlQuery->addTag($entity_type . '_access');
     }
-    $sqlQuery->addTag('entity_query');
-    $sqlQuery->addTag('entity_query_' . $this->entityType);
+    $this->sqlQuery->addTag('entity_query');
+    $this->sqlQuery->addTag('entity_query_' . $this->entityType);
 
     // Add further tags added.
     if (isset($this->alterTags)) {
       foreach ($this->alterTags as $tag => $value) {
-        $sqlQuery->addTag($tag);
+        $this->sqlQuery->addTag($tag);
       }
     }
 
     // Add further metadata added.
     if (isset($this->alterMetaData)) {
       foreach ($this->alterMetaData as $key => $value) {
-        $sqlQuery->addMetaData($key, $value);
+        $this->sqlQuery->addMetaData($key, $value);
       }
     }
     // This now contains first the table containing entity properties and
     // last the entity base table. They might be the same.
-    $sqlQuery->addMetaData('age', $this->age);
-    $sqlQuery->addMetaData('simple_query', $simple_query);
-    $this->condition->compile($sqlQuery);
+    $this->sqlQuery->addMetaData('age', $this->age);
+    $this->sqlQuery->addMetaData('simple_query', $simple_query);
+    return $this;
+  }
+
+  /**
+   * @return \Drupal\field_sql_storage\Entity\Query
+   */
+  protected function compile() {
+    $this->condition->compile($this->sqlQuery);
+    return $this;
+  }
+
+  /**
+   * @return \Drupal\field_sql_storage\Entity\Query
+   */
+  protected function addSort() {
     if ($this->count) {
-      $this->sort = FALSE;
+      $this->sort = array();
     }
     // Gather the SQL field aliases first to make sure every field table
     // necessary is added. This might change whether the query is simple or
     // not. See below for more on simple queries.
     $sort = array();
     if ($this->sort) {
-      $tables = new Tables($sqlQuery);
-      foreach ($this->sort as $property => $data) {
-        $sort[$property] = isset($fields[$property]) ? $property : $tables->addField($property, 'LEFT', $data['langcode']);
+      foreach ($this->sort as $key => $data) {
+        $sort[$key] = $this->getSqlField($data['field'], $data['langcode']);
       }
     }
+    $simple_query = $this->isSimpleQuery();
     // If the query is set up for paging either via pager or by range or a
     // count is requested, then the correct amount of rows returned is
     // important. If the entity has a data table or multiple value fields are
     // involved then each revision might appear in several rows and this needs
     // a significantly more complex query.
-    $simple_query = (!$this->pager && !$this->range && !$this->count) || $sqlQuery->getMetaData('simple_query');
     if (!$simple_query) {
       // First, GROUP BY revision id (if it has been added) and entity id.
       // Now each group contains a single revision of an entity.
-      foreach (array_keys($fields) as $field) {
-        $sqlQuery->groupBy($field);
+      foreach ($this->sqlFields as $field) {
+        $group_by = "$field[0].$field[1]";
+        $this->sqlGroupBy[$group_by] = $group_by;
       }
     }
     // Now we know whether this is a simple query or not, actually do the
     // sorting.
-    foreach ($sort as $property => $sql_alias) {
-      $direction = $this->sort[$property]['direction'];
-      if ($simple_query || isset($fields[$property])) {
+    foreach ($sort as $key => $sql_alias) {
+      $direction = $this->sort[$key]['direction'];
+      if ($simple_query || isset($this->sqlGroupBy[$sql_alias])) {
         // Simple queries, and the grouped columns of complicated queries
         // can be ordered normally, without the aggregation function.
-        $sqlQuery->orderBy($sql_alias, $direction);
+        $this->sqlQuery->orderBy($sql_alias, $direction);
+        if (!isset($this->sqlFields[$sql_alias])) {
+          $this->sqlFields[$sql_alias] = explode('.', $sql_alias);
+        }
       }
       else {
         // Order based on the smallest element of each group if the
@@ -140,21 +206,76 @@ public function execute() {
         // if the direction is descending.
         $function = $direction == 'ASC' ? 'min' : 'max';
         $expression = "$function($sql_alias)";
-        $sqlQuery->addExpression($expression, "order_by_{$property}_$direction");
-        $sqlQuery->orderBy($expression, $direction);
+        $this->sqlQuery->addExpression($expression);
+        $this->sqlQuery->orderBy($expression, $direction);
       }
     }
+    return $this;
+  }
+
+  /**
+   * Finish the query by adding fields, GROUP BY and range.
+   *
+   * @return \Drupal\field_sql_storage\Entity\Query
+   */
+  protected function finish() {
     $this->initializePager();
     if ($this->range) {
-      $sqlQuery->range($this->range['start'], $this->range['length']);
+      $this->sqlQuery->range($this->range['start'], $this->range['length']);
+    }
+   foreach ($this->sqlGroupBy as $field) {
+      $this->sqlQuery->groupBy($field);
+    }
+    foreach ($this->sqlFields as $field) {
+      $this->sqlQuery->addField($field[0], $field[1], isset($field[2]) ? $field[2] : NULL);
     }
+    return $this;
+  }
+
+  /**
+   * Actually create the result.
+   *
+   * @return int|array
+   */
+  protected function result() {
     if ($this->count) {
-      return $sqlQuery->countQuery()->execute()->fetchField();
+      return $this->sqlQuery->countQuery()->execute()->fetchField();
     }
     // Return a keyed array of results. The key is either the revision_id or
     // the entity_id depending on whether the entity type supports revisions.
     // The value is always the entity id.
-    return $sqlQuery->execute()->fetchAllKeyed();
+    return $this->sqlQuery->execute()->fetchAllKeyed();
+  }
+
+  protected function getSqlField($field, $langcode) {
+    if (!isset($this->tables)) {
+      $this->tables = new Tables($this->sqlQuery);
+    }
+    $base_property = "base_table.$field";
+    if (isset($this->sqlFields[$base_property])) {
+      return $base_property;
+    }
+    else {
+      return $this->tables->addField($field, 'LEFT', $langcode);
+    }
+  }
+
+  /**
+   * Whether the query requires GROUP BY and ORDER BY MIN/MAX.
+   *
+   * @return bool
+   */
+  protected function isSimpleQuery() {
+    return (!$this->pager && !$this->range && !$this->count) || $this->sqlQuery->getMetaData('simple_query');
+  }
+
+  /**
+   * Zero out fields and GROUP BY when cloning.
+   */
+  public function __clone() {
+    parent::__clone();
+    $this->sqlFields = array();
+    $this->sqlGroupBy = array();
   }
 
 }
diff --git a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/QueryAggregate.php b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/QueryAggregate.php
new file mode 100644
index 0000000..662d193
--- /dev/null
+++ b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/QueryAggregate.php
@@ -0,0 +1,192 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field_sql_storage\Entity\QueryAggregate.
+ */
+
+namespace Drupal\field_sql_storage\Entity;
+
+use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Entity\Query\QueryAggregateInterface;
+
+/**
+ * The SQL storage entity query aggregate class.
+ */
+class QueryAggregate extends Query implements QueryAggregateInterface {
+
+  /**
+   * Stores the sql expressions used to build the sql query.
+   *
+   * @var array
+   *   An array of expressions.
+   */
+  protected $sqlExpressions = array();
+
+  /**
+   * Stores the entity manager used by the query.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a QueryAggregate object.
+   */
+  public function __construct($entity_type, EntityManager $entity_manager, $conjunction, $connection) {
+    parent::__construct($entity_type, $conjunction, $connection);
+    $this->entityManager = $entity_manager;
+    $this->connection = $connection;
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\Query\QueryAggregateInterface::execute()
+   */
+  public function execute() {
+    return $this
+      ->prepare()
+      ->addAggregate()
+      ->compile()
+      ->compileAggregate()
+      ->addGroupBy()
+      ->addSort()
+      ->addSortAggregate()
+      ->finish()
+      ->result();
+  }
+
+  public function prepare() {
+    parent::prepare();
+    // Throw away the id fields.
+    $this->sqlFields = array();
+    return $this;
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\Query\QueryAggregateInterface::conditionAggregateGroupFactory()
+   */
+  public function conditionAggregateGroupFactory($conjunction = 'AND') {
+    return new ConditionAggregate($conjunction);
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\Query\QueryAggregateInterface::exists()
+   */
+  public function exists($field, $function = NULL, $langcode = NULL) {
+    // @todo Implement that function.
+    return parent::exists($field, $langcode);
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\Query\QueryAggregateInterface::exists()
+   */
+  public function notExists($field, $function = NULL, $langcode = NULL) {
+    // @todo Implement that function.
+    return parent::notExists($field, $langcode);
+  }
+
+
+  /**
+   * @return \Drupal\field_sql_storage\Entity\Queryaggregate
+   */
+  protected function addAggregate() {
+    if ($this->aggregate) {
+      foreach ($this->aggregate as $aggregate) {
+        $sql_field = $this->getSqlField($aggregate['field'], $aggregate['langcode']);
+        $this->sqlExpressions[$aggregate['alias']] = $aggregate['function'] . "($sql_field)";
+      }
+    }
+    return $this;
+  }
+
+  /**
+   * Build the aggregation conditions part of the query.
+   *
+   * @return \Drupal\field_sql_storage\Entity\QueryAggregate
+   *   The called object.
+   */
+  protected function compileAggregate() {
+    $this->conditionAggregate->compile($this->sqlQuery);
+    return $this;
+  }
+
+  /**
+   * Add the groupby values to the actual query.
+   *
+   * @return \Drupal\field_sql_storage\Entity\QueryAggregate
+   *   The called object.
+   */
+  protected function addGroupBy() {
+    foreach ($this->groupBy as $group_by) {
+      $field = $group_by['field'];
+      $sql_field = $this->getSqlField($field, $group_by['langcode']);
+      $this->sqlGroupBy[$sql_field] = $sql_field;
+      list($table, $real_sql_field) = explode('.', $sql_field);
+      $this->sqlFields[$sql_field] = array($table, $real_sql_field, $this->createSqlAlias($field, $real_sql_field));
+    }
+
+    return $this;
+  }
+
+  /**
+   * Build the aggregation sort part of the query.
+   *
+   * @return \Drupal\field_sql_storage\Entity\QueryAggregate
+   *   The called object.
+   */
+  protected function addSortAggregate() {
+    if(!$this->count) {
+      foreach ($this->sortAggregate as $alias => $sort) {
+        $this->sqlQuery->orderBy($this->sqlExpressions[$alias], $sort['direction']);
+      }
+    }
+    return $this;
+  }
+
+
+  /**
+   *
+   * @return \Drupal\field_sql_storage\Entity\QueryAggregate
+   *   The called object.
+   */
+  protected function finish() {
+    foreach ($this->sqlExpressions as $alias => $expression) {
+      $this->sqlQuery->addExpression($expression, $alias);
+    }
+    return parent::finish();
+  }
+
+  /**
+   * @param string $field
+   *   The field as passed in by the caller.
+   * @param string $sql_field
+   *   The sql field as returned by getSqlField.
+   * @return string
+   *   The SQL alias expected in the return value. The dots in $sql_field are
+   *   replaced with underscores and if a default fallback to .value happened,
+   *   the _value is stripped.
+   */
+  function createSqlAlias($field, $sql_field) {
+    $alias = str_replace('.', '_', $sql_field);
+    if (substr($alias, 0, 6) === 'field_' && substr($field, -6) !== '_value' && substr($alias, -6) === '_value') {
+      $alias = substr($alias, 0, -6);
+    }
+    return $alias;
+  }
+
+  /**
+   * @return array|int
+   *   Returns the aggregated result, or a number if it's a count query.
+   */
+  protected function result() {
+    if ($this->count) {
+      return parent::result();
+    }
+    $return = array();
+    foreach ($this->sqlQuery->execute() as $row) {
+      $return[] = (array)$row;
+    }
+    return $return;
+  }
+
+}
diff --git a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/QueryFactory.php b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/QueryFactory.php
index d8b36ba..5ee132c 100644
--- a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/QueryFactory.php
+++ b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/QueryFactory.php
@@ -2,23 +2,63 @@
 
 /**
  * @file
- * Definition of Drupal\field_sql_storage\Entity\QueryFactory.
+ * Contains \Drupal\field_sql_storage\Entity\QueryFactory.
  */
 
 namespace Drupal\field_sql_storage\Entity;
 
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityManager;
 
 /**
  * Factory class creating entity query objects for the SQL backend.
+ *
+ * @see \Drupal\field_sql_storage\Entity\Query
+ * @see \Drupal\field_sql_storage\Entity\QueryAggregate
  */
 class QueryFactory {
 
+  /**
+   * Constructs a QueryFactory object.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection used by the entity query.
+   */
   function __construct(Connection $connection) {
     $this->connection = $connection;
   }
 
+  /**
+   * Constructs a entity query for a certain entity type.
+   *
+   * @param string $entity_type
+   *   The entity type.
+   * @param string $conjunction
+   *   - AND: all of the conditions on the query need to match.
+   *   - OR: at least one of the conditions on the query need to match.
+   *
+   * @return \Drupal\field_sql_storage\Entity\Query
+   *   The factored query.
+   */
   function get($entity_type, $conjunction = 'AND') {
     return new Query($entity_type, $conjunction, $this->connection);
   }
+
+  /**
+   * Constructs a entity aggregation query for a certain entity type.
+   *
+   * @param string $entity_type
+   *   The entity type.
+   * @param string $conjunction
+   *   - AND: all of the conditions on the query need to match.
+   *   - OR: at least one of the conditions on the query need to match.
+   *
+   * @return \Drupal\field_sql_storage\Entity\QueryAggregate
+   *   The factored aggregation query.
+   */
+  function getAggregate($entity_type, $conjunction = 'AND') {
+    // @todo Do the same as in the config query patch.
+    return new QueryAggregate($entity_type, drupal_container()->get('plugin.manager.entity'), $conjunction, $this->connection);
+  }
+
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryAggregationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryAggregationTest.php
new file mode 100644
index 0000000..47c1211
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryAggregationTest.php
@@ -0,0 +1,591 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Entity\EntityQueryAggregationTest.
+ */
+namespace Drupal\system\Tests\Entity;
+
+use Drupal\field_sql_storage\Entity\ConditionAggregate;
+use Drupal\simpletest\DrupalUnitTestBase;
+
+/**
+ * Defines a test for testing aggregation support for entity query.
+ *
+ * @todo Use EntityUnitBaseTest provided by http://drupal.org/node/1893108.
+ *
+ * @see \Drupal\entity_test\Plugin\Core\Entity\EntityTest
+ */
+
+class EntityQueryAggregationTest extends DrupalUnitTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('user', 'system', 'field', 'number', 'text', 'field_sql_storage', 'entity_test');
+
+  /**
+   * The entity_test storage controller to create the test entities.
+   *
+   * @var \Drupal\entity_test\EntityTestStorageController
+   */
+  protected $entityStorageController;
+
+  /**
+   * The actual query result, to compare later.
+   *
+   * @var array
+   */
+  protected $queryResult;
+
+  /**
+   * The query factory to create entity queries.
+   *
+   * @var \Drupal\field_sql_storage\Entity\QueryFactory
+   */
+  public $factory;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity Query aggregation',
+      'description' => 'Tests the Entity Query Aggregation API',
+      'group' => 'Entity API',
+    );
+  }
+
+  protected function setUp() {
+    parent::setUp();
+    $this->installSchema('user', 'users');
+    $this->installSchema('system', 'sequences');
+    $this->installSchema('field', 'field_config');
+    $this->installSchema('field', 'field_config_instance');
+    $this->installSchema('entity_test', 'entity_test');
+
+    $this->entityStorageController = $this->container->get('plugin.manager.entity')->getStorageController('entity_test');
+    $this->factory = drupal_container()->get('entity.query.field_sql_storage');
+
+    // Add some fieldapi fields to be used in the test.
+
+    // @todo Maybe start from 0.
+    for ($i = 1; $i <= 2; $i++) {
+      $field = array(
+        'field_name' => 'field_test_' . $i,
+        'type' => 'number_integer',
+        'cardinality' => 2,
+      );
+      field_create_field($field);
+      $instance = array(
+        'field_name' => $field['field_name'],
+        'entity_type' => 'entity_test',
+        'bundle' => 'entity_test',
+      );
+      field_create_instance($instance);
+    }
+
+    $entity = $this->entityStorageController->create(array(
+      'id' => 1,
+      'user_id' => 1,
+      'field_test_1' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => 1))),
+      'field_test_2' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => 2))),
+    ));
+    $entity->enforceIsNew();
+    $entity->save();
+
+    $entity = $this->entityStorageController->create(array(
+      'id' => 2,
+      'user_id' => 2,
+      'field_test_1' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => 1))),
+      'field_test_2' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => 7))),
+    ));
+    $entity->enforceIsNew();
+    $entity->save();
+    $entity = $this->entityStorageController->create(array(
+      'id' => 3,
+      'user_id' => 2,
+      'field_test_1' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => 2))),
+      'field_test_2' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => 1))),
+    ));
+    $entity->enforceIsNew();
+    $entity->save();
+    $entity = $this->entityStorageController->create(array(
+      'id' => 4,
+      'user_id' => 2,
+      'field_test_1' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => 2))),
+      'field_test_2' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => 8))),
+    ));
+    $entity->enforceIsNew();
+    $entity->save();
+    $entity = $this->entityStorageController->create(array(
+      'id' => 5,
+      'user_id' => 3,
+      'field_test_1' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => 2))),
+      'field_test_2' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => 2))),
+    ));
+    $entity->enforceIsNew();
+    $entity->save();
+    $entity = $this->entityStorageController->create(array(
+      'id' => 6,
+      'user_id' => 3,
+      'field_test_1' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => 3))),
+      'field_test_2' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => 8))),
+    ));
+    $entity->enforceIsNew();
+    $entity->save();
+
+  }
+
+  /**
+   * Test aggregation support.
+   */
+  public function testAggregation() {
+    // Apply just a simple groupby.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->groupBy('user_id')
+      ->execute();
+
+    $this->assertResults(array(
+      array('user_id' => 1),
+      array('user_id' => 2),
+      array('user_id' => 3),
+    ));
+
+    $function_expected = array();
+    $function_expected['count'] = array(array('id_count' => 6));
+    $function_expected['min'] = array(array('id_min' => 1));
+    $function_expected['max'] = array(array('id_max' => 6));
+    $function_expected['sum'] = array(array('id_sum' => 21));
+    $function_expected['avg'] = array(array('id_avg' => (21.0/6.0)));
+
+    // Apply just a simple aggregation for different aggregation functions.
+    foreach ($function_expected as $aggregation_function => $expected) {
+      $this->queryResult = $this->factory->getAggregate('entity_test')
+        ->aggregate('id', $aggregation_function)
+        ->execute();
+      $this->assertEqual($this->queryResult, $expected);
+    }
+
+    // Apply aggregation and groupby on the same query.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('id', 'COUNT')
+      ->groupBy('user_id')
+      ->execute();
+    $this->assertResults(array(
+      array('user_id' => 1, 'id_count' => 1),
+      array('user_id' => 2, 'id_count' => 3),
+      array('user_id' => 3, 'id_count' => 2),
+    ));
+
+    // Apply aggregation and a condition which matches.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('id', 'COUNT')
+      ->conditionAggregate('id', 'COUNT', 8)
+      ->execute();
+    $this->assertResults(array());
+
+    // Don't call aggregate to test the implicit aggregate call.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->conditionAggregate('id', 'COUNT', 8)
+      ->execute();
+    $this->assertResults(array());
+
+    // Apply aggregation and a condition which matches.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('id', 'count')
+      ->conditionAggregate('id', 'COUNT', 6)
+      ->execute();
+    $this->assertResults(array(array('id_count' => 6)));
+
+    // Apply aggregation, a groupby and a condition which matches partially via
+    // the operator '='.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('id', 'count')
+      ->conditionAggregate('id', 'count', 2)
+      ->groupBy('user_id')
+      ->execute();
+    $this->assertResults(array(array('id_count' => 2, 'user_id' => 3)));
+
+    // Apply aggregation, a groupby and a condition which matches partially via
+    // the operator '>'.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('id', 'count')
+      ->conditionAggregate('id', 'COUNT', 1, '>')
+      ->groupBy('user_id')
+      ->execute();
+    $this->assertResults(array(
+      array('id_count' => 2, 'user_id' => 3),
+      array('id_count' => 3, 'user_id' => 2),
+    ));
+
+    // Apply aggregation and a sort. This might not be useful, but have a proper
+    // test coverage.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('id', 'COUNT')
+      ->sortAggregate('id', 'COUNT')
+      ->execute();
+    $this->assertResults(array(array('id_count' => 6)));
+
+    // Don't call aggregate to test the implicit aggregate call.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->sortAggregate('id', 'COUNT')
+      ->execute();
+    $this->assertResults(array(array('id_count' => 6)));
+
+    // Apply aggregation, groupby and a sort descending.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('id', 'COUNT')
+      ->groupBy('user_id')
+      ->sortAggregate('id', 'COUNT', 'DESC')
+      ->execute();
+    $this->assertResults(array(
+      array('user_id' => 2, 'id_count' => 3),
+      array('user_id' => 3, 'id_count' => 2),
+      array('user_id' => 1, 'id_count' => 1),
+    ));
+
+    // Apply aggregation, groupby and a sort ascending.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('id', 'COUNT')
+      ->groupBy('user_id')
+      ->sortAggregate('id', 'COUNT', 'ASC')
+      ->execute();
+    $this->assertResults(array(
+      array('user_id' => 1, 'id_count' => 1),
+      array('user_id' => 3, 'id_count' => 2),
+      array('user_id' => 2, 'id_count' => 3),
+    ));
+
+    // Apply aggregation, groupby, a aggregation condition and a sort with the
+    // operator '='.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('id', 'COUNT')
+      ->groupBy('user_id')
+      ->sortAggregate('id', 'COUNT')
+      ->conditionAggregate('id', 'COUNT', 2)
+      ->execute();
+    $this->assertResults(array(array('id_count' => 2, 'user_id' => 3)));
+
+    // Apply aggregation, groupby, a aggregation condition and a sort with the
+    // operator '<' and order ASC.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('id', 'COUNT')
+      ->groupBy('user_id')
+      ->sortAggregate('id', 'COUNT', 'ASC')
+      ->conditionAggregate('id', 'COUNT', 3, '<')
+      ->execute();
+    $this->assertResults(array(
+      array('id_count' => 1, 'user_id' => 1),
+      array('id_count' => 2, 'user_id' => 3),
+    ));
+
+    // Apply aggregation, groupby, a aggregation condition and a sort with the
+    // operator '<' and order DESC.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('id', 'COUNT')
+      ->groupBy('user_id')
+      ->sortAggregate('id', 'COUNT', 'DESC')
+      ->conditionAggregate('id', 'COUNT', 3, '<')
+      ->execute();
+    $this->assertResults(array(
+      array('id_count' => 2, 'user_id' => 3),
+      array('id_count' => 1, 'user_id' => 1),
+    ));
+
+    // Test aggregation/groupby support for fieldapi fields.
+
+    // Just group by a fieldapi field.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->groupBy('field_test_1')
+      ->execute();
+    $this->assertResults(array(
+      array('field_test_1' => 1),
+      array('field_test_1' => 2),
+      array('field_test_1' => 3),
+    ));
+
+    // Group by a fieldapi field and aggregate a normal property.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('user_id', 'COUNT')
+      ->groupBy('field_test_1')
+      ->execute();
+
+    $this->assertResults(array(
+      array('field_test_1' => 1, 'user_id_count' => 2),
+      array('field_test_1' => 2, 'user_id_count' => 3),
+      array('field_test_1' => 3, 'user_id_count' => 1),
+    ));
+
+    // Group by a normal property and aggregate a fieldapi field.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('field_test_1', 'COUNT')
+      ->groupBy('user_id')
+      ->execute();
+
+    $this->assertResults(array(
+      array('user_id' => 1, 'field_test_1_count' => 1),
+      array('user_id' => 2, 'field_test_1_count' => 3),
+      array('user_id' => 3, 'field_test_1_count' => 2),
+    ));
+
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('field_test_1', 'SUM')
+      ->groupBy('user_id')
+      ->execute();
+    $this->assertResults(array(
+      array('user_id' => 1, 'field_test_1_sum' => 1),
+      array('user_id' => 2, 'field_test_1_sum' => 5),
+      array('user_id' => 3, 'field_test_1_sum' => 5),
+    ));
+
+    // Aggregate by two different fieldapi fields.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('field_test_1', 'SUM')
+      ->aggregate('field_test_2', 'SUM')
+      ->groupBy('user_id')
+      ->execute();
+    $this->assertResults(array(
+      array('user_id' => 1, 'field_test_1_sum' => 1, 'field_test_2_sum' => 2),
+      array('user_id' => 2, 'field_test_1_sum' => 5, 'field_test_2_sum' => 16),
+      array('user_id' => 3, 'field_test_1_sum' => 5, 'field_test_2_sum' => 10),
+    ));
+
+    // This time aggregate the same field twice.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('field_test_1', 'SUM')
+      ->aggregate('field_test_1', 'COUNT')
+      ->groupBy('user_id')
+      ->execute();
+    $this->assertResults(array(
+      array('user_id' => 1, 'field_test_1_sum' => 1, 'field_test_1_count' => 1),
+      array('user_id' => 2, 'field_test_1_sum' => 5, 'field_test_1_count' => 3),
+      array('user_id' => 3, 'field_test_1_sum' => 5, 'field_test_1_count' => 2),
+    ));
+
+    // Group by and aggregate by a fieldapi field.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->groupBy('field_test_1')
+      ->aggregate('field_test_2', 'COUNT')
+      ->execute();
+    $this->assertResults(array(
+      array('field_test_1' => 1, 'field_test_2_count' => 2),
+      array('field_test_1' => 2, 'field_test_2_count' => 3),
+      array('field_test_1' => 3, 'field_test_2_count' => 1),
+    ));
+
+    // Group by and aggregate by a fieldapi field and use multiple aggregate
+    // functions.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->groupBy('field_test_1')
+      ->aggregate('field_test_2', 'COUNT')
+      ->aggregate('field_test_2', 'SUM')
+      ->execute();
+    $this->assertResults(array(
+      array('field_test_1' => 1, 'field_test_2_count' => 2, 'field_test_2_sum' => 9),
+      array('field_test_1' => 2, 'field_test_2_count' => 3, 'field_test_2_sum' => 11),
+      array('field_test_1' => 3, 'field_test_2_count' => 1, 'field_test_2_sum' => 8),
+    ));
+
+    // Apply a aggregate condition for a fieldapi field and group by a simple
+    // property.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->conditionAggregate('field_test_1', 'COUNT', 3)
+      ->groupBy('user_id')
+      ->execute();
+    $this->assertResults(array(
+      array('user_id' => 2, 'field_test_1_count' => 3),
+      array('user_id' => 3, 'field_test_1_count' => 2),
+    ));
+
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('field_test_1', 'SUM')
+      ->conditionAggregate('field_test_1', 'COUNT', 2, '>')
+      ->groupBy('user_id')
+      ->execute();
+    $this->assertResults(array(
+      array('user_id' => 2, 'field_test_1_sum' => 5, 'field_test_1_count' => 3),
+      array('user_id' => 3, 'field_test_1_sum' => 5, 'field_test_1_count' => 2),
+    ));
+
+    // Apply a aggregate condition for a simple property and a group by a
+    // fieldapi field.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->conditionAggregate('user_id', 'COUNT', 2)
+      ->groupBy('field_test_1')
+      ->execute();
+    $this->assertResults(array(
+      array('field_test_1' => 1, 'user_id_count' => 2),
+    ));
+
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->conditionAggregate('user_id', 'COUNT', 2, '>')
+      ->groupBy('field_test_1')
+      ->execute();
+    $this->assertResults(array(
+      array('field_test_1' => 1, 'user_id_count' => 2),
+      array('field_test_1' => 2, 'user_id_count' => 3),
+    ));
+
+    // Apply a aggregate condition and a group by fieldapi fields.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->groupBy('field_test_1')
+      ->conditionAggregate('field_test_2', 'COUNT', 2)
+      ->execute();
+    $this->assertResults(array(
+      array('field_test_1' => 1, 'field_test_2_count' => 2),
+    ));
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->groupBy('field_test_1')
+      ->conditionAggregate('field_test_2', 'COUNT', 2, '>')
+      ->execute();
+    $this->assertResults(array(
+      array('field_test_1' => 1, 'field_test_2_count' => 2),
+      array('field_test_1' => 2, 'field_test_2_count' => 3),
+    ));
+
+    // Apply a aggregate condition and a group by fieldapi fields with multiple
+    // conditions via AND.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->groupBy('field_test_1')
+      ->conditionAggregate('field_test_2', 'COUNT', 2)
+      ->conditionAggregate('field_test_2', 'SUM', 8)
+      ->execute();
+    $this->assertResults(array());
+
+    // Apply a aggregate condition and a group by fieldapi fields with multiple
+    // conditions via OR.
+    $this->queryResult = $this->factory->getAggregate('entity_test', 'OR')
+      ->groupBy('field_test_1')
+      ->conditionAggregate('field_test_2', 'COUNT', 2)
+      ->conditionAggregate('field_test_2', 'SUM', 8)
+      ->execute();
+    $this->assertResults(array(
+      array('field_test_1' => 1, 'field_test_2_count' => 2, 'field_test_2_sum' => 9),
+      array('field_test_1' => 3, 'field_test_2_count' => 1, 'field_test_2_sum' => 8),
+    ));
+
+    // Group by a normal property and aggregate a fieldapi field and sort by
+    // the groupby field.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('field_test_1', 'COUNT')
+      ->groupBy('user_id')
+      ->sort('user_id', 'DESC')
+      ->execute();
+
+    $this->assertResults(array(
+      array('user_id' => 3, 'field_test_1_count' => 2),
+      array('user_id' => 2, 'field_test_1_count' => 3),
+      array('user_id' => 1, 'field_test_1_count' => 1),
+    ));
+
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->aggregate('field_test_1', 'COUNT')
+      ->groupBy('user_id')
+      ->sort('user_id', 'ASC')
+      ->execute();
+
+    $this->assertResults(array(
+      array('user_id' => 1, 'field_test_1_count' => 1),
+      array('user_id' => 2, 'field_test_1_count' => 3),
+      array('user_id' => 3, 'field_test_1_count' => 2),
+    ));
+
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->conditionAggregate('field_test_1', 'COUNT', 2, '>')
+      ->groupBy('user_id')
+      ->sort('user_id', 'ASC')
+      ->execute();
+
+    $this->assertResults(array(
+      array('user_id' => 2, 'field_test_1_count' => 3),
+      array('user_id' => 3, 'field_test_1_count' => 2),
+    ));
+
+    // Group by a normal property and aggregate a fieldapi field and sort by
+    // the aggregated field.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->sortAggregate('field_test_1', 'COUNT', 'DESC')
+      ->groupBy('user_id')
+      ->execute();
+    $this->assertResults(array(
+      array('user_id' => 2, 'field_test_1_count' => 3),
+      array('user_id' => 3, 'field_test_1_count' => 2),
+      array('user_id' => 1, 'field_test_1_count' => 1),
+    ));
+
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->sortAggregate('field_test_1', 'COUNT', 'ASC')
+      ->groupBy('user_id')
+      ->execute();
+
+    $this->assertResults(array(
+      array('user_id' => 1, 'field_test_1_count' => 1),
+      array('user_id' => 3, 'field_test_1_count' => 2),
+      array('user_id' => 2, 'field_test_1_count' => 3),
+    ));
+
+    // Group by and aggregate by fieldapi fields and sort by the groupby field.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->groupBy('field_test_1')
+      ->aggregate('field_test_2', 'COUNT')
+      ->sort('field_test_1', 'ASC')
+      ->execute();
+    $this->assertResults(array(
+      array('field_test_1' => 1, 'field_test_2_count' => 2),
+      array('field_test_1' => 2, 'field_test_2_count' => 3),
+      array('field_test_1' => 3, 'field_test_2_count' => 1),
+    ));
+
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->groupBy('field_test_1')
+      ->aggregate('field_test_2', 'COUNT')
+      ->sort('field_test_1', 'DESC')
+      ->execute();
+    $this->assertResults(array(
+      array('field_test_1' => 3, 'field_test_2_count' => 1),
+      array('field_test_1' => 2, 'field_test_2_count' => 3),
+      array('field_test_1' => 1, 'field_test_2_count' => 2),
+    ));
+
+    // Groupy and aggregate by fieldapi fields and sort by the aggregated field.
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->groupBy('field_test_1')
+      ->sortAggregate('field_test_2', 'COUNT', 'DESC')
+      ->execute();
+    $this->assertResults(array(
+      array('field_test_1' => 2, 'field_test_2_count' => 3),
+      array('field_test_1' => 1, 'field_test_2_count' => 2),
+      array('field_test_1' => 3, 'field_test_2_count' => 1),
+    ));
+
+    $this->queryResult = $this->factory->getAggregate('entity_test')
+      ->groupBy('field_test_1')
+      ->sortAggregate('field_test_2', 'COUNT', 'DESC')
+      ->execute();
+    $this->assertResults(array(
+      array('field_test_1' => 3, 'field_test_2_count' => 1),
+      array('field_test_1' => 1, 'field_test_2_count' => 2),
+      array('field_test_1' => 2, 'field_test_2_count' => 3),
+    ));
+
+  }
+
+  public function assertResults($expected) {
+    if (empty($expected)) {
+      $this->assertTrue(empty($this->queryResult));
+      return;
+    }
+    $found = TRUE;
+    foreach ($this->queryResult as $row) {
+      foreach ($expected as $expected_row) {
+        if (!array_diff_assoc($expected_row, $row) && !array_diff_assoc($row, $expected_row)) {
+          continue 2;
+        }
+      }
+      $found = FALSE;
+      break;
+    }
+    $this->assertTrue($found, strtr('!expected expected, !found found', array('!expected' => print_r($expected, TRUE), '!found' => print_r($this->queryResult, TRUE))));
+  }
+}
+
