Skip to content

Commit

Permalink
Support minDistance and maxDistance options for $near and $nearSphere…
Browse files Browse the repository at this point in the history
… operators (doctrine#2583)

* Support minDistance and maxDistance options for $near and $nearSphere operators

* Fix static analysis errors
  • Loading branch information
alcaeus authored Nov 24, 2023
1 parent 3cec5e3 commit b098ab9
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 21 deletions.
5 changes: 3 additions & 2 deletions lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
use Doctrine\Persistence\Mapping\MappingException;
use InvalidArgumentException;
use Iterator as SplIterator;
use MongoDB\BSON\ObjectId;
use MongoDB\Collection;
use MongoDB\Driver\Cursor;
use MongoDB\Driver\CursorInterface;
use MongoDB\Driver\Exception\Exception as DriverException;
use MongoDB\Driver\Exception\WriteException;
use MongoDB\Driver\WriteConcern;
Expand Down Expand Up @@ -581,7 +582,7 @@ private function getShardKeyQuery(object $document): array
/**
* Wraps the supplied base cursor in the corresponding ODM class.
*/
private function wrapCursor(Cursor $baseCursor): Iterator
private function wrapCursor(SplIterator&CursorInterface $baseCursor): Iterator
{
return new CachingIterator(new HydratingIterator($baseCursor, $this->dm->getUnitOfWork(), $this->class));
}
Expand Down
8 changes: 4 additions & 4 deletions lib/Doctrine/ODM/MongoDB/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -991,9 +991,9 @@ public function mul($value): self
* @param float|array<string, mixed>|Point $x
* @param float $y
*/
public function near($x, $y = null): self
public function near($x, $y = null, ?float $minDistance = null, ?float $maxDistance = null): self
{
$this->expr->near($x, $y);
$this->expr->near($x, $y, $minDistance, $maxDistance);

return $this;
}
Expand All @@ -1011,9 +1011,9 @@ public function near($x, $y = null): self
* @param float|array<string, mixed>|Point $x
* @param float $y
*/
public function nearSphere($x, $y = null): self
public function nearSphere($x, $y = null, ?float $minDistance = null, ?float $maxDistance = null): self
{
$this->expr->nearSphere($x, $y);
$this->expr->nearSphere($x, $y, $minDistance, $maxDistance);

return $this;
}
Expand Down
47 changes: 41 additions & 6 deletions lib/Doctrine/ODM/MongoDB/Query/Expr.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use MongoDB\BSON\Binary;
use MongoDB\BSON\Javascript;

use function array_filter;
use function array_key_exists;
use function array_map;
use function array_merge;
Expand Down Expand Up @@ -822,17 +823,34 @@ public function mul($value): self
* @param float|array<string, mixed>|Point $x
* @param float $y
*/
public function near($x, $y = null): self
public function near($x, $y = null, ?float $minDistance = null, ?float $maxDistance = null): self
{
if ($x instanceof Point) {
$x = $x->jsonSerialize();
}

if (is_array($x)) {
return $this->operator('$near', ['$geometry' => $x]);
return $this->operator(
'$near',
array_filter([
'$geometry' => $x,
'$minDistance' => $minDistance,
'$maxDistance' => $maxDistance,
]),
);
}

$this->operator('$near', [$x, $y]);

if ($minDistance !== null) {
$this->operator('$minDistance', $minDistance);
}

return $this->operator('$near', [$x, $y]);
if ($maxDistance !== null) {
$this->operator('$maxDistance', $maxDistance);
}

return $this;
}

/**
Expand All @@ -848,17 +866,34 @@ public function near($x, $y = null): self
* @param float|array<string, mixed>|Point $x
* @param float $y
*/
public function nearSphere($x, $y = null): self
public function nearSphere($x, $y = null, ?float $minDistance = null, ?float $maxDistance = null): self
{
if ($x instanceof Point) {
$x = $x->jsonSerialize();
}

if (is_array($x)) {
return $this->operator('$nearSphere', ['$geometry' => $x]);
return $this->operator(
'$nearSphere',
array_filter([
'$geometry' => $x,
'$minDistance' => $minDistance,
'$maxDistance' => $maxDistance,
]),
);
}

$this->operator('$nearSphere', [$x, $y]);

if ($minDistance !== null) {
$this->operator('$minDistance', $minDistance);
}

return $this->operator('$nearSphere', [$x, $y]);
if ($maxDistance !== null) {
$this->operator('$maxDistance', $maxDistance);
}

return $this;
}

/**
Expand Down
3 changes: 1 addition & 2 deletions lib/Doctrine/ODM/MongoDB/Types/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,7 @@ public static function getTypesMap(): array
return self::$typesMap;
}

/** @return string */
public function __toString()
public function __toString(): string
{
$e = explode('\\', static::class);
$className = end($e);
Expand Down
5 changes: 0 additions & 5 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,6 @@
<code>$datetime instanceof DateTime</code>
</RedundantCondition>
</file>
<file src="lib/Doctrine/ODM/MongoDB/Types/Type.php">
<MethodSignatureMustProvideReturnType>
<code>__toString</code>
</MethodSignatureMustProvideReturnType>
</file>
<file src="lib/Doctrine/ODM/MongoDB/UnitOfWork.php">
<InvalidArgument>
<code>$assoc</code>
Expand Down
4 changes: 2 additions & 2 deletions tests/Doctrine/ODM/MongoDB/Tests/Query/BuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -504,8 +504,8 @@ public static function provideProxiedExprMethods(): array
'type()' => ['type', [7]],
'all()' => ['all', [['value1', 'value2']]],
'mod()' => ['mod', [2, 0]],
'near()' => ['near', [1, 2]],
'nearSphere()' => ['nearSphere', [1, 2]],
'near()' => ['near', [1, 2], null, 5, 10],
'nearSphere()' => ['nearSphere', [1, 2], null, 5, 10],
'geoIntersects()' => ['geoIntersects', [self::createGeometry()]],
'geoWithin()' => ['geoWithin', [self::createGeometry()]],
'geoWithinBox()' => ['geoWithinBox', [1, 2, 3, 4]],
Expand Down
164 changes: 164 additions & 0 deletions tests/Doctrine/ODM/MongoDB/Tests/Query/ExprTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,64 @@ public function testNearWithGeoJsonPoint($point, array $expected): void
self::assertEquals(['$near' => $expected], $expr->getQuery());
}

public function testNearWithGeoJsonPointAndMinDistance(): void
{
$expr = $this->createExpr();

$coordinates = [1, 2];
$point = new Point($coordinates);

self::assertSame($expr, $expr->near($point, null, 5));
self::assertEquals(
[
'$near' => [
'$geometry' => ['type' => 'Point', 'coordinates' => $coordinates],
'$minDistance' => 5,
],
],
$expr->getQuery(),
);
}

public function testNearWithGeoJsonPointAndMaxDistance(): void
{
$expr = $this->createExpr();

$coordinates = [1, 2];
$point = new Point($coordinates);

self::assertSame($expr, $expr->near($point, null, null, 10));
self::assertEquals(
[
'$near' => [
'$geometry' => ['type' => 'Point', 'coordinates' => $coordinates],
'$maxDistance' => 10,
],
],
$expr->getQuery(),
);
}

public function testNearWithGeoJsonPointAndMinAndMaxDistance(): void
{
$expr = $this->createExpr();

$coordinates = [1, 2];
$point = new Point($coordinates);

self::assertSame($expr, $expr->near($point, null, 5, 10));
self::assertEquals(
[
'$near' => [
'$geometry' => ['type' => 'Point', 'coordinates' => $coordinates],
'$minDistance' => 5,
'$maxDistance' => 10,
],
],
$expr->getQuery(),
);
}

public function testNearWithLegacyCoordinates(): void
{
$expr = $this->createExpr();
Expand All @@ -430,6 +488,30 @@ public function testNearWithLegacyCoordinates(): void
self::assertEquals(['$near' => [1, 2]], $expr->getQuery());
}

public function testNearWithLegacyCoordinatesAndMinDistance(): void
{
$expr = $this->createExpr();

self::assertSame($expr, $expr->near(1, 2, 5));
self::assertEquals(['$near' => [1, 2], '$minDistance' => 5], $expr->getQuery());
}

public function testNearWithLegacyCoordinatesAndMaxDistance(): void
{
$expr = $this->createExpr();

self::assertSame($expr, $expr->near(1, 2, null, 10));
self::assertEquals(['$near' => [1, 2], '$maxDistance' => 10], $expr->getQuery());
}

public function testNearWithLegacyCoordinatesAndMinAndMaxDistance(): void
{
$expr = $this->createExpr();

self::assertSame($expr, $expr->near(1, 2, 5, 10));
self::assertEquals(['$near' => [1, 2], '$minDistance' => 5, '$maxDistance' => 10], $expr->getQuery());
}

/**
* @param Point|array<string, mixed> $point
* @param array<string, mixed> $expected
Expand All @@ -443,6 +525,64 @@ public function testNearSphereWithGeoJsonPoint($point, array $expected): void
self::assertEquals(['$nearSphere' => $expected], $expr->getQuery());
}

public function testNearSphereWithGeoJsonPointAndMinDistance(): void
{
$expr = $this->createExpr();

$coordinates = [1, 2];
$point = new Point($coordinates);

self::assertSame($expr, $expr->nearSphere($point, null, 5));
self::assertEquals(
[
'$nearSphere' => [
'$geometry' => ['type' => 'Point', 'coordinates' => $coordinates],
'$minDistance' => 5,
],
],
$expr->getQuery(),
);
}

public function testNearSphereWithGeoJsonPointAndMaxDistance(): void
{
$expr = $this->createExpr();

$coordinates = [1, 2];
$point = new Point($coordinates);

self::assertSame($expr, $expr->nearSphere($point, null, null, 10));
self::assertEquals(
[
'$nearSphere' => [
'$geometry' => ['type' => 'Point', 'coordinates' => $coordinates],
'$maxDistance' => 10,
],
],
$expr->getQuery(),
);
}

public function testNearSphereWithGeoJsonPointAndMinAndMaxDistance(): void
{
$expr = $this->createExpr();

$coordinates = [1, 2];
$point = new Point($coordinates);

self::assertSame($expr, $expr->nearSphere($point, null, 5, 10));
self::assertEquals(
[
'$nearSphere' => [
'$geometry' => ['type' => 'Point', 'coordinates' => $coordinates],
'$minDistance' => 5,
'$maxDistance' => 10,
],
],
$expr->getQuery(),
);
}

public function testNearSphereWithLegacyCoordinates(): void
{
$expr = $this->createExpr();
Expand All @@ -451,6 +591,30 @@ public function testNearSphereWithLegacyCoordinates(): void
self::assertEquals(['$nearSphere' => [1, 2]], $expr->getQuery());
}

public function testNearSphereWithLegacyCoordinatesAndMinDistance(): void
{
$expr = $this->createExpr();

self::assertSame($expr, $expr->nearSphere(1, 2, 5));
self::assertEquals(['$nearSphere' => [1, 2], '$minDistance' => 5], $expr->getQuery());
}

public function testNearSphereWithLegacyCoordinatesAndMaxDistance(): void
{
$expr = $this->createExpr();

self::assertSame($expr, $expr->nearSphere(1, 2, null, 10));
self::assertEquals(['$nearSphere' => [1, 2], '$maxDistance' => 10], $expr->getQuery());
}

public function testNearSphereWithLegacyCoordinatesAndMinAndMaxDistance(): void
{
$expr = $this->createExpr();

self::assertSame($expr, $expr->nearSphere(1, 2, 5, 10));
self::assertEquals(['$nearSphere' => [1, 2], '$minDistance' => 5, '$maxDistance' => 10], $expr->getQuery());
}

public function testPullWithValue(): void
{
$expr = $this->createExpr();
Expand Down

0 comments on commit b098ab9

Please sign in to comment.