Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
115e341
Spatial
fogelito Sep 10, 2025
745d785
Merge branch 'main' of github.com:utopia-php/database into spatial-en…
fogelito Sep 11, 2025
a313da3
Merge branch 'main' of github.com:utopia-php/database into spatial-en…
fogelito Sep 14, 2025
6f8bdf9
Postgres point
fogelito Sep 14, 2025
4ef5206
decodePoint
fogelito Sep 14, 2025
37db0b1
same decode
fogelito Sep 14, 2025
d01fdbc
decodeLinestring
fogelito Sep 14, 2025
f25ede9
polygon
fogelito Sep 14, 2025
9e9d50a
Postgres linestring
fogelito Sep 14, 2025
4e195f8
Postgres polygon
fogelito Sep 14, 2025
bb4b3e4
Mysql polygon
fogelito Sep 14, 2025
b94dada
clean var_dump
fogelito Sep 14, 2025
7f7dfbb
Remove decodeSpatialData method
fogelito Sep 15, 2025
773df87
Remove try catch
fogelito Sep 15, 2025
f6bd630
Add hints
fogelito Sep 15, 2025
cc972d2
dbg
fogelito Sep 15, 2025
1b9b095
formatting
fogelito Sep 15, 2025
9a0e285
signature
fogelito Sep 15, 2025
5d2c0c3
fix Pool adapter
fogelito Sep 15, 2025
44cc3f3
formatting
fogelito Sep 15, 2025
7849c6c
formatting
fogelito Sep 15, 2025
2aed9dc
fix getAttributeProjection
fogelito Sep 15, 2025
4ed19c6
Runn tests
fogelito Sep 15, 2025
ee7fbc1
decode polygon
fogelito Sep 15, 2025
2648ef9
remove $spatialAttributes
fogelito Sep 15, 2025
a0b963a
unpack
fogelito Sep 15, 2025
cfe9b84
stopOnFailure
fogelito Sep 15, 2025
a1a52f6
fix decode point
fogelito Sep 16, 2025
0275563
Merge branch 'main' of github.com:utopia-php/database into spatial-en…
fogelito Sep 16, 2025
c349c08
DatabaseException
fogelito Sep 16, 2025
96e8a5e
Postgres update point
fogelito Sep 16, 2025
09be1c0
formatting
fogelito Sep 16, 2025
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
3 changes: 1 addition & 2 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
>
stopOnFailure="true">
<testsuites>
<testsuite name="unit">
<directory>./tests/unit</directory>
Expand Down
20 changes: 3 additions & 17 deletions src/Database/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -1324,19 +1324,6 @@ public function delete(?string $database = null): bool
*/
public function createCollection(string $id, array $attributes = [], array $indexes = [], ?array $permissions = null, bool $documentSecurity = true): Document
{
foreach ($attributes as &$attribute) {
if (in_array($attribute['type'], Database::SPATIAL_TYPES)) {
$existingFilters = $attribute['filters'] ?? [];
if (!is_array($existingFilters)) {
$existingFilters = [$existingFilters];
}
$attribute['filters'] = array_values(
array_unique(array_merge($existingFilters, [$attribute['type']]))
);
}
}
unset($attribute);

$permissions ??= [
Permission::create(Role::any()),
];
Expand Down Expand Up @@ -1693,10 +1680,6 @@ public function createAttribute(string $collection, string $id, string $type, in
if ($collection->isEmpty()) {
throw new NotFoundException('Collection not found');
}
if (in_array($type, Database::SPATIAL_TYPES)) {
$filters[] = $type;
$filters = array_unique($filters);
}

$attribute = $this->validateAttribute(
$collection,
Expand Down Expand Up @@ -1988,6 +1971,9 @@ protected function getRequiredFilters(?string $type): array
{
return match ($type) {
self::VAR_DATETIME => ['datetime'],
self::VAR_POINT => ['point'],
self::VAR_POLYGON => ['polygon'],
self::VAR_LINESTRING => ['linestring'],
default => [],
};
}
Expand Down
12 changes: 6 additions & 6 deletions tests/e2e/Adapter/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@

abstract class Base extends TestCase
{
use CollectionTests;
use DocumentTests;
use AttributeTests;
use IndexTests;
use PermissionTests;
use RelationshipTests;
// use CollectionTests;
// use DocumentTests;
// use AttributeTests;
// use IndexTests;
// use PermissionTests;
// use RelationshipTests;
use SpatialTests;
use GeneralTests;

Expand Down
66 changes: 33 additions & 33 deletions tests/e2e/Adapter/Scopes/SpatialTests.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public function testSpatialCollection(): void
$this->assertIsArray($col->getAttribute('indexes'));
$this->assertCount(2, $col->getAttribute('indexes'));

$database->createAttribute($collectionName, 'attribute3', Database::VAR_POINT, 0, true);
$database->createAttribute($collectionName, 'attribute3', Database::VAR_POINT, 0, true, filters: ['point']);
$database->createIndex($collectionName, ID::custom("index3"), Database::INDEX_SPATIAL, ['attribute3']);

$col = $database->getCollection($collectionName);
Expand Down Expand Up @@ -105,9 +105,9 @@ public function testSpatialTypeDocuments(): void
$database->createCollection($collectionName);

// Create spatial attributes using createAttribute method
$this->assertEquals(true, $database->createAttribute($collectionName, 'pointAttr', Database::VAR_POINT, 0, $database->getAdapter()->getSupportForSpatialIndexNull() ? false : true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'lineAttr', Database::VAR_LINESTRING, 0, $database->getAdapter()->getSupportForSpatialIndexNull() ? false : true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'polyAttr', Database::VAR_POLYGON, 0, $database->getAdapter()->getSupportForSpatialIndexNull() ? false : true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'pointAttr', Database::VAR_POINT, 0, $database->getAdapter()->getSupportForSpatialIndexNull() ? false : true, filters: ['point']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'lineAttr', Database::VAR_LINESTRING, 0, $database->getAdapter()->getSupportForSpatialIndexNull() ? false : true, filters: ['linestring']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'polyAttr', Database::VAR_POLYGON, 0, $database->getAdapter()->getSupportForSpatialIndexNull() ? false : true, filters: ['polygon']));

// Create spatial indexes
$this->assertEquals(true, $database->createIndex($collectionName, 'point_spatial', Database::INDEX_SPATIAL, ['pointAttr']));
Expand Down Expand Up @@ -234,7 +234,7 @@ public function testSpatialRelationshipOneToOne(): void
$database->createCollection('building');

$database->createAttribute('location', 'name', Database::VAR_STRING, 255, true);
$database->createAttribute('location', 'coordinates', Database::VAR_POINT, 0, true);
$database->createAttribute('location', 'coordinates', Database::VAR_POINT, 0, true, filters: ['point']);
$database->createAttribute('building', 'name', Database::VAR_STRING, 255, true);
$database->createAttribute('building', 'area', Database::VAR_STRING, 255, true);

Expand Down Expand Up @@ -339,9 +339,9 @@ public function testSpatialAttributes(): void
$database->createCollection($collectionName);

$required = $database->getAdapter()->getSupportForSpatialIndexNull() ? false : true;
$this->assertEquals(true, $database->createAttribute($collectionName, 'pointAttr', Database::VAR_POINT, 0, $required));
$this->assertEquals(true, $database->createAttribute($collectionName, 'lineAttr', Database::VAR_LINESTRING, 0, $required));
$this->assertEquals(true, $database->createAttribute($collectionName, 'polyAttr', Database::VAR_POLYGON, 0, $required));
$this->assertEquals(true, $database->createAttribute($collectionName, 'pointAttr', Database::VAR_POINT, 0, $required, filters: ['point']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'lineAttr', Database::VAR_LINESTRING, 0, $required, filters: ['linestring']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'polyAttr', Database::VAR_POLYGON, 0, $required, filters: ['polygon']));

// Create spatial indexes
$this->assertEquals(true, $database->createIndex($collectionName, 'idx_point', Database::INDEX_SPATIAL, ['pointAttr']));
Expand Down Expand Up @@ -390,7 +390,7 @@ public function testSpatialOneToMany(): void

$database->createAttribute($parent, 'name', Database::VAR_STRING, 255, true);
$database->createAttribute($child, 'name', Database::VAR_STRING, 255, true);
$database->createAttribute($child, 'coord', Database::VAR_POINT, 0, true);
$database->createAttribute($child, 'coord', Database::VAR_POINT, 0, true, filters: ['point']);
$database->createIndex($child, 'coord_spatial', Database::INDEX_SPATIAL, ['coord']);

$database->createRelationship(
Expand Down Expand Up @@ -502,7 +502,7 @@ public function testSpatialManyToOne(): void

$database->createAttribute($parent, 'name', Database::VAR_STRING, 255, true);
$database->createAttribute($child, 'name', Database::VAR_STRING, 255, true);
$database->createAttribute($child, 'coord', Database::VAR_POINT, 0, true);
$database->createAttribute($child, 'coord', Database::VAR_POINT, 0, true, filters: ['point']);
$database->createIndex($child, 'coord_spatial', Database::INDEX_SPATIAL, ['coord']);

$database->createRelationship(
Expand Down Expand Up @@ -606,10 +606,10 @@ public function testSpatialManyToMany(): void
$database->createCollection($b);

$database->createAttribute($a, 'name', Database::VAR_STRING, 255, true);
$database->createAttribute($a, 'home', Database::VAR_POINT, 0, true);
$database->createAttribute($a, 'home', Database::VAR_POINT, 0, true, filters: ['point']);
$database->createIndex($a, 'home_spatial', Database::INDEX_SPATIAL, ['home']);
$database->createAttribute($b, 'title', Database::VAR_STRING, 255, true);
$database->createAttribute($b, 'area', Database::VAR_POLYGON, 0, true);
$database->createAttribute($b, 'area', Database::VAR_POLYGON, 0, true, filters: ['polygon']);
$database->createIndex($b, 'area_spatial', Database::INDEX_SPATIAL, ['area']);

$database->createRelationship(
Expand Down Expand Up @@ -708,7 +708,7 @@ public function testSpatialIndex(): void
$collectionName = 'spatial_index_';
try {
$database->createCollection($collectionName);
$database->createAttribute($collectionName, 'loc', Database::VAR_POINT, 0, true);
$database->createAttribute($collectionName, 'loc', Database::VAR_POINT, 0, true, filters: ['point']);
$this->assertEquals(true, $database->createIndex($collectionName, 'loc_spatial', Database::INDEX_SPATIAL, ['loc']));

$collection = $database->getCollection($collectionName);
Expand Down Expand Up @@ -769,7 +769,7 @@ public function testSpatialIndex(): void
$collOrderIndex = 'spatial_idx_order_index_' . uniqid();
try {
$database->createCollection($collOrderIndex);
$database->createAttribute($collOrderIndex, 'loc', Database::VAR_POINT, 0, true);
$database->createAttribute($collOrderIndex, 'loc', Database::VAR_POINT, 0, true, filters: ['point']);
if ($orderSupported) {
$this->assertTrue($database->createIndex($collOrderIndex, 'idx_loc', Database::INDEX_SPATIAL, ['loc'], [], [Database::ORDER_DESC]));
} else {
Expand Down Expand Up @@ -829,7 +829,7 @@ public function testSpatialIndex(): void
$collNullIndex = 'spatial_idx_null_index_' . uniqid();
try {
$database->createCollection($collNullIndex);
$database->createAttribute($collNullIndex, 'loc', Database::VAR_POINT, 0, false);
$database->createAttribute($collNullIndex, 'loc', Database::VAR_POINT, 0, false, filters: ['point']);
if ($nullSupported) {
$this->assertTrue($database->createIndex($collNullIndex, 'idx_loc', Database::INDEX_SPATIAL, ['loc']));
} else {
Expand Down Expand Up @@ -905,12 +905,12 @@ public function testComplexGeometricShapes(): void
$database->createCollection($collectionName);

// Create spatial attributes for different geometric shapes
$this->assertEquals(true, $database->createAttribute($collectionName, 'rectangle', Database::VAR_POLYGON, 0, true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'square', Database::VAR_POLYGON, 0, true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'triangle', Database::VAR_POLYGON, 0, true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'circle_center', Database::VAR_POINT, 0, true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'complex_polygon', Database::VAR_POLYGON, 0, true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'multi_linestring', Database::VAR_LINESTRING, 0, true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'rectangle', Database::VAR_POLYGON, 0, true, filters: ['polygon']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'square', Database::VAR_POLYGON, 0, true, filters: ['polygon']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'triangle', Database::VAR_POLYGON, 0, true, filters: ['polygon']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'circle_center', Database::VAR_POINT, 0, true, filters: ['point']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'complex_polygon', Database::VAR_POLYGON, 0, true, filters: ['polygon']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'multi_linestring', Database::VAR_LINESTRING, 0, true, filters: ['linestring']));

// Create spatial indexes
$this->assertEquals(true, $database->createIndex($collectionName, 'idx_rectangle', Database::INDEX_SPATIAL, ['rectangle']));
Expand Down Expand Up @@ -1335,9 +1335,9 @@ public function testSpatialQueryCombinations(): void
$database->createCollection($collectionName);

// Create spatial attributes
$this->assertEquals(true, $database->createAttribute($collectionName, 'location', Database::VAR_POINT, 0, true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'area', Database::VAR_POLYGON, 0, true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'route', Database::VAR_LINESTRING, 0, true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'location', Database::VAR_POINT, 0, true, filters: ['point']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'area', Database::VAR_POLYGON, 0, true, filters: ['polygon']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'route', Database::VAR_LINESTRING, 0, true, filters: ['linestring']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'name', Database::VAR_STRING, 255, true));

// Create spatial indexes
Expand Down Expand Up @@ -1766,8 +1766,8 @@ public function testSptialAggregation(): void
// Create collection with spatial and numeric attributes
$database->createCollection($collectionName);
$database->createAttribute($collectionName, 'name', Database::VAR_STRING, 255, true);
$database->createAttribute($collectionName, 'loc', Database::VAR_POINT, 0, true);
$database->createAttribute($collectionName, 'area', Database::VAR_POLYGON, 0, true);
$database->createAttribute($collectionName, 'loc', Database::VAR_POINT, 0, true, filters: ['point']);
$database->createAttribute($collectionName, 'area', Database::VAR_POLYGON, 0, true, filters: ['polygon']);
$database->createAttribute($collectionName, 'score', Database::VAR_INTEGER, 0, true);

// Spatial indexes
Expand Down Expand Up @@ -1855,21 +1855,21 @@ public function testUpdateSpatialAttributes(): void

// 0) Disallow creation of spatial attributes with size or array
try {
$database->createAttribute($collectionName, 'geom_bad_size', Database::VAR_POINT, 10, true);
$database->createAttribute($collectionName, 'geom_bad_size', Database::VAR_POINT, 10, true, filters: ['point']);
$this->fail('Expected DatabaseException when creating spatial attribute with non-zero size');
} catch (\Throwable $e) {
$this->assertInstanceOf(Exception::class, $e);
}

try {
$database->createAttribute($collectionName, 'geom_bad_array', Database::VAR_POINT, 0, true, array: true);
$database->createAttribute($collectionName, 'geom_bad_array', Database::VAR_POINT, 0, true, array: true, filters: ['point']);
$this->fail('Expected DatabaseException when creating spatial attribute with array=true');
} catch (\Throwable $e) {
$this->assertInstanceOf(Exception::class, $e);
}

// Create a single spatial attribute (required=true)
$this->assertEquals(true, $database->createAttribute($collectionName, 'geom', Database::VAR_POINT, 0, true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'geom', Database::VAR_POINT, 0, true, filters: ['point']));
$this->assertEquals(true, $database->createIndex($collectionName, 'idx_geom', Database::INDEX_SPATIAL, ['geom']));

// 1) Disallow size and array updates on spatial attributes: expect DatabaseException
Expand Down Expand Up @@ -1940,9 +1940,9 @@ public function testSpatialAttributeDefaults(): void
$database->createCollection($collectionName);

// Create spatial attributes with defaults and no indexes to avoid nullability/index constraints
$this->assertEquals(true, $database->createAttribute($collectionName, 'pt', Database::VAR_POINT, 0, false, [1.0, 2.0]));
$this->assertEquals(true, $database->createAttribute($collectionName, 'ln', Database::VAR_LINESTRING, 0, false, [[0.0, 0.0], [1.0, 1.0]]));
$this->assertEquals(true, $database->createAttribute($collectionName, 'pg', Database::VAR_POLYGON, 0, false, [[[0.0, 0.0], [0.0, 2.0], [2.0, 2.0], [0.0, 0.0]]]));
$this->assertEquals(true, $database->createAttribute($collectionName, 'pt', Database::VAR_POINT, 0, false, [1.0, 2.0], filters: ['point']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'ln', Database::VAR_LINESTRING, 0, false, [[0.0, 0.0], [1.0, 1.0]], filters: ['linestring']));
$this->assertEquals(true, $database->createAttribute($collectionName, 'pg', Database::VAR_POLYGON, 0, false, [[[0.0, 0.0], [0.0, 2.0], [2.0, 2.0], [0.0, 0.0]]], filters: ['polygon']));

// Create non-spatial attributes (mix of defaults and no defaults)
$this->assertEquals(true, $database->createAttribute($collectionName, 'title', Database::VAR_STRING, 255, false, 'Untitled'));
Expand Down Expand Up @@ -2147,7 +2147,7 @@ public function testSpatialDistanceInMeter(): void
$collectionName = 'spatial_distance_meters_';
try {
$database->createCollection($collectionName);
$this->assertEquals(true, $database->createAttribute($collectionName, 'loc', Database::VAR_POINT, 0, true));
$this->assertEquals(true, $database->createAttribute($collectionName, 'loc', Database::VAR_POINT, 0, true, filters: ['point']));
$this->assertEquals(true, $database->createIndex($collectionName, 'idx_loc', Database::INDEX_SPATIAL, ['loc']));

// Two points roughly ~1000 meters apart by latitude delta (~0.009 deg ≈ 1km)
Expand Down
Loading