Repository: blevesearch/bleve
Pull request #1720 (2022-08-05 05:53:35)

Added intersects unit tests and fixed existing tests.
This PR extends the unit tests for 'intersects', particularly polygon-linestring tests. 
It also fixes some issues with geometry collection unit tests across relations.


Patch:
diff --git a/go.mod b/go.mod
index f654bbba..2db0ad3c 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
 	github.com/RoaringBitmap/roaring v0.9.4
 	github.com/bits-and-blooms/bitset v1.2.0
 	github.com/blevesearch/bleve_index_api v1.0.3
-	github.com/blevesearch/geo v0.1.13
+	github.com/blevesearch/geo v0.1.14
 	github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475
 	github.com/blevesearch/go-porterstemmer v1.0.3
 	github.com/blevesearch/goleveldb v1.0.1
diff --git a/go.sum b/go.sum
index dada1932..f0f7d83e 100644
--- a/go.sum
+++ b/go.sum
@@ -6,8 +6,8 @@ github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjL
 github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
 github.com/blevesearch/bleve_index_api v1.0.3 h1:DDSWaPXOZZJ2BB73ZTWjKxydAugjwywcqU+91AAqcAg=
 github.com/blevesearch/bleve_index_api v1.0.3/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
-github.com/blevesearch/geo v0.1.13 h1:RsY1vfFm81iv1g+uoCQtsOFvKAhZnpOdTOK8JRA6pqw=
-github.com/blevesearch/geo v0.1.13/go.mod h1:cRIvqCdk3cgMhGeHNNe6yPzb+w56otxbfo1FBJfR2Pc=
+github.com/blevesearch/geo v0.1.14 h1:TTDpJN6l9ck/cUYbXSn4aCElNls0Whe44rcQKsB7EfU=
+github.com/blevesearch/geo v0.1.14/go.mod h1:cRIvqCdk3cgMhGeHNNe6yPzb+w56otxbfo1FBJfR2Pc=
 github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:kDy+zgJFJJoJYBvdfBSiZYBbdsUL0XcjHYWezpQBGPA=
 github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:9eJDeqxJ3E7WnLebQUlPD7ZjSce7AnDb9vjGmMCbD0A=
 github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=


