Commit d4a597fd authored by webchick's avatar webchick

#561104 by Crell: Added support for random ordering in dynamic select queries.

parent f24f709e
......@@ -173,3 +173,13 @@ public function execute() {
return $stmt->rowCount();
}
}
class SelectQuery_pgsql extends SelectQuery {
public function orderRandom() {
$alias = $this->addExpression('RANDOM()', 'random_field');
$this->orderBy($alias);
return $this;
}
}
......@@ -347,6 +347,28 @@ public function addJoin($type, $table, $alias = NULL, $condition = NULL, $argume
*/
public function orderBy($field, $direction = 'ASC');
/**
* Orders the result set by a random value.
*
* This may be stacked with other orderBy() calls. If so, the query will order
* by each specified field, including this one, in the order called. Although
* this method may be called multiple times on the same query, doing so
* is not particularly useful.
*
* Note: The method used by most drivers may not scale to very large result
* sets. If you need to work with extremely large data sets, you may create
* your own database driver by subclassing off of an existing driver and
* implementing your own randomization mechanism. See
*
* http://jan.kneschke.de/projects/mysql/order-by-rand/
*
* for an example of such an alternate sorting mechanism.
*
* @return
* The called object
*/
public function orderRandom();
/**
* Restricts a query to a given range in the result set.
*
......@@ -643,6 +665,11 @@ public function orderBy($field, $direction = 'ASC') {
return $this;
}
public function orderRandom() {
$this->query->orderRandom();
return $this;
}
public function range($start = NULL, $length = NULL) {
$this->query->range($start, $length);
return $this;
......@@ -1182,6 +1209,12 @@ public function orderBy($field, $direction = 'ASC') {
return $this;
}
public function orderRandom() {
$alias = $this->addExpression('RAND()', 'random_field');
$this->orderBy($alias);
return $this;
}
public function range($start = NULL, $length = NULL) {
$this->range = func_num_args() ? array('start' => $start, 'length' => $length) : array();
return $this;
......
......@@ -1400,6 +1400,55 @@ class DatabaseSelectTestCase extends DatabaseTestCase {
$this->assertEqual($names[1], 'Ringo', t('Second query returned correct second name.'));
$this->assertEqual($names[2], 'Ringo', t('Third query returned correct name.'));
}
/**
* Test that random ordering of queries works.
*
* This is a non-deterministic query. That is, it is not guaranteed to pass
* 100% of the time. Random numbers are like that. Instead, we take two sets
* of random selects. By the laws of probability, the average of each set
* should be roughly the average of the data set being selected from and
* roughly the same each time through, provided that the original data set
* is evenly distributed. Because we know that ours is, this test should pass
* correctly 99% of the time. It is not possible to unit test for that other
* 1% successfully, so that will have to do.
*/
function testRandomOrder() {
$sets = 2;
$runs = 50;
$ids = array();
$ids = array();
$max = db_query("SELECT MAX(id) FROM {test}")->fetchField();
$min = db_query("SELECT MIN(id) FROM {test}")->fetchField();
$ideal_average = ($min + $max) / 2;
$allowed_deviation = ($max - $min) / 4;
for ($i = 0; $i < $sets; ++$i) {
for ($j = 0; $j < $runs; ++$j) {
$ids[$i][] = db_select('test', 't')
->fields('t', array('id'))
->range(0, 1)
->orderRandom()
->orderBy('id')
->execute()
->fetchField();
}
}
for ($i = 0; $i < $sets; ++$i) {
$found_average[$i] = array_sum($ids[$i]) / count($ids[$i]);
$deviation = abs($found_average[$i] - $ideal_average);
$this->assertTrue($deviation <= $allowed_deviation, t('Random ids are within allowed deviation.'));
}
for ($i = 1; $i < $sets; ++$i) {
$deviation = abs($found_average[$i] - $found_average[$i-1]);
$this->assertTrue($deviation <= $allowed_deviation, t('Random ids are within allowed deviation from each other.'));
}
}
}
/**
......@@ -2897,7 +2946,7 @@ class DatabaseExtraTypesTestCase extends DrupalWebTestCase {
'description' => t('Test Time field'),
'type' => 'time',
'not null' => FALSE,
),
),
),
);
$ret = array();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment