Skip to content

Commit d71ded3

Browse files
committed
Fix $geoNear stage with empty query
1 parent 123ab7d commit d71ded3

File tree

5 files changed

+117
-6
lines changed

5 files changed

+117
-6
lines changed

lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use GeoJson\Geometry\Point;
1515
use MongoDB\Collection;
1616
use OutOfRangeException;
17+
use stdClass;
1718
use TypeError;
1819

1920
use function array_map;
@@ -313,7 +314,7 @@ public function getPipeline(/* bool $applyFilters = true */): array
313314
}
314315

315316
$matchExpression = $this->applyFilters([]);
316-
if ($matchExpression !== []) {
317+
if ((array) $matchExpression !== []) {
317318
array_unshift($pipeline, ['$match' => $matchExpression]);
318319
}
319320

@@ -698,15 +699,23 @@ public function addStage(Stage $stage): Stage
698699
*
699700
* @param array<string, mixed> $query
700701
*
701-
* @return array<string, mixed>
702+
* @return array<string, mixed>|stdClass
702703
*/
703-
private function applyFilters(array $query): array
704+
private function applyFilters(array|stdClass $query): array|stdClass
704705
{
706+
if (! is_array($query)) {
707+
$query = (array) $query;
708+
}
709+
705710
$documentPersister = $this->getDocumentPersister();
706711

707712
$query = $documentPersister->addDiscriminatorToPreparedQuery($query);
708713
$query = $documentPersister->addFilterToPreparedQuery($query);
709714

715+
if ($query === []) {
716+
return (object) $query;
717+
}
718+
710719
return $query;
711720
}
712721

lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GeoNear.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Doctrine\ODM\MongoDB\Aggregation\Builder;
88
use GeoJson\Geometry\Point;
9+
use stdClass;
910

1011
use function is_array;
1112

@@ -46,11 +47,12 @@ public function __construct(Builder $builder, $x, $y = null)
4647

4748
public function getExpression(): array
4849
{
50+
$query = $this->query->getQuery();
4951
$geoNear = [
5052
'near' => $this->near,
5153
'spherical' => $this->spherical,
5254
'distanceField' => $this->distanceField,
53-
'query' => $this->query->getQuery(),
55+
'query' => $query ?: new stdClass(),
5456
'distanceMultiplier' => $this->distanceMultiplier,
5557
'includeLocs' => $this->includeLocs,
5658
'maxDistance' => $this->maxDistance,

tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GeoNearTest.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Doctrine\ODM\MongoDB\Tests\Aggregation\AggregationTestTrait;
99
use Doctrine\ODM\MongoDB\Tests\BaseTestCase;
1010
use PHPUnit\Framework\Attributes\DataProvider;
11+
use stdClass;
1112

1213
class GeoNearTest extends BaseTestCase
1314
{
@@ -72,7 +73,11 @@ public function testLimitDoesNotCreateExtraStage(): void
7273
->geoNear(0, 0)
7374
->limit(1);
7475

75-
$stage = ['near' => [0, 0], 'spherical' => false, 'distanceField' => null, 'query' => [], 'num' => 1];
76-
self::assertSame([['$geoNear' => $stage]], $builder->getPipeline());
76+
$stage = $builder->getPipeline()[0];
77+
self::assertSame([0, 0], $stage['$geoNear']['near']);
78+
self::assertFalse($stage['$geoNear']['spherical']);
79+
self::assertNull($stage['$geoNear']['distanceField']);
80+
self::assertEquals(new stdClass(), $stage['$geoNear']['query']);
81+
self::assertSame(1, $stage['$geoNear']['num']);
7782
}
7883
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\ODM\MongoDB\Tests\Functional;
6+
7+
use Doctrine\ODM\MongoDB\Tests\BaseTestCase;
8+
use Documents\Place;
9+
10+
class GeoTest extends BaseTestCase
11+
{
12+
public function setUp(): void
13+
{
14+
parent::setUp();
15+
16+
$this->dm->getSchemaManager()->ensureDocumentIndexes(Place::class);
17+
$this->dm->persist(new Place(
18+
name: 'Central Park',
19+
location: ['type' => 'Point', 'coordinates' => [-73.97, 40.77]],
20+
category: 'Parks',
21+
));
22+
$this->dm->persist(new Place(
23+
name: 'Sara D. Roosevelt Park',
24+
location: ['type' => 'Point', 'coordinates' => [-73.9928, 40.7193]],
25+
category: 'Parks',
26+
));
27+
$this->dm->persist(new Place(
28+
name: 'Polo Grounds',
29+
location: ['type' => 'Point', 'coordinates' => [-73.9928, 40.7193]],
30+
category: 'Stadiums',
31+
));
32+
$this->dm->flush();
33+
}
34+
35+
public function testGeoNearWithQuery(): void
36+
{
37+
$pipeline = $this->dm->createAggregationBuilder(Place::class)
38+
->geoNear(-73.99279, 40.719296)
39+
->field('category')->equals('Parks')
40+
->distanceField('calculated')
41+
->maxDistance(2)
42+
->spherical(true)
43+
->getAggregation();
44+
45+
$results = $pipeline->execute()->toArray();
46+
self::assertCount(2, $results);
47+
self::assertSame('Sara D. Roosevelt Park', $results[0]['name']);
48+
self::assertIsFloat($results[0]['calculated']);
49+
}
50+
51+
public function testGeoNearWithEmptyQuery(): void
52+
{
53+
$pipeline = $this->dm->createAggregationBuilder(Place::class)
54+
->geoNear(-73.99279, 40.719296)
55+
->distanceField('dist.calculated')
56+
->maxDistance(2)
57+
->includeLocs('dist.location')
58+
->spherical(true)
59+
->getAggregation();
60+
61+
$results = $pipeline->execute()->toArray();
62+
self::assertCount(3, $results);
63+
self::assertSame('Sara D. Roosevelt Park', $results[0]['name']);
64+
self::assertIsArray($results[0]['dist']);
65+
self::assertIsArray($results[0]['dist']['location']);
66+
self::assertIsFloat($results[0]['dist']['calculated']);
67+
}
68+
}

tests/Documents/Place.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Documents;
6+
7+
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
8+
9+
/** For testing $geoNear and other geographic operators */
10+
#[ODM\Document(collection: 'places')]
11+
#[ODM\Index(keys: ['location' => '2dsphere'])]
12+
class Place
13+
{
14+
#[ODM\Id]
15+
public ?string $id;
16+
17+
public function __construct(
18+
#[ODM\Field]
19+
public string $name,
20+
// Geometry object
21+
#[ODM\Field]
22+
public array $location,
23+
#[ODM\Field]
24+
public string $category,
25+
) {
26+
}
27+
}

0 commit comments

Comments
 (0)