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);