Skip to content

Commit b545c21

Browse files
authored
Fix $geoNear stage with empty query (#2743)
1 parent b0252b6 commit b545c21

File tree

6 files changed

+107
-3
lines changed

6 files changed

+107
-3
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ private function applyFilters(array|stdClass $query): array|stdClass
723723
$query = $documentPersister->addDiscriminatorToPreparedQuery($query);
724724
$query = $documentPersister->addFilterToPreparedQuery($query);
725725

726+
// An empty array is encoded as a BSON array. We need a BSON document.
726727
return $query === [] ? (object) $query : $query;
727728
}
728729

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

Lines changed: 2 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

@@ -50,7 +51,7 @@ public function getExpression(): array
5051
'near' => $this->near,
5152
'spherical' => $this->spherical,
5253
'distanceField' => $this->distanceField,
53-
'query' => $this->query->getQuery(),
54+
'query' => $this->query->getQuery() ?: new stdClass(),
5455
'distanceMultiplier' => $this->distanceMultiplier,
5556
'includeLocs' => $this->includeLocs,
5657
'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' => (object) [], 'num' => 1];
76-
self::assertEquals([['$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/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Documents\CmsProduct;
1818
use Documents\Comment;
1919
use Documents\File;
20+
use Documents\Place;
2021
use Documents\SchemaValidated;
2122
use Documents\Sharded\ShardedOne;
2223
use Documents\Sharded\ShardedOneWithDifferentKey;
@@ -60,6 +61,7 @@ class SchemaManagerTest extends BaseTestCase
6061
CmsComment::class,
6162
CmsProduct::class,
6263
Comment::class,
64+
Place::class,
6365
SimpleReferenceUser::class,
6466
ShardedOne::class,
6567
ShardedOneWithDifferentKey::class,

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+
/** @param array{type:string, coordinates: list<float|int>} $location Geometry */
18+
public function __construct(
19+
#[ODM\Field]
20+
public string $name,
21+
#[ODM\Field]
22+
public array $location,
23+
#[ODM\Field]
24+
public string $category,
25+
) {
26+
}
27+
}

0 commit comments

Comments
 (0)