Revise support for geoNear commands and $near query criteria #63

Merged
merged 3 commits into from Jul 13, 2012
@@ -330,7 +330,7 @@ protected function doFindAndUpdate(array $query, array $newObj, array $options)
public function near(array $near, array $query = array(), array $options = array())
{
if ($this->eventManager->hasListeners(Events::preNear)) {
- $this->eventManager->dispatchEvent(Events::preNear, new NearEventArgs($this, $near, $query));
+ $this->eventManager->dispatchEvent(Events::preNear, new NearEventArgs($this, $query, $near));
}
$result = $this->doNear($near, $query, $options);
@@ -354,20 +354,6 @@ public function selectSlice($fieldName, $skip, $limit = null)
}
/**
- * Add where near criteria.
- *
- * @param string $x
- * @param string $y
- * @return Builder
- */
- public function near($value)
- {
- $this->query['type'] = Query::TYPE_GEO_LOCATION;
- $this->query['near'][$this->currentField] = $value;
- return $this;
- }
-
- /**
* Set the current field to operate on.
*
* @param string $field
@@ -560,6 +546,62 @@ public function mod($mod)
}
/**
+ * Specify a geoNear command for this query.
+ *
+ * This method sets the "near" option for the geoNear command. The "num"
+ * option may be set using limit(). The "distanceMultiplier" and
+ * "maxDistance" options may be set using their respective builder methods.
+ * Additional query criteria will be assigned to the "query" option.
+ *
+ * @param string $x
+ * @param string $y
+ * @return Builder
+ */
+ public function geoNear($x, $y)
+ {
+ $this->query['type'] = Query::TYPE_GEO_LOCATION;
+ $this->query['geoNear'] = array('near' => array($x, $y));
+ return $this;
+ }
+
+ /**
+ * Set the "distanceMultiplier" option for a geoNear command query.
+ *
+ * @param string $distanceMultiplier
+ * @return Builder
+ */
+ public function distanceMultiplier($distanceMultiplier)
+ {
+ $this->query['geoNear']['distanceMultiplier'] = $distanceMultiplier;
+ return $this;
+ }
+
+ /**
+ * Set the "maxDistance" option for a geoNear command query.
+ *
+ * @param string $maxDistance
+ * @return Builder
+ */
+ public function maxDistance($maxDistance)
+ {
+ $this->query['geoNear']['maxDistance'] = $maxDistance;
+ return $this;
+ }
+
+ /**
+ * Add where $near query.
+ *
+ * @param string $x
+ * @param string $y
+ * @return Builder
+ */
+ public function near($x, $y)
+ {
+ $this->expr->near($x, $y);
+ return $this;
+ }
+
+ /**
* Add where $within $box query.
*
* @param string $x1
@@ -207,6 +207,16 @@ public function mod($mod)
return $this->operator($this->cmd . 'mod', $mod);
}
+ public function near($x, $y)
+ {
+ if ($this->currentField) {
+ $this->query[$this->currentField][$this->cmd . 'near'] = array($x, $y);
+ } else {
+ $this->query[$this->cmd . 'near'] = array($x, $y);
+ }
+ return $this;
+ }
+
public function withinBox($x1, $y1, $x2, $y2)
{
if ($this->currentField) {
@@ -213,7 +213,14 @@ public function execute()
if (isset($this->query['limit']) && $this->query['limit']) {
$this->options['num'] = $this->query['limit'];
}
- return $this->collection->near($this->query['near'], $this->query['query'], $this->options);
+
+ foreach (array('distanceMultiplier', 'maxDistance') as $key) {
+ if (isset($this->query['geoNear'][$key]) && $this->query['geoNear'][$key]) {
+ $this->options[$key] = $this->query['geoNear'][$key];
+ }
+ }
+
+ return $this->collection->near($this->query['geoNear']['near'], $this->query['query'], $this->options);
case self::TYPE_COUNT:
return $this->collection->count($this->query['query']);
@@ -89,21 +89,6 @@ public function testFindAndUpdateQuery()
$this->assertNull($query->execute());
}
- public function testGeoLocationQuery()
- {
- $qb = $this->getTestQueryBuilder()
- ->field('x')->near(1)
- ->field('y')->near(2)
- ->field('username')->equals('jwage');
-
- $this->assertEquals(Query::TYPE_GEO_LOCATION, $qb->getType());
- $expected = array(
- 'username' => 'jwage'
- );
- $this->assertEquals($expected, $qb->getQueryArray());
- $this->assertInstanceOf('Doctrine\MongoDB\ArrayIterator', $qb->getQuery()->execute());
- }
-
public function testGroupQuery()
{
$qb = $this->getTestQueryBuilder()
@@ -366,6 +351,37 @@ public function testDeepClone()
$this->assertCount(1, $qb->getQueryArray());
}
+ public function testGeoNearQuery()
+ {
+ $qb = $this->getTestQueryBuilder()
+ ->geoNear(50, 50)
+ ->distanceMultiplier(2.5)
+ ->maxDistance(5)
+ ->field('type')->equals('restaurant')
+ ->limit(10);
+
+ $this->assertEquals(Query::TYPE_GEO_LOCATION, $qb->getType());
+
+ $expectedQuery = array('type' => 'restaurant');
+ $this->assertEquals($expectedQuery, $qb->getQueryArray());
+
+ $geoNear = $qb->debug('geoNear');
+ $this->assertEquals(array(50, 50), $geoNear['near']);
+ $this->assertEquals(2.5, $geoNear['distanceMultiplier']);
+ $this->assertEquals(5, $geoNear['maxDistance']);
+ $this->assertEquals(10, $qb->debug('limit'));
+ $this->assertInstanceOf('Doctrine\MongoDB\ArrayIterator', $qb->getQuery()->execute());
+ }
+
+ public function testNear()
+ {
+ $qb = $this->getTestQueryBuilder()
+ ->field('loc')->near(50, 50);
+
+ $expected = array('loc' => array('$near' => array(50, 50)));
+ $this->assertEquals($expected, $qb->getQueryArray());
+ }
+
public function testWithinBox()
{
$qb = $this->getTestQueryBuilder()
@@ -6,25 +6,18 @@
class QueryTest extends \PHPUnit_Framework_TestCase
{
- const MAP_REDUCE_OPTION_KEY = 'limit';
- const MAP_REDUCE_OPTION_VALUE = 10;
-
public function testMapReduceOptionsArePassed()
{
$collection = $this->getMockCollection();
- $mapReduceOptions = array(
- self::MAP_REDUCE_OPTION_KEY => self::MAP_REDUCE_OPTION_VALUE
- );
-
$queryArray = array(
'type' => Query::TYPE_MAP_REDUCE,
'mapReduce' => array(
'map' => '',
'reduce' => '',
- 'options' => $mapReduceOptions
+ 'options' => array('limit' => 10),
),
- 'query' => array()
+ 'query' => array()
);
$query = new Query(
@@ -41,12 +34,43 @@ public function testMapReduceOptionsArePassed()
$this->anything(),
$this->anything(),
$this->anything(),
+ new ArrayHasValueUnderKey('limit', 10)
+ );
+
+ $query->execute();
+ }
+
+ public function testGeoNearOptionsArePassed()
+ {
+ $collection = $this->getMockCollection();
+
+ $queryArray = array(
+ 'type' => Query::TYPE_GEO_LOCATION,
+ 'geoNear' => array(
+ 'near' => array(50, 50),
+ 'distanceMultiplier' => 2.5,
+ 'maxDistance' => 5,
+ ),
+ 'limit' => 10,
+ 'query' => array('altitude' => array('$gt' => 1)),
+ );
+
+ $query = new Query(
+ $this->getMockDatabase(),
+ $collection,
+ $queryArray,
+ array(),
+ ''
+ );
+
+ $collection->expects($this->any())
+ ->method('geoNear')
+ ->with(array(50, 50),
+ array('altitude' => array('$gt' => 1)),
$this->logicalAnd(
- $this->arrayHasKey(self::MAP_REDUCE_OPTION_KEY),
- new ArrayHasValueUnderKey(
- self::MAP_REDUCE_OPTION_KEY,
- self::MAP_REDUCE_OPTION_VALUE
- )
+ new ArrayHasValueUnderKey('distanceMultiplier', 2.5),
+ new ArrayHasValueUnderKey('maxDistance', 5),
+ new ArrayHasValueUnderKey('num', 10)
)
);