Skip to content

Loading…

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

Merged
merged 3 commits into from

1 participant

@jmikola
Doctrine member

Previously, the near() builder method was used to support the geoNear command instead of $near query criteria. This was unintuitive, given how other geo methods like withinBox() operated.

Instead, we add a new geoNear() builder method, which operates similarly to mapReduce() and switches the query into GEO_LOCATION mode. We also add methods to specify the distanceMultipler and maxDistance options.

The near() builder method is repurposed to define $near query criteria for a field or expression.

@jmikola jmikola referenced this pull request
Closed

Fix Builder near() #53

jmikola and others added some commits
@jmikola jmikola Revise support for geoNear commands and $near query criteria
Fixes #47. Previously, the near() builder method was used to support the geoNear command instead of $near query criteria. This was unintuitive, given how other geo methods like withinBox() operated.

Instead, we add a new geoNear() builder method, which operates similarly to mapReduce() and switches the query into GEO_LOCATION mode. We also add methods to specify the distanceMultipler and maxDistance options.

The near() builder method is repurposed to define $near query criteria for a field or expression.
6637851
Koji Iwazaki Fix event argument order when dispatching collectionPreNear afff46e
@jmikola jmikola merged commit 3994f6b into master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 12, 2012
  1. @jmikola
  2. @jmikola

    Revise support for geoNear commands and $near query criteria

    jmikola committed
    Fixes #47. Previously, the near() builder method was used to support the geoNear command instead of $near query criteria. This was unintuitive, given how other geo methods like withinBox() operated.
    
    Instead, we add a new geoNear() builder method, which operates similarly to mapReduce() and switches the query into GEO_LOCATION mode. We also add methods to specify the distanceMultipler and maxDistance options.
    
    The near() builder method is repurposed to define $near query criteria for a field or expression.
  3. @jmikola

    Fix event argument order when dispatching collectionPreNear

    Koji Iwazaki committed with jmikola
This page is out of date. Refresh to see the latest.
View
2 lib/Doctrine/MongoDB/Collection.php
@@ -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);
View
70 lib/Doctrine/MongoDB/Query/Builder.php
@@ -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
View
10 lib/Doctrine/MongoDB/Query/Expr.php
@@ -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) {
View
9 lib/Doctrine/MongoDB/Query/Query.php
@@ -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']);
View
46 tests/Doctrine/MongoDB/Tests/Query/BuilderTest.php
@@ -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()
View
52 tests/Doctrine/MongoDB/Tests/Query/QueryTest.php
@@ -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)
)
);
Something went wrong with that request. Please try again.