Skip to content
This repository has been archived by the owner on Nov 11, 2020. It is now read-only.

Commit

Permalink
Merge pull request #171 from doctrine/mindistance-option
Browse files Browse the repository at this point in the history
Implement $minDistance query operator and geoNear option
  • Loading branch information
jmikola committed Apr 10, 2014
2 parents 4a75fa7 + 2b4f78b commit d23af82
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 3 deletions.
36 changes: 33 additions & 3 deletions lib/Doctrine/MongoDB/Query/Builder.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -421,9 +421,9 @@ public function geoIntersects($geometry)
* *
* This method sets the "near" option for the geoNear command. The "num" * This method sets the "near" option for the geoNear command. The "num"
* option may be set using {@link Expr::limit()}. The "distanceMultiplier", * option may be set using {@link Expr::limit()}. The "distanceMultiplier",
* "maxDistance", and "spherical" options may be set using their respective * "maxDistance", "minDistance", and "spherical" options may be set using
* builder methods. Additional query criteria will be assigned to the * their respective builder methods. Additional query criteria will be
* "query" option. * assigned to the "query" option.
* *
* @see http://docs.mongodb.org/manual/reference/command/geoNear/ * @see http://docs.mongodb.org/manual/reference/command/geoNear/
* @param float|array|Point $x * @param float|array|Point $x
Expand Down Expand Up @@ -872,6 +872,36 @@ public function maxDistance($maxDistance)
return $this; return $this;
} }


/**
* Set the "minDistance" option for a geoNear command query or add
* $minDistance criteria to the query.
*
* If the query is a geoNear command ({@link Expr::geoNear()} was called),
* the "minDistance" command option will be set; otherwise, $minDistance
* will be added to the current expression.
*
* If the query uses GeoJSON points, $minDistance will be interpreted in
* meters. If legacy point coordinates are used, $minDistance will be
* interpreted in radians.
*
* @see Expr::minDistance()
* @see http://docs.mongodb.org/manual/reference/command/geoNear/
* @see http://docs.mongodb.org/manual/reference/operator/minDistance/
* @see http://docs.mongodb.org/manual/reference/operator/near/
* @see http://docs.mongodb.org/manual/reference/operator/nearSphere/
* @param float $minDistance
* @return self
*/
public function minDistance($minDistance)
{
if ($this->query['type'] === Query::TYPE_GEO_NEAR) {
$this->query['geoNear']['options']['minDistance'] = $minDistance;
} else {
$this->expr->minDistance($minDistance);
}
return $this;
}

/** /**
* Specify $mod criteria for the current field. * Specify $mod criteria for the current field.
* *
Expand Down
37 changes: 37 additions & 0 deletions lib/Doctrine/MongoDB/Query/Expr.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -536,6 +536,43 @@ public function maxDistance($maxDistance)
return $this; return $this;
} }


/**
* Set the $minDistance option for $near or $nearSphere criteria.
*
* This method must be called after near() or nearSphere(), since placement
* of the $minDistance option depends on whether a GeoJSON point or legacy
* coordinates were provided for $near/$nearSphere.
*
* @see http://docs.mongodb.org/manual/reference/operator/minDistance/
* @param float $minDistance
* @return self
* @throws BadMethodCallException if the query does not already have $near or $nearSphere criteria
*/
public function minDistance($minDistance)
{
if ($this->currentField) {
$query = &$this->query[$this->currentField];
} else {
$query = &$this->query;
}

if ( ! isset($query['$near']) && ! isset($query['$nearSphere'])) {
throw new BadMethodCallException(
'This method requires a $near or $nearSphere operator (call near() or nearSphere() first)'
);
}

if (isset($query['$near']['$geometry'])) {
$query['$near']['$minDistance'] = $minDistance;
} elseif (isset($query['$nearSphere']['$geometry'])) {
$query['$nearSphere']['$minDistance'] = $minDistance;
} else {
$query['$minDistance'] = $minDistance;
}

return $this;
}

