diff --git a/src/EFCore.PG.NTS/Extensions/NpgsqlNetTopologySuiteDbFunctionsExtensions.cs b/src/EFCore.PG.NTS/Extensions/NpgsqlNetTopologySuiteDbFunctionsExtensions.cs index 4dabe27a1..4eb33058e 100644 --- a/src/EFCore.PG.NTS/Extensions/NpgsqlNetTopologySuiteDbFunctionsExtensions.cs +++ b/src/EFCore.PG.NTS/Extensions/NpgsqlNetTopologySuiteDbFunctionsExtensions.cs @@ -7,6 +7,13 @@ namespace Microsoft.EntityFrameworkCore; /// public static class NpgsqlNetTopologySuiteDbFunctionsExtensions { + /// + /// Checks whether the 2D bounding boxes of two geometries intersect. + /// Translates to the PostGIS && operator. + /// + public static bool IntersectsBbox(this DbFunctions _, Geometry geometry, Geometry anotherGeometry) + => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(IntersectsBbox))); + /// /// Returns a new geometry with its coordinates transformed to a different spatial reference system. /// Translates to ST_Transform(geometry, srid). diff --git a/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMethodCallTranslatorPlugin.cs b/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMethodCallTranslatorPlugin.cs index b6cd534eb..93b0b5c52 100644 --- a/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMethodCallTranslatorPlugin.cs +++ b/src/EFCore.PG.NTS/Query/ExpressionTranslators/Internal/NpgsqlNetTopologySuiteMethodCallTranslatorPlugin.cs @@ -122,6 +122,11 @@ var t when typeof(Geometry).IsAssignableFrom(t) && instance is not null method.ReturnType, arguments[1].TypeMapping), + nameof(NpgsqlNetTopologySuiteDbFunctionsExtensions.IntersectsBbox) => _sqlExpressionFactory.MakePostgresBinary( + PgExpressionType.Overlaps, + arguments[1], + arguments[2]), + nameof(NpgsqlNetTopologySuiteDbFunctionsExtensions.DistanceKnn) => _sqlExpressionFactory.MakePostgresBinary( PgExpressionType.Distance, arguments[1], diff --git a/test/EFCore.PG.FunctionalTests/Migrations/MigrationsInfrastructureNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Migrations/MigrationsInfrastructureNpgsqlTest.cs index 3885e461b..ea94d626e 100644 --- a/test/EFCore.PG.FunctionalTests/Migrations/MigrationsInfrastructureNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Migrations/MigrationsInfrastructureNpgsqlTest.cs @@ -220,4 +220,4 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseNpgsql(TestEnvironment.DefaultConnection); public DbSet Blogs { get; set; } -} \ No newline at end of file +} diff --git a/test/EFCore.PG.FunctionalTests/Query/SpatialQueryNpgsqlGeometryTest.cs b/test/EFCore.PG.FunctionalTests/Query/SpatialQueryNpgsqlGeometryTest.cs index 8f3eb1d61..4af518795 100644 --- a/test/EFCore.PG.FunctionalTests/Query/SpatialQueryNpgsqlGeometryTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/SpatialQueryNpgsqlGeometryTest.cs @@ -363,6 +363,32 @@ public override async Task Intersection(bool async) """); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public async Task IntersectsBbox(bool async) + { + var polygon = Fixture.GeometryFactory.CreatePolygon([new Coordinate(0, 0), new Coordinate(1, 0), new Coordinate(0, 1), new Coordinate(0, 0)]); + + await AssertQuery( + async, + ss => ss.Set().Select(e => new { e.Id, IntersectsBbox = (bool?)EF.Functions.IntersectsBbox(e.Polygon, polygon) }), + ss => ss.Set().Select(e => new { e.Id, IntersectsBbox = (e.Polygon == null ? (bool?)null : e.Polygon.EnvelopeInternal.Intersects(polygon.EnvelopeInternal)) }), + elementSorter: e => e.Id, + elementAsserter: (e, a) => + { + Assert.Equal(e.Id, a.Id); + Assert.Equal(e.IntersectsBbox, a.IntersectsBbox); + }); + + AssertSql( + """ +@polygon='POLYGON ((0 0, 1 0, 0 1, 0 0))' (DbType = Object) + +SELECT p."Id", p."Polygon" && @polygon AS "IntersectsBbox" +FROM "PolygonEntity" AS p +"""); + } + public override async Task Intersects(bool async) { await base.Intersects(async);