Test patch:
diff --git a/search/searcher/geoshape_intersects_test.go b/search/searcher/geoshape_intersects_test.go
index 7027a167..8c403cc7 100644
--- a/search/searcher/geoshape_intersects_test.go
+++ b/search/searcher/geoshape_intersects_test.go
@@ -720,7 +720,7 @@ func TestLinestringPolygonIntersects(t *testing.T) {
 			DocShapeVertices: [][][]float64{{{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}, {0.0, 0.0}}},
 			DocShapeName:     "polygon1",
 			Desc:             "linestring within polygon",
-			Expected:         nil, // nil since linestring intersects polygon only if it intersects any of the polygons edges
+			Expected:         []string{"polygon1"},
 		},
 		{
 			QueryShape:       [][]float64{{-0.5, 0.5}, {0.5, 0.5}},
@@ -893,7 +893,7 @@ func TestMultiLinestringIntersects(t *testing.T) {
 		},
 		{
 			QueryShape:       [][][]float64{{{1.0, 1.0}, {1.1, 1.1}, {2.0, 2.0}, {2.1, 2.1}}},
-			DocShapeVertices: [][][]float64{{{100.1, 100.5}, {101.5, 102.5}}},
+			DocShapeVertices: [][][]float64{{{10.1, 100.5}, {11.5, 102.5}}},
 			DocShapeName:     "multilinestring1",
 			Desc:             "non-intersecting multilinestrings",
 			Expected:         nil,
@@ -1067,6 +1067,152 @@ func TestPolygonIntersects(t *testing.T) {
 	}
 }
 
+func TestPolygonLinestringIntersects(t *testing.T) {
+	tests := []struct {
+		QueryShape       [][][]float64
+		DocShapeVertices [][]float64
+		DocShapeName     string
+		Desc             string
+		Expected         []string
+	}{
+		{
+			QueryShape:       [][][]float64{{{150, 85}, {160, 85}, {-20, 85}, {-30, 85}, {150, 85}}},
+			DocShapeVertices: [][]float64{{150, 85}, {160, 85}},
+			DocShapeName:     "linestring1",
+			Desc:             "polygon intersects line along edge",
+			Expected:         []string{"linestring1"},
+		},
+		{
+			QueryShape:       [][][]float64{{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}},
+			DocShapeVertices: [][]float64{{150, 85}, {160, 85}},
+			DocShapeName:     "linestring1",
+			Desc:             "polygon not intersecting line",
+			Expected:         nil,
+		},
+		{
+			QueryShape:       [][][]float64{{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}},
+			DocShapeVertices: [][]float64{{0.2, 0.2}, {0.4, 0.4}},
+			DocShapeName:     "linestring1",
+			Desc:             "polygon completely encloses line",
+			Expected:         []string{"linestring1"},
+		},
+		{
+			QueryShape:       [][][]float64{{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}},
+			DocShapeVertices: [][]float64{{-0.5, 0.5}, {1.5, 0.5}},
+			DocShapeName:     "linestring1",
+			Desc:             "line cuts through entire polygon",
+			Expected:         []string{"linestring1"},
+		},
+		{
+			QueryShape:       [][][]float64{{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}},
+			DocShapeVertices: [][]float64{{-0.439, -0.318}, {0.4339, 0.335}},
+			DocShapeName:     "linestring1",
+			Desc:             "line partially cuts through polygon",
+			Expected:         []string{"linestring1"},
+		},
+	}
+
+	i := setupIndex(t)
+
+	for _, test := range tests {
+		indexReader, closeFn, err := testCaseSetup(t, test.DocShapeName, "linestring",
+			[][][][]float64{{test.DocShapeVertices}}, i)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+
+		t.Run(test.Desc, func(t *testing.T) {
+			got, err := runGeoShapePolygonQueryWithRelation("intersects",
+				indexReader, test.QueryShape, "geometry")
+			if err != nil {
+				t.Fatal(err)
+			}
+			if !reflect.DeepEqual(got, test.Expected) {
+				t.Errorf("expected %v, got %v for polygon: %+v",
+					test.Expected, got, test.QueryShape)
+			}
+		})
+
+		err = closeFn()
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+	}
+}
+
+func TestPolygonMultiLinestringIntersects(t *testing.T) {
+	tests := []struct {
+		QueryShape       [][][]float64
+		DocShapeVertices [][][]float64
+		DocShapeName     string
+		Desc             string
+		Expected         []string
+	}{
+		{
+			QueryShape:       [][][]float64{{{150, 85}, {160, 85}, {-20, 85}, {-30, 85}, {150, 85}}},
+			DocShapeVertices: [][][]float64{{{150, 85}, {160, 85}}, {{0, 1}, {5, 10}}},
+			DocShapeName:     "multilinestring1",
+			Desc:             "polygon intersects one line along edge",
+			Expected:         []string{"multilinestring1"},
+		},
+		{
+			QueryShape:       [][][]float64{{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}},
+			DocShapeVertices: [][][]float64{{{150, 85}, {160, 85}}},
+			DocShapeName:     "multilinestring1",
+			Desc:             "polygon not intersecting any line",
+			Expected:         nil,
+		},
+		{
+			QueryShape:       [][][]float64{{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}},
+			DocShapeVertices: [][][]float64{{{0.2, 0.2}, {0.4, 0.4}}},
+			DocShapeName:     "multilinestring1",
+			Desc:             "polygon completely encloses line",
+			Expected:         []string{"multilinestring1"},
+		},
+		{
+			QueryShape:       [][][]float64{{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}},
+			DocShapeVertices: [][][]float64{{{-0.5, 0.5}, {1.5, 0.5}}},
+			DocShapeName:     "multilinestring1",
+			Desc:             "line cuts through entire polygon",
+			Expected:         []string{"multilinestring1"},
+		},
+		{
+			QueryShape:       [][][]float64{{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}},
+			DocShapeVertices: [][][]float64{{{-0.439, -0.318}, {0.4339, 0.335}}},
+			DocShapeName:     "multilinestring1",
+			Desc:             "line partially cuts through polygon",
+			Expected:         []string{"multilinestring1"},
+		},
+	}
+
+	i := setupIndex(t)
+
+	for _, test := range tests {
+		indexReader, closeFn, err := testCaseSetup(t, test.DocShapeName, "multilinestring",
+			[][][][]float64{test.DocShapeVertices}, i)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+
+		t.Run(test.Desc, func(t *testing.T) {
+			got, err := runGeoShapePolygonQueryWithRelation("intersects",
+				indexReader, test.QueryShape, "geometry")
+			if err != nil {
+				t.Fatal(err)
+			}
+			if !reflect.DeepEqual(got, test.Expected) {
+				t.Errorf("expected %v, got %v for polygon: %+v",
+					test.Expected, got, test.QueryShape)
+			}
+		})
+
+		err = closeFn()
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+	}
+}
+
 func TestPolygonPointIntersects(t *testing.T) {
 	tests := []struct {
 		QueryShape       [][][]float64
@@ -1286,12 +1432,12 @@ func TestGeometryCollectionIntersects(t *testing.T) {
 		Types            []string
 	}{
 		{
-			QueryShape:       nil,
-			DocShapeVertices: nil,
+			QueryShape:       [][][][][]float64{{{{}}}},
+			DocShapeVertices: [][][][][]float64{{{{}}}},
 			DocShapeName:     "geometrycollection1",
 			Desc:             "empty geometry collections",
 			Expected:         nil,
-			Types:            nil,
+			Types:            []string{""},
 		},
 	}
 
@@ -1308,7 +1454,7 @@ func TestGeometryCollectionIntersects(t *testing.T) {
 			got, err := runGeoShapeGeometryCollectionRelationQuery("intersects",
 				indexReader, test.QueryShape, test.Types, "geometry")
 			if err != nil {
-				t.Fatal(err)
+				t.Errorf(err.Error())
 			}
 			if !reflect.DeepEqual(got, test.Expected) {
 				t.Errorf("expected %v, got %v for geometry collection: %+v",
@@ -1364,7 +1510,7 @@ func TestGeometryCollectionPointIntersects(t *testing.T) {
 		indexReader, closeFn, err := testCaseSetup(t, test.DocShapeName, "point",
 			[][][][]float64{{{test.DocShapeVertices}}}, i)
 		if err != nil {
-			t.Errorf(err.Error())
+			t.Fatal(err.Error())
 		}
 
 		t.Run(test.Desc, func(t *testing.T) {
@@ -1504,11 +1650,11 @@ func TestPointGeometryCollectionIntersects(t *testing.T) {
 	}{
 		{
 			QueryShape:       []float64{1.0, 2.0},
-			DocShapeVertices: nil,
+			DocShapeVertices: [][][][][]float64{{{{}}}},
 			DocShapeName:     "geometrycollection1",
 			Desc:             "geometry collection does not intersect with a point",
 			Expected:         nil,
-			Types:            nil,
+			Types:            []string{""},
 		},
 		{
 			QueryShape:       []float64{1.0, 2.0},
diff --git a/search/searcher/geoshape_within_test.go b/search/searcher/geoshape_within_test.go
index c10f8892..6d350b05 100644
--- a/search/searcher/geoshape_within_test.go
+++ b/search/searcher/geoshape_within_test.go
@@ -15,6 +15,7 @@
 package searcher
 
 import (
+	"fmt"
 	"reflect"
 	"testing"
 
@@ -23,19 +24,24 @@ import (
 )
 
 var (
-	leftRect             [][][]float64   = [][][]float64{{{-1, 0}, {0, 0}, {0, 1}, {-1, 1}, {-1, 0}}}
-	leftRectPoint        []float64       = []float64{-0.5, 0.5}
-	rightRect            [][][]float64   = [][][]float64{{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}}
-	rightRectPoint       []float64       = []float64{0.5, 0.5}
-	overLappingRightRect [][][][]float64 = [][][][]float64{{{{-1, 0}, {1, 0}, {1, 1}, {-0.1, 1}, {-1, 0}}}}
-	middleLine           [][]float64     = [][]float64{{-0.5, 0.5}, {0.5, 0.5}}
+	leftRect       [][][]float64 = [][][]float64{{{-1, 0}, {0, 0}, {0, 1}, {-1, 1}, {-1, 0}}}
+	leftRectPoint  []float64     = []float64{-0.5, 0.5}
+	rightRect      [][][]float64 = [][][]float64{{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}}
+	rightRectPoint []float64     = []float64{0.5, 0.5}
 )
 
 func testCaseSetupGeometryCollection(t *testing.T, docShapeName string, types []string, docShapeVertices [][][][][]float64,
 	i index.Index) (index.IndexReader, func() error, error) {
 	doc := document.NewDocument(docShapeName)
-	doc.AddField(document.NewGeometryCollectionFieldWithIndexingOptions("geometry",
-		[]uint64{}, docShapeVertices, types, document.DefaultGeoShapeIndexingOptions))
+	gcField := document.NewGeometryCollectionFieldWithIndexingOptions("geometry",
+		[]uint64{}, docShapeVertices, types, document.DefaultGeoShapeIndexingOptions)
+	if gcField == nil {
+		return nil, nil, fmt.Errorf("the GC field is nil")
+	}
+	doc.AddField(gcField)
+	if doc == nil {
+		return nil, nil, fmt.Errorf("the doc is nil")
+	}
 	err := i.Update(doc)
 	if err != nil {
 		t.Errorf(err.Error())
@@ -675,7 +681,6 @@ func TestLinestringGeometryCollectionWithin(t *testing.T) {
 			Types:            []string{"point"},
 			QueryType:        "within",
 		},
-		{},
 	}
 
 	i := setupIndex(t)
@@ -1210,14 +1215,14 @@ func TestGeometryCollectionWithin(t *testing.T) {
 		DocShapeTypes    []string
 	}{
 		{
-			QueryShape:       nil,
-			DocShapeVertices: nil,
+			QueryShape:       [][][][][]float64{{{{}}}},
+			DocShapeVertices: [][][][][]float64{{{{}}}},
 			DocShapeName:     "geometrycollection1",
 			Desc:             "empty geometry collections",
 			Expected:         nil,
 			QueryType:        "within",
-			QueryShapeTypes:  nil,
-			DocShapeTypes:    nil,
+			QueryShapeTypes:  []string{""},
+			DocShapeTypes:    []string{""},
 		},
 		{
 			QueryShape:       [][][][][]float64{{{{{1, 2}, {2, 3}}}}},
@@ -1281,11 +1286,11 @@ func TestGeometryCollectionPointWithin(t *testing.T) {
 	}{
 		{
 			QueryShape:       []float64{1.0, 2.0},
-			DocShapeVertices: nil,
+			DocShapeVertices: [][][][][]float64{{{{}}}},
 			DocShapeName:     "geometrycollection1",
 			Desc:             "empty geometry collection not within a point",
 			Expected:         nil,
-			Types:            nil,
+			Types:            []string{""},
 			QueryType:        "within",
 		},
 	}