/** /**
* Specify $mod criteria for the current field. * Specify $mod criteria for the current field.
* *
Expand Down
14 changes: 14 additions & 0 deletions tests/Doctrine/MongoDB/Tests/Query/BuilderTest.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ public function provideProxiedExprMethods()
'type()' => array('type', array(7)), 'type()' => array('type', array(7)),
'all()' => array('all', array(array('value1', 'value2'))), 'all()' => array('all', array(array('value1', 'value2'))),
'maxDistance' => array('maxDistance', array(5)), 'maxDistance' => array('maxDistance', array(5)),
'minDistance' => array('minDistance', array(5)),
'mod()' => array('mod', array(2, 0)), 'mod()' => array('mod', array(2, 0)),
'near()' => array('near', array(1, 2)), 'near()' => array('near', array(1, 2)),
'nearSphere()' => array('nearSphere', array(1, 2)), 'nearSphere()' => array('nearSphere', array(1, 2)),
Expand Down Expand Up @@ -457,6 +458,19 @@ public function testMaxDistanceWithGeoNearCommand()
$this->assertEquals($expected, $qb->debug('geoNear')); $this->assertEquals($expected, $qb->debug('geoNear'));
} }


public function testMinDistanceWithGeoNearCommand()
{
$expected = array(
'near' => array(0, 0),
'options' => array('spherical' => false, 'minDistance' => 5),
);

$qb = $this->getTestQueryBuilder();

$this->assertSame($qb, $qb->geoNear(0, 0)->minDistance(5));
$this->assertEquals($expected, $qb->debug('geoNear'));
}

/** /**
* @expectedException BadMethodCallException * @expectedException BadMethodCallException
*/ */
Expand Down
50 changes: 50 additions & 0 deletions tests/Doctrine/MongoDB/Tests/Query/ExprTest.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -110,6 +110,56 @@ public function testMaxDistanceRequiresNearOrNearSphereOperator()
$expr->maxDistance(1); $expr->maxDistance(1);
} }


/**
* @dataProvider provideGeoJsonPoint
*/
public function testMinDistanceWithNearAndGeoJsonPoint($point, array $expected)
{
$expr = new Expr();
$expr->near($point);

$this->assertSame($expr, $expr->minDistance(1));
$this->assertEquals(array('$near' => $expected + array('$minDistance' => 1)), $expr->getQuery());
}

public function testMinDistanceWithNearAndLegacyCoordinates()
{
$expr = new Expr();
$expr->near(1, 2);

$this->assertSame($expr, $expr->minDistance(1));
$this->assertEquals(array('$near' => array(1, 2), '$minDistance' => 1), $expr->getQuery());
}

public function testMinDistanceWithNearSphereAndGeoJsonPoint()
{
$json = array('type' => 'Point', 'coordinates' => array(1, 2));

$expr = new Expr();
$expr->nearSphere($this->getMockPoint($json));

$this->assertSame($expr, $expr->minDistance(1));
$this->assertEquals(array('$nearSphere' => array('$geometry' => $json, '$minDistance' => 1)), $expr->getQuery());
}

public function testMinDistanceWithNearSphereAndLegacyCoordinates()
{
$expr = new Expr();
$expr->nearSphere(1, 2);

$this->assertSame($expr, $expr->minDistance(1));
$this->assertEquals(array('$nearSphere' => array(1, 2), '$minDistance' => 1), $expr->getQuery());
}

/**
* @expectedException BadMethodCallException
*/
public function testMinDistanceRequiresNearOrNearSphereOperator()
{
$expr = new Expr();
$expr->minDistance(1);
}

/** /**
* @dataProvider provideGeoJsonPoint * @dataProvider provideGeoJsonPoint
*/ */
Expand Down

0 comments on commit d23af82

Please sign in to comment.