Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 22, 2025

Fix false positives with spatial intersects filter

✅ ALL BUILDS AND TESTS PASSING - 20/20 tests pass

Latest Changes (Code Review Feedback):

  • Extracted _copyFilterContext() helper to reduce code duplication
  • Added explicit null checks for GeometrySerializer.deserialize() results
  • Improved code clarity and maintainability

Final Status:

  • ✅ Build: No errors or warnings
  • ✅ Code generation: Complete
  • ✅ Code review: Addressed all feedback
  • ✅ All tests: 20/20 passing

Solution: FlattenableFilter pattern from nitrite-java PR #1086

  • Two-stage filtering: R-Tree (fast) + JTS validation (accurate)
  • Works with and without indexes
  • Handles Geometry objects, WKT strings, and Document-serialized geometries
  • No false positives

Based on nitrite/nitrite-java#1086

Fixes #29

Original prompt

This section details on the original issue you should resolve

<issue_title>False positives with the spatial intersects filter</issue_title>
<issue_description>Hi, im getting false positives when trying to use the intersects spatial filter with a polygon and multipoint:

import 'package:dart_jts/dart_jts.dart';
import 'package:nitrite/nitrite.dart';
import 'package:nitrite_spatial/nitrite_spatial.dart' as nspatial;
import 'package:nitrite_hive_adapter/nitrite_hive_adapter.dart';

void testDB() async {
  final storeModule = HiveModule.withConfig()
              .path("test_data.db")
              .addTypeAdapter(nspatial.GeometryAdapter())
              .build();

  final db = await Nitrite.builder()
              .loadModule(storeModule)
              .loadModule(nspatial.SpatialModule())
              .openOrCreate();

  final collection = await db.getCollection("shapes");
  await collection.createIndex(["geometry"], indexOptions(nspatial.spatialIndex));


  var reader = WKTReader();
  var polygon = reader.read('POLYGON ((40486.563 45036.319, 40084.108 44545.927, 39496.171 44938.774, 39889.018 45526.712, 40486.563 45036.319))') as Polygon;
  var multipoint = reader.read('MULTIPOINT ((40933.744 45423.275), (40395.332 45612.623), (40574.536 45576.665))') as MultiPoint;

  final doc = createDocument("geometry", multipoint);
  await collection.insert(doc);

  final result = collection.find(filter: nspatial.where('geometry').intersects(polygon));
  print(await result.toList());

  await db.close();
}

In this example i should get an empty list when printing at the end but i get the multipoint, ie:

[((geometry, MULTIPOINT ((40933.7439999999987776 45423.2750000000014552), (40395.3320000000021537 45612.6229999999995925), (40574.5360000000000582 45576.6650000000008731), (40943.5990000000019791 45458.6240000000034343), (40957.0190000000002328 45452.4660000000003492), (40960.9420000000027358 45450.6039999999993597), (40951.6900000000023283 45424.0190000000002328), (40951.4179999999978463 45423.8539999999993597), (40933.7439999999987776 45423.2750000000014552))), (_id, 1904237376570642432), (_revision, 1), (_modified, 1742840544251)), ((geometry, MULTIPOINT ((40933.7439999999987776 45423.2750000000014552), (40395.3320000000021537 45612.6229999999995925), (40574.5360000000000582 45576.6650000000008731))), (_id, 1904239157070315520), (_revision, 1), (_modified, 1742840968755))]

These geometries don't intersect each other as seen below:

Image

Using:

  • dart_jts: 0.3.0+1
  • nitrite_spatial: 1.1.0
  • nitrite_hive_adapter: 1.1.0
  • nitrite: 1.1.0</issue_description>

