Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support minDistance and maxDistance options for $near and $nearSphere operators #2583

Merged
merged 2 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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