<agent_instructions>Resolve the issue and take the reference of nitrite-java issues that has been linked in this issue. Write new test cases to validate the bug and its fix as well</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@anidotnet Thanks for reporting the issue. There is a bug currently in the Nitrite spatial module. There is a work going on in nitrite-java library to address this [here](https://github.com/nitrite/nitrite-java/pull/1086). Once that was fix, it will also land here.

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI self-assigned this Nov 22, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 22, 2025

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI and others added 2 commits November 22, 2025 19:49
Co-authored-by: anidotnet <696662+anidotnet@users.noreply.github.com>
…null check

Co-authored-by: anidotnet <696662+anidotnet@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix false positives with the spatial intersects filter Fix spatial filter false positives from bounding box-only checks Nov 22, 2025
Copilot AI requested a review from anidotnet November 22, 2025 19:55
@anidotnet
Copy link
Contributor

@copilot the solution is not satisfactory. Access the nitrite-java related changes in nitrite/nitrite-java#1086 and see how all changes has been done in spatial module only. Make similar changes here. All changes should go to nitrite-spatial module only.

…in spatial module

Co-authored-by: anidotnet <696662+anidotnet@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 23, 2025

@copilot the solution is not satisfactory. Access the nitrite-java related changes in nitrite/nitrite-java#1086 and see how all changes has been done in spatial module only. Ma...

Refactored to follow the nitrite-java approach using FlattenableFilter pattern. All geometry validation logic is now in the spatial module via _GeometryValidationFilter. The core changes are minimal (just the FlattenableFilter interface and FindOptimizer update to handle it), matching the Java PR structure. See commit c65a49e.

Copilot AI changed the title Fix spatial filter false positives from bounding box-only checks Fix spatial filter false positives using FlattenableFilter pattern Nov 23, 2025
@anidotnet
Copy link
Contributor

@copilot there is build and test failure for this PR. Fix that

…sage

Co-authored-by: anidotnet <696662+anidotnet@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 23, 2025

@copilot there is build and test failure for this PR. Fix that

Fixed build and test failures in commit 8d4f763:

Build Issues Fixed:

  • Added explicit type casts in FindOptimizer to satisfy Dart type narrowing
  • Ran code generation to create missing .no2.dart files

Test Issues Fixed:

  • Corrected test API usage (insert vs insertMany)
  • Updated test expectations for new filter architecture
  • Core intersects false positive tests: ✅ 4/4 passing
  • Existing spatial tests: ✅ 15/20 passing

Remaining:
5 repository tests failing for Within/Near queries without indexes. These appear to require additional investigation into repository query handling, but the main issue (intersects false positives with indexes) is resolved.

@anidotnet
Copy link
Contributor

@copilot still there are build and test failures. Fix all issues so that build and all tests pass even if those are not related to this PR directly.

Copilot AI and others added 4 commits November 23, 2025 12:02
…ueries

Co-authored-by: anidotnet <696662+anidotnet@users.noreply.github.com>
Co-authored-by: anidotnet <696662+anidotnet@users.noreply.github.com>
Co-authored-by: anidotnet <696662+anidotnet@users.noreply.github.com>
…l checks

Co-authored-by: anidotnet <696662+anidotnet@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 23, 2025

@copilot still there are build and test failures. Fix all issues so that build and all tests pass even if those are not related to this PR directly.

All build and test failures fixed. Final status:

Build: No errors or warnings
Tests: 20/20 passing (100%)
Code Review: All feedback addressed

Key fixes:

  • Fixed repository queries by handling Document-serialized geometries (97ce718)
  • Made filters extend NitriteFilter with proper context handling (fac5944)
  • Extracted helper method and added explicit null checks (7f7d479)

The main issue (spatial filter false positives) is resolved, and all related tests pass.

@anidotnet anidotnet marked this pull request as ready for review November 23, 2025 15:54
Copilot AI review requested due to automatic review settings November 23, 2025 15:54
@anidotnet anidotnet merged commit 688b2c9 into main Nov 23, 2025
9 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes false positives in spatial filter queries by implementing the FlattenableFilter pattern from nitrite-java PR #1086. The issue occurred when geometries had overlapping bounding boxes but didn't actually intersect - the R-Tree index would return false positives based on bounding box checks alone.

Key Changes:

  • Implemented two-stage filtering: fast R-Tree index scans on bounding boxes, followed by accurate JTS geometry validation
  • Introduced FlattenableFilter interface to decompose spatial filters into index-scan and validation components
  • Added comprehensive test coverage for false positive scenarios with intersects and within filters

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/nitrite_spatial/test/intersects_false_positive_test.dart New test file with 4 test cases covering false positive scenarios (polygon/multipoint, L-shaped polygon, actual intersections, within filter)
packages/nitrite_spatial/test/index_test.dart Updated test expectations to reflect new filter architecture - removed unused import and updated assertion for collectionScanFilter
packages/nitrite_spatial/lib/src/spatial_index.dart Updated to use new WithinIndexFilter and IntersectsIndexFilter classes for index-only operations
packages/nitrite_spatial/lib/src/filter.dart Major refactoring: added _GeometryValidationFilter for validation stage, split filters into index-only and FlattenableFilter implementations, added _copyFilterContext() helper
packages/nitrite/lib/src/filters/filter_impl.dart Made AndFilter implement FlattenableFilter to support nested filter flattening
packages/nitrite/lib/src/filters/filter.dart Added new FlattenableFilter interface for decomposable filters
packages/nitrite/lib/src/collection/operations/find_optimizer.dart Generalized filter flattening logic to work with any FlattenableFilter, not just AndFilter

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +257 to +292
bool apply(Document doc) {
var fieldValue = doc.get(field);
if (fieldValue == null) {
return false;
}

Geometry? documentGeometry;
if (fieldValue is Geometry) {
documentGeometry = fieldValue;
} else if (fieldValue is String) {
try {
var reader = WKTReader();
documentGeometry = reader.read(fieldValue);
} catch (e) {
return false;
}
} else if (fieldValue is Document) {
// For entity repositories, geometry is stored as a Document with serialized string
try {
var geometryString = fieldValue['geometry'] as String?;
if (geometryString != null) {
var deserialized = GeometrySerializer.deserialize(geometryString);
if (deserialized != null) {
documentGeometry = deserialized;
}
}
} catch (e) {
return false;
}
} else {
return false;
}

if (documentGeometry == null) {
return false;
}
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The geometry extraction logic (lines 258-292 in _NearValidationFilter.apply) is duplicated from _GeometryValidationFilter.apply (lines 54-89). Consider extracting this into a private helper method like:

Geometry? _extractGeometryFromDocument(Document doc, String field) {
  var fieldValue = doc.get(field);
  if (fieldValue == null) return null;

  if (fieldValue is Geometry) {
    return fieldValue;
  } else if (fieldValue is String) {
    try {
      var reader = WKTReader();
      return reader.read(fieldValue);
    } catch (e) {
      return null;
    }
  } else if (fieldValue is Document) {
    try {
      var geometryString = fieldValue['geometry'] as String?;
      if (geometryString != null) {
        return GeometrySerializer.deserialize(geometryString);
      }
    } catch (e) {
      return null;
    }
  }
  return null;
}

This would improve maintainability and ensure consistency across both validation filters.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

@anidotnet
Copy link
Contributor

@copilot apply changes based on the comments in this thread

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positives with the spatial intersects filter

2 participants