Repository: blevesearch/bleve
Pull request #1684 (2022-05-19 07:32:36)

Refactoring the geo shape implementations
-Moving the shape implementations to blevesearch/geo.
-Changed the byte representation of the shapes on disk
 to save marshal/unmarshal efforts between struct types.
-Latest implementation leverages s2's encode/decode methods.
-Adopting different region term indexer's options for better
 indexing and query performance.

sreekanth-cb: Query profiles BEFORE

<img width="1704" alt="Screenshot 2022-05-19 at 1 35 52 PM" src="https://user-images.githubusercontent.com/25240115/169246921-2913d063-f902-4b08-a749-275987379dbf.png">


Query profiles AFTER

<img width="1667" alt="Screenshot 2022-05-19 at 1 51 01 PM" src="https://user-images.githubusercontent.com/25240115/169247455-ce249766-adf4-4c45-81b3-4966510ced5e.png">



Patch:
diff --git a/document/field_geoshape.go b/document/field_geoshape.go
index 061ff3f9..31c578c6 100644
--- a/document/field_geoshape.go
+++ b/document/field_geoshape.go
@@ -130,6 +130,10 @@ func NewGeoShapeFieldWithIndexingOptions(name string, arrayPositions []uint64,
 		return nil
 	}
 
+	// extra glue bytes to work around the term splitting logic from interfering
+	// the custom encoding of the geoshape coordinates inside the docvalues.
+	value = append(geo.GlueBytes, append(value, geo.GlueBytes...)...)
+
 	options = options | DefaultGeoShapeIndexingOptions
 
 	return &GeoShapeField{
@@ -150,6 +154,10 @@ func NewGeometryCollectionFieldWithIndexingOptions(name string,
 		return nil
 	}
 
+	// extra glue bytes to work around the term splitting logic from interfering
+	// the custom encoding of the geoshape coordinates inside the docvalues.
+	value = append(geo.GlueBytes, append(value, geo.GlueBytes...)...)
+
 	options = options | DefaultGeoShapeIndexingOptions
 
 	return &GeoShapeField{
@@ -170,6 +178,10 @@ func NewGeoCircleFieldWithIndexingOptions(name string, arrayPositions []uint64,
 		return nil
 	}
 
+	// extra glue bytes to work around the term splitting logic from interfering
+	// the custom encoding of the geoshape coordinates inside the docvalues.
+	value = append(geo.GlueBytes, append(value, geo.GlueBytes...)...)
+
 	options = options | DefaultGeoShapeIndexingOptions
 
 	return &GeoShapeField{
diff --git a/geo/geo_s2_util.go b/geo/geo_s2_util.go
deleted file mode 100644
index 8c149df1..00000000
--- a/geo/geo_s2_util.go
+++ /dev/null
@@ -1,382 +0,0 @@
-//  Copyright (c) 2022 Couchbase, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 		http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package geo
-
-import (
-	"strings"
-
-	index "github.com/blevesearch/bleve_index_api"
-	"github.com/blevesearch/geo/s2"
-	"github.com/golang/geo/s1"
-)
-
-// ------------------------------------------------------------------------
-
-func polylineIntersectsPoint(pls []*s2.Polyline,
-	point []float64) bool {
-	s2cell := s2.CellFromLatLng(s2.LatLngFromDegrees(
-		point[1], point[0]))
-
-	for _, pl := range pls {
-		if pl.IntersectsCell(s2cell) {
-			return true
-		}
-	}
-
-	return false
-}
-
-func polylineIntersectsPolygons(pls []*s2.Polyline,
-	coordinates [][][][]float64) bool {
-	for _, pl := range pls {
-		for _, vertices := range coordinates {
-			s2pgn := s2PolygonFromCoordinates(vertices)
-			for i := 0; i < pl.NumEdges(); i++ {
-				edge := pl.Edge(i)
-				a := []float64{edge.V0.X, edge.V0.Y}
-				b := []float64{edge.V1.X, edge.V1.Y}
-
-				for i := 0; i < s2pgn.NumEdges(); i++ {
-					edgeB := s2pgn.Edge(i)
-
-					c := []float64{edgeB.V0.X, edgeB.V0.Y}
-					d := []float64{edgeB.V1.X, edgeB.V1.Y}
-
-					if doIntersect(a, b, c, d) {
-						return true
-					}
-				}
-			}
-		}
-	}
-
-	return false
-}
-
-func polylineIntersectsPolylines(pls []*s2.Polyline,
-	coordinates [][][]float64) bool {
-	var plsInDoc []*s2.Polyline
-	for _, lines := range coordinates {
-		latlngs := make([]s2.LatLng, 0, len(lines))
-		for _, line := range lines {
-			latlngs = append(latlngs, s2.LatLngFromDegrees(line[1], line[0]))
-		}
-		plsInDoc = append(plsInDoc, s2.PolylineFromLatLngs(latlngs))
-	}
-
-	for _, pl := range pls {
-		for _, pl2 := range plsInDoc {
-			if pl.Intersects(pl2) {
-				return true
-			}
-		}
-	}
-
-	return false
-}
-
-func geometryCollectionIntersectsShape(gc *geometryCollection,
-	shapeIn index.GeoJSON) bool {
-	for _, shape := range gc.Members() {
-		intersects, err := shapeIn.Intersects(shape)
-		if err == nil && intersects {
-			return true
-		}
-	}
-	return false
-}
-
-func polylinesContainPoints(lineVertices [][][]float64,
-	coordinates [][]float64) bool {
-	// check the intersection for every point in the array.
-	lookup := make(map[int]struct{})
-NextPoint:
-	for pos, points := range coordinates {
-		s2point := s2.PointFromLatLng(
-			s2.LatLngFromDegrees(points[1], points[0]))
-
-		for _, lines := range lineVertices {
-			for _, point := range lines {
-
-				linePoint := s2.PointFromLatLng(
-					s2.LatLngFromDegrees(point[1], point[0]))
-
-				if linePoint.ContainsPoint(s2point) {
-					lookup[pos] = struct{}{}
-					continue NextPoint
-				}
-			}
-		}
-	}
-
-	return len(coordinates) == len(lookup)
-}
-
-func polygonsIntersectsLinestrings(s2pgn *s2.Polygon,
-	lineVertices [][][]float64) bool {
-	for _, vertices := range lineVertices {
-		t1 := s2.PointFromLatLng(s2.LatLngFromDegrees(
-			vertices[0][1], vertices[0][0]))
-		t2 := s2.PointFromLatLng(s2.LatLngFromDegrees(
-			vertices[1][1], vertices[1][0]))
-		a := []float64{t1.X, t1.Y}
-		b := []float64{t2.X, t2.Y}
-
-		for i := 0; i < s2pgn.NumEdges(); i++ {
-			edgeB := s2pgn.Edge(i)
-
-			c := []float64{edgeB.V0.X, edgeB.V0.Y}
-			d := []float64{edgeB.V1.X, edgeB.V1.Y}
-
-			if doIntersect(a, b, c, d) {
-				return true
-			}
-		}
-	}
-	return false
-}
-
-func polygonsContainsLineStrings(s2pgns []*s2.Polygon,
-	lineVertices [][][]float64) bool {
-	linesWithIn := make(map[int]struct{})
-nextLine:
-	for lineIndex, points := range lineVertices {
-		start := s2.PointFromLatLng(s2.LatLngFromDegrees(
-			points[0][1], points[0][0]))
-		end := s2.PointFromLatLng(s2.LatLngFromDegrees(
-			points[1][1], points[1][0]))
-
-		// check whether both the end vertices are inside the polygon.
-		for _, s2pgn := range s2pgns {
-			if s2pgn.ContainsPoint(start) && s2pgn.ContainsPoint(end) {
-				// if both endpoints lie within the polygon then check
-				// for any edge intersections to confirm the containment.
-				for i := 0; i < s2pgn.NumEdges(); i++ {
-					edgeA := s2pgn.Edge(i)
-					a := []float64{edgeA.V0.X, edgeA.V0.Y}
-					b := []float64{edgeA.V1.X, edgeA.V1.Y}
-					c := []float64{start.X, start.Y}
-					d := []float64{end.X, end.Y}
-					if doIntersect(a, b, c, d) {
-						continue nextLine
-					}
-				}
-				linesWithIn[lineIndex] = struct{}{}
-				continue nextLine
-			}
-		}
-	}
-
-	return len(lineVertices) == len(linesWithIn)
-}
-
-func rectangleIntersectsWithPolygons(s2rect *s2.Rect,
-	coordinates [][][][]float64) bool {
-	s2pgnFromRect := s2PolygonFromS2Rectangle(s2rect)
-	for _, pgnVertices := range coordinates {
-		s2pgn := s2PolygonFromCoordinates(pgnVertices)
-		if s2pgn.Intersects(s2pgnFromRect) {
-			return true
-		}
-	}
-
-	return false
-}
-
-func rectangleIntersectsWithLineStrings(s2rect *s2.Rect,
-	coordinates [][][]float64) bool {
-
-	var polylines []*s2.Polyline
-
-	for _, lines := range coordinates {
-		var latlngs []s2.LatLng
-		for _, line := range lines {
-			v := s2.LatLngFromDegrees(line[1], line[0])
-			latlngs = append(latlngs, v)
-		}
-		pl := s2.PolylineFromLatLngs(latlngs)
-		polylines = append(polylines, pl)
-	}
-
-	for _, pl := range polylines {
-		for i := 0; i < pl.NumEdges(); i++ {
-			edgeA := pl.Edge(i)
-			a := []float64{edgeA.V0.X, edgeA.V0.Y}
-			b := []float64{edgeA.V1.X, edgeA.V1.Y}
-
-			for j := 0; j < 4; j++ {
-				v1 := s2.PointFromLatLng(s2rect.Vertex(j))
-				v2 := s2.PointFromLatLng(s2rect.Vertex((j + 1) % 4))
-
-				c := []float64{v1.X, v1.Y}
-				d := []float64{v2.X, v2.Y}
-
-				if doIntersect(a, b, c, d) {
-					return true
-				}
-			}
-		}
-	}
-
-	return false
-}
-
-func s2PolygonFromCoordinates(coordinates [][][]float64) *s2.Polygon {
-	loops := make([]*s2.Loop, 0, len(coordinates))
-	for _, loop := range coordinates {
-		var points []s2.Point
-		for _, point := range loop {
-			p := s2.PointFromLatLng(s2.LatLngFromDegrees(point[1], point[0]))
-			points = append(points, p)
-		}
-		loops = append(loops, s2.LoopFromPoints(points))
-	}
-
-	return s2.PolygonFromOrientedLoops(loops)
-}
-
-func s2PolygonFromS2Rectangle(s2rect *s2.Rect) *s2.Polygon {
-	loops := make([]*s2.Loop, 0, 1)
-	var points []s2.Point
-	for j := 0; j <= 4; j++ {
-		points = append(points, s2.PointFromLatLng(s2rect.Vertex(j%4)))
-	}
-
-	loops = append(loops, s2.LoopFromPoints(points))
-	return s2.PolygonFromLoops(loops)
-}
-
-func deduplicateTerms(terms []string) []string {
-	var rv []string
-	hash := make(map[string]struct{}, len(terms))
-	for _, term := range terms {
-		if _, exists := hash[term]; !exists {
-			rv = append(rv, term)
-			hash[term] = struct{}{}
-		}
-	}
-
-	return rv
-}
-
-//----------------------------------------------------------------------
-
-var earthRadiusInMeter = 6378137.0
-
-func radiusInMetersToS1Angle(radius float64) s1.Angle {
-	return s1.Angle(radius / earthRadiusInMeter)
-}
-
-func s2PolylinesFromCoordinates(coordinates [][][]float64) []*s2.Polyline {
-	var polylines []*s2.Polyline
-	for _, lines := range coordinates {
-		var latlngs []s2.LatLng
-		for _, line := range lines {
-			v := s2.LatLngFromDegrees(line[1], line[0])
-			latlngs = append(latlngs, v)
-		}
-		polylines = append(polylines, s2.PolylineFromLatLngs(latlngs))
-	}
-	return polylines
-}
-
-func s2RectFromBounds(topLeft, bottomRight []float64) *s2.Rect {
-	rect := s2.EmptyRect()
-	rect = rect.AddPoint(s2.LatLngFromDegrees(topLeft[1], topLeft[0]))
-	rect = rect.AddPoint(s2.LatLngFromDegrees(bottomRight[1], bottomRight[0]))
-	return &rect
-}
-
-func s2Cap(vertices []float64, radiusInMeter float64) *s2.Cap {
-	cp := s2.PointFromLatLng(s2.LatLngFromDegrees(vertices[1], vertices[0]))
-	angle := radiusInMetersToS1Angle(float64(radiusInMeter))
-	cap := s2.CapFromCenterAngle(cp, angle)
-	return &cap
-}
-
-func max(a, b float64) float64 {
-	if a >= b {
-		return a
-	}
-	return b
-}
-
-func min(a, b float64) float64 {
-	if a >= b {
-		return b
-	}
-	return a
-}
-
-func onsegment(p, q, r []float64) bool {
-	if q[0] <= max(p[0], r[0]) && q[0] >= min(p[0], r[0]) &&
-		q[1] <= max(p[1], r[1]) && q[1] >= min(p[1], r[1]) {
-		return true
-	}
-
-	return false
-}
-
-func doIntersect(p1, q1, p2, q2 []float64) bool {
-	o1 := orientation(p1, q1, p2)
-	o2 := orientation(p1, q1, q2)
-	o3 := orientation(p2, q2, p1)
-	o4 := orientation(p2, q2, q1)
-
-	if o1 != o2 && o3 != o4 {
-		return true
-	}
-
-	if o1 == 0 && onsegment(p1, p2, q1) {
-		return true
-	}
-
-	if o2 == 0 && onsegment(p1, q2, q1) {
-		return true
-	}
-
-	if o3 == 0 && onsegment(p2, p1, q2) {
-		return true
-	}
-
-	if o4 == 0 && onsegment(p2, q1, q2) {
-		return true
-	}
-
-	return false
-}
-
-func orientation(p, q, r []float64) int {
-	val := (q[1]-p[1])*(r[0]-q[0]) - (q[0]-p[0])*(r[1]-q[1])
-	if val == 0 {
-		return 0
-	}
-	if val > 0 {
-		return 1
-	}
-	return 2
-}
-
-func stripCoveringTerms(terms []string) []string {
-	rv := make([]string, 0, len(terms))
-	for _, term := range terms {
-		if strings.HasPrefix(term, "$") {
-			rv = append(rv, term[1:])
-			continue
-		}
-		rv = append(rv, term)
-	}
-	return deduplicateTerms(rv)
-}
diff --git a/geo/geo_s2plugin_impl.go b/geo/geo_s2plugin_impl.go
index ed570bdd..e1a3d42d 100644
--- a/geo/geo_s2plugin_impl.go
+++ b/geo/geo_s2plugin_impl.go
@@ -15,19 +15,46 @@
 package geo
 
 import (
+	"encoding/json"
 	"sync"
 
 	index "github.com/blevesearch/bleve_index_api"
 
+	"github.com/blevesearch/geo/geojson"
 	"github.com/blevesearch/geo/s2"
 )
 
+const (
+	PointType              = "point"
+	MultiPointType         = "multipoint"
+	LineStringType         = "linestring"
+	MultiLineStringType    = "multilinestring"
+	PolygonType            = "polygon"
+	MultiPolygonType       = "multipolygon"
+	GeometryCollectionType = "geometrycollection"
+	CircleType             = "circle"
+	EnvelopeType           = "envelope"
+)
+
 // spatialPluginsMap is spatial plugin cache.
 var (
 	spatialPluginsMap = make(map[string]index.SpatialAnalyzerPlugin)
 	pluginsMapLock    = sync.RWMutex{}
 )
 
+func init() {
+	registerS2RegionTermIndexer()
+}
+
+func registerS2RegionTermIndexer() {
+	spatialPlugin := S2SpatialAnalyzerPlugin{
+		s2Indexer:  s2.NewRegionTermIndexerWithOptions(initS2IndexerOptions()),
+		s2Searcher: s2.NewRegionTermIndexerWithOptions(initS2SearcherOptions()),
+	}
+
+	RegisterSpatialAnalyzerPlugin(&spatialPlugin)
+}
+
 // RegisterSpatialAnalyzerPlugin registers the given plugin implementation.
 func RegisterSpatialAnalyzerPlugin(plugin index.SpatialAnalyzerPlugin) {
 	pluginsMapLock.Lock()
@@ -43,41 +70,53 @@ func GetSpatialAnalyzerPlugin(typ string) index.SpatialAnalyzerPlugin {
 	return rv
 }
 
-func init() {
-	registerS2RegionTermIndexer()
-}
+func initS2IndexerOptions() s2.Options {
+	options := s2.Options{}
+	// maxLevel control the maximum size of the
+	// S2Cells used to approximate regions.
+	options.SetMaxLevel(16)
 
-func registerS2RegionTermIndexer() {
-	// refer for detailed commentary on s2 options here
-	// https://github.com/blevesearch/geo/blob/5734244d948a0ccf9a6b00e02d1a05f94cbc8849/s2/region_term_indexer.go#L92
-	options := &s2.Options{}
+	// minLevel control the minimum size of the
+	// S2Cells used to approximate regions.
+	options.SetMinLevel(2)
+
+	// levelMod value greater than 1 increases the effective branching
+	// factor of the S2Cell hierarchy by skipping some levels.
+	options.SetLevelMod(1)
+
+	// maxCells controls the maximum number of cells
+	// when approximating each s2 region.
+	options.SetMaxCells(20)
+
+	return options
+}
 
+func initS2SearcherOptions() s2.Options {
+	options := s2.Options{}
 	// maxLevel control the maximum size of the
 	// S2Cells used to approximate regions.
 	options.SetMaxLevel(16)
 
 	// minLevel control the minimum size of the
 	// S2Cells used to approximate regions.
-	options.SetMinLevel(4)
+	options.SetMinLevel(2)
 
 	// levelMod value greater than 1 increases the effective branching
 	// factor of the S2Cell hierarchy by skipping some levels.
-	options.SetLevelMod(2)
+	options.SetLevelMod(1)
 
 	// maxCells controls the maximum number of cells
 	// when approximating each s2 region.
 	options.SetMaxCells(8)
 
-	spatialPlugin := S2SpatialAnalyzerPlugin{
-		s2Indexer: s2.NewRegionTermIndexerWithOptions(*options)}
-
-	RegisterSpatialAnalyzerPlugin(&spatialPlugin)
+	return options
 }
 
 // S2SpatialAnalyzerPlugin is an implementation of
 // the index.SpatialAnalyzerPlugin interface.
 type S2SpatialAnalyzerPlugin struct {
-	s2Indexer *s2.RegionTermIndexer
+	s2Indexer  *s2.RegionTermIndexer
+	s2Searcher *s2.RegionTermIndexer
 }
 
 func (s *S2SpatialAnalyzerPlugin) Type() string {
@@ -87,33 +126,33 @@ func (s *S2SpatialAnalyzerPlugin) Type() string {
 func (s *S2SpatialAnalyzerPlugin) GetIndexTokens(queryShape index.GeoJSON) []string {
 	var rv []string
 	shapes := []index.GeoJSON{queryShape}
-	if gc, ok := queryShape.(*geometryCollection); ok {
+	if gc, ok := queryShape.(*geojson.GeometryCollection); ok {
 		shapes = gc.Shapes
 	}
 
 	for _, shape := range shapes {
 		if s2t, ok := shape.(s2Tokenizable); ok {
-			rv = append(rv, s2t.IndexTokens(s)...)
+			rv = append(rv, s2t.IndexTokens(s.s2Indexer)...)
 		}
 	}
 
-	return stripCoveringTerms(rv)
+	return geojson.DeduplicateTerms(rv)
 }
 
 func (s *S2SpatialAnalyzerPlugin) GetQueryTokens(queryShape index.GeoJSON) []string {
 	var rv []string
 	shapes := []index.GeoJSON{queryShape}
-	if gc, ok := queryShape.(*geometryCollection); ok {
+	if gc, ok := queryShape.(*geojson.GeometryCollection); ok {
 		shapes = gc.Shapes
 	}
 
 	for _, shape := range shapes {
 		if s2t, ok := shape.(s2Tokenizable); ok {
-			rv = append(rv, s2t.QueryTokens(s)...)
+			rv = append(rv, s2t.QueryTokens(s.s2Searcher)...)
 		}
 	}
 
-	return stripCoveringTerms(rv)
+	return geojson.DeduplicateTerms(rv)
 }
 
 // ------------------------------------------------------------------------
@@ -123,221 +162,224 @@ func (s *S2SpatialAnalyzerPlugin) GetQueryTokens(queryShape index.GeoJSON) []str
 
 type s2Tokenizable interface {
 	// IndexTokens returns the tokens for indexing.
-	IndexTokens(*S2SpatialAnalyzerPlugin) []string
+	IndexTokens(*s2.RegionTermIndexer) []string
 
 	// QueryTokens returns the tokens for searching.
-	QueryTokens(*S2SpatialAnalyzerPlugin) []string
+	QueryTokens(*s2.RegionTermIndexer) []string
 }
 
-// ------------------------------------------------------------------------
+//----------------------------------------------------------------------------------
 
-func (p *point) IndexTokens(s *S2SpatialAnalyzerPlugin) []string {
-	ll := s2.LatLngFromDegrees(p.Vertices[1], p.Vertices[0])
-	point := s2.PointFromLatLng(ll)
-	return s.s2Indexer.GetIndexTermsForPoint(point, "")
+func (p *Point) Type() string {
+	return PointType
 }
 
-func (p *point) QueryTokens(s *S2SpatialAnalyzerPlugin) []string {
-	ll := s2.LatLngFromDegrees(p.Vertices[1], p.Vertices[0])
-	point := s2.PointFromLatLng(ll)
-	return s.s2Indexer.GetQueryTermsForPoint(point, "")
+func (p *Point) Intersects(s index.GeoJSON) (bool, error) {
+	// placeholder implementation
+	return false, nil
 }
 
-// ------------------------------------------------------------------------
-
-func (mp *multipoint) IndexTokens(s *S2SpatialAnalyzerPlugin) []string {
-	var rv []string
-	for _, point := range mp.Vertices {
-		terms := s.s2Indexer.GetIndexTermsForPoint(s2.PointFromLatLng(
-			s2.LatLngFromDegrees(point[1], point[0])), "")
-		rv = append(rv, terms...)
-	}
-	return deduplicateTerms(rv)
+func (p *Point) Contains(s index.GeoJSON) (bool, error) {
+	// placeholder implementation
+	return false, nil
 }
 
-func (mp *multipoint) QueryTokens(s *S2SpatialAnalyzerPlugin) []string {
-	var rv []string
-	for _, point := range mp.Vertices {
-		terms := s.s2Indexer.GetQueryTermsForPoint(s2.PointFromLatLng(
-			s2.LatLngFromDegrees(point[1], point[0])), "")
-		rv = append(rv, terms...)
-	}
+func (p *Point) IndexTokens(s *s2.RegionTermIndexer) []string {
+	return s.GetIndexTermsForPoint(s2.PointFromLatLng(
+		s2.LatLngFromDegrees(p.Lat, p.Lon)), "")
+}
 
-	return deduplicateTerms(rv)
+func (p *Point) QueryTokens(s *s2.RegionTermIndexer) []string {
+	return nil
 }
 
-// ------------------------------------------------------------------------
+//----------------------------------------------------------------------------------
 
-func (ls *linestring) IndexTokens(s *S2SpatialAnalyzerPlugin) []string {
-	pls := s2PolylinesFromCoordinates([][][]float64{ls.Vertices})
-	if len(pls) == 1 {
-		return s.s2Indexer.GetIndexTermsForRegion(pls[0].CapBound(), "")
-	}
-	return nil
+type boundedRectangle struct {
+	minLat float64
+	maxLat float64
+	minLon float64
+	maxLon float64
 }
 
-func (ls *linestring) QueryTokens(s *S2SpatialAnalyzerPlugin) []string {
-	pls := s2PolylinesFromCoordinates([][][]float64{ls.Vertices})
-	if len(pls) == 1 {
-		return s.s2Indexer.GetQueryTermsForRegion(pls[0].CapBound(), "")
-	}
-	return nil
+func NewBoundedRectangle(minLat, minLon, maxLat,
+	maxLon float64) *boundedRectangle {
+	return &boundedRectangle{minLat: minLat,
+		maxLat: maxLat, minLon: minLon, maxLon: maxLon}
 }
 
-// ------------------------------------------------------------------------
+func (br *boundedRectangle) Type() string {
+	// placeholder implementation
+	return "boundedRectangle"
+}
 
-func (mls *multilinestring) IndexTokens(s *S2SpatialAnalyzerPlugin) []string {
-	var rv []string
-	polylines := s2PolylinesFromCoordinates(mls.Vertices)
+func (p *boundedRectangle) Intersects(s index.GeoJSON) (bool, error) {
+	// placeholder implementation
+	return false, nil
+}
 
-	for _, pline := range polylines {
-		terms := s.s2Indexer.GetIndexTermsForRegion(pline.CapBound(), "")
-		rv = append(rv, terms...)
-	}
+func (p *boundedRectangle) Contains(s index.GeoJSON) (bool, error) {
+	// placeholder implementation
+	return false, nil
+}
 
-	return deduplicateTerms(rv)
+func (br *boundedRectangle) IndexTokens(s *s2.RegionTermIndexer) []string {
+	return nil
 }
 
-func (mls *multilinestring) QueryTokens(s *S2SpatialAnalyzerPlugin) []string {
-	var rv []string
-	polylines := s2PolylinesFromCoordinates(mls.Vertices)
+func (br *boundedRectangle) QueryTokens(s *s2.RegionTermIndexer) []string {
+	rect := s2.RectFromDegrees(br.minLat, br.minLon, br.maxLat, br.maxLon)
 
-	for _, pline := range polylines {
-		terms := s.s2Indexer.GetQueryTermsForRegion(pline.CapBound(), "")
-		rv = append(rv, terms...)
-	}
+	// obtain the terms to be searched for the given bounding box.
+	terms := s.GetQueryTermsForRegion(rect, "")
 
-	return deduplicateTerms(rv)
+	return geojson.StripCoveringTerms(terms)
 }
 
-// ------------------------------------------------------------------------
-
-func (mp *multipolygon) IndexTokens(s *S2SpatialAnalyzerPlugin) []string {
-	var rv []string
-	for _, loops := range mp.Coordinates() {
-		s2polygon := s2PolygonFromCoordinates(loops)
-		terms := s.s2Indexer.GetIndexTermsForRegion(s2polygon.CapBound(), "")
-		rv = append(rv, terms...)
-	}
+//----------------------------------------------------------------------------------
 
-	return rv
+type boundedPolygon struct {
+	coordinates []Point
 }
 
-func (mp *multipolygon) QueryTokens(s *S2SpatialAnalyzerPlugin) []string {
-	var rv []string
-	for _, coords := range mp.Coordinates() {
-		s2polygon := s2PolygonFromCoordinates(coords)
-		terms := s.s2Indexer.GetQueryTermsForRegion(s2polygon.CapBound(), "")
-		rv = append(rv, terms...)
-	}
+func NewBoundedPolygon(coordinates []Point) *boundedPolygon {
+	return &boundedPolygon{coordinates: coordinates}
+}
 
-	return rv
+func (bp *boundedPolygon) Type() string {
+	// placeholder implementation
+	return "boundedPolygon"
 }
 
-// ------------------------------------------------------------------------
+func (p *boundedPolygon) Intersects(s index.GeoJSON) (bool, error) {
+	// placeholder implementation
+	return false, nil
+}
 
-func (pgn *polygon) IndexTokens(s *S2SpatialAnalyzerPlugin) []string {
-	s2polygon := s2PolygonFromCoordinates(pgn.Coordinates())
-	return s.s2Indexer.GetIndexTermsForRegion(
-		s2polygon.CapBound(), "")
+func (p *boundedPolygon) Contains(s index.GeoJSON) (bool, error) {
+	// placeholder implementation
+	return false, nil
 }
 
-func (pgn *polygon) QueryTokens(s *S2SpatialAnalyzerPlugin) []string {
-	s2polygon := s2PolygonFromCoordinates(pgn.Coordinates())
-	return s.s2Indexer.GetQueryTermsForRegion(
-		s2polygon.CapBound(), "")
+func (bp *boundedPolygon) IndexTokens(s *s2.RegionTermIndexer) []string {
+	return nil
 }
 
-// ------------------------------------------------------------------------
+func (bp *boundedPolygon) QueryTokens(s *s2.RegionTermIndexer) []string {
+	vertices := make([]s2.Point, len(bp.coordinates))
+	for i, point := range bp.coordinates {
+		vertices[i] = s2.PointFromLatLng(
+			s2.LatLngFromDegrees(point.Lat, point.Lon))
+	}
+	s2polygon := s2.PolygonFromOrientedLoops([]*s2.Loop{s2.LoopFromPoints(vertices)})
 
-func (c *circle) IndexTokens(s *S2SpatialAnalyzerPlugin) []string {
-	cp := s2.PointFromLatLng(s2.LatLngFromDegrees(c.Vertices[1], c.Vertices[0]))
-	angle := radiusInMetersToS1Angle(float64(c.RadiusInMeters))
-	cap := s2.CapFromCenterAngle(cp, angle)
+	// obtain the terms to be searched for the given polygon.
+	terms := s.GetQueryTermsForRegion(
+		s2polygon.CapBound(), "")
 
-	return s.s2Indexer.GetIndexTermsForRegion(cap.CapBound(), "")
+	return geojson.StripCoveringTerms(terms)
 }
 
-func (c *circle) QueryTokens(s *S2SpatialAnalyzerPlugin) []string {
-	cp := s2.PointFromLatLng(s2.LatLngFromDegrees(c.Vertices[1], c.Vertices[0]))
-	angle := radiusInMetersToS1Angle(float64(c.RadiusInMeters))
-	cap := s2.CapFromCenterAngle(cp, angle)
+//----------------------------------------------------------------------------------
 
-	return s.s2Indexer.GetQueryTermsForRegion(cap.CapBound(), "")
+type pointDistance struct {
+	dist      float64
+	centerLat float64
+	centerLon float64
 }
 
-// ------------------------------------------------------------------------
-
-func (e *envelope) IndexTokens(s *S2SpatialAnalyzerPlugin) []string {
-	s2rect := s2RectFromBounds(e.Vertices[0], e.Vertices[1])
-	return s.s2Indexer.GetIndexTermsForRegion(s2rect.CapBound(), "")
+func (p *pointDistance) Type() string {
+	// placeholder implementation
+	return "pointDistance"
 }
 
-func (e *envelope) QueryTokens(s *S2SpatialAnalyzerPlugin) []string {
-	s2rect := s2RectFromBounds(e.Vertices[0], e.Vertices[1])
-	return s.s2Indexer.GetQueryTermsForRegion(s2rect.CapBound(), "")
+func NewPointDistance(centerLat, centerLon,
+	dist float64) *pointDistance {
+	return &pointDistance{centerLat: centerLat,
+		centerLon: centerLon, dist: dist}
 }
 
-// ------------------------------------------------------------------------
-
-func (p *Point) IndexTokens(s *S2SpatialAnalyzerPlugin) []string {
-	return s.s2Indexer.GetIndexTermsForPoint(s2.PointFromLatLng(
-		s2.LatLngFromDegrees(p.Lat, p.Lon)), "")
+func (p *pointDistance) Intersects(s index.GeoJSON) (bool, error) {
+	// placeholder implementation
+	return false, nil
 }
 
-func (p *Point) QueryTokens(s *S2SpatialAnalyzerPlugin) []string {
-	return nil
+func (p *pointDistance) Contains(s index.GeoJSON) (bool, error) {
+	// placeholder implementation
+	return false, nil
 }
 
-// ------------------------------------------------------------------------
-
-func (pd *pointDistance) IndexTokens(s *S2SpatialAnalyzerPlugin) []string {
+func (pd *pointDistance) IndexTokens(s *s2.RegionTermIndexer) []string {
 	return nil
 }
 
-func (pd *pointDistance) QueryTokens(s *S2SpatialAnalyzerPlugin) []string {
+func (pd *pointDistance) QueryTokens(s *s2.RegionTermIndexer) []string {
 	// obtain the covering query region from the given points.
 	queryRegion := s2.CapFromCenterAndRadius(pd.centerLat,
 		pd.centerLon, pd.dist)
 
 	// obtain the query terms for the query region.
-	terms := s.s2Indexer.GetQueryTermsForRegion(queryRegion, "")
+	terms := s.GetQueryTermsForRegion(queryRegion, "")
 
-	return s2.FilterOutCoveringTerms(terms)
+	return geojson.StripCoveringTerms(terms)
 }
 
 // ------------------------------------------------------------------------
 
-func (bp *boundedPolygon) IndexTokens(s *S2SpatialAnalyzerPlugin) []string {
-	return nil
+// NewGeometryCollection instantiate a geometrycollection
+// and prefix the byte contents with certain glue bytes that
+// can be used later while filering the doc values.
+func NewGeometryCollection(coordinates [][][][][]float64,
+	typs []string) (index.GeoJSON, []byte, error) {
+
+	return geojson.NewGeometryCollection(coordinates, typs)
 }
 
-func (bp *boundedPolygon) QueryTokens(s *S2SpatialAnalyzerPlugin) []string {
-	vertices := make([]s2.Point, len(bp.coordinates))
-	for i, point := range bp.coordinates {
-		vertices[i] = s2.PointFromLatLng(
-			s2.LatLngFromDegrees(point.Lat, point.Lon))
-	}
-	s2polygon := s2.PolygonFromOrientedLoops([]*s2.Loop{s2.LoopFromPoints(vertices)})
+// NewGeoCircleShape instantiate a circle shape and
+// prefix the byte contents with certain glue bytes that
+// can be used later while filering the doc values.
+func NewGeoCircleShape(cp []float64,
+	radiusInMeter float64) (index.GeoJSON, []byte, error) {
+	return geojson.NewGeoCircleShape(cp, radiusInMeter)
+}
 
-	// obtain the terms to be searched for the given polygon.
-	terms := s.s2Indexer.GetQueryTermsForRegion(
-		s2polygon.CapBound(), "")
+func NewGeoJsonShape(coordinates [][][][]float64, typ string) (
+	index.GeoJSON, []byte, error) {
+	return geojson.NewGeoJsonShape(coordinates, typ)
+}
 
-	return s2.FilterOutCoveringTerms(terms)
+func NewGeoJsonPoint(points []float64) index.GeoJSON {
+	return geojson.NewGeoJsonPoint(points)
 }
 
-// ------------------------------------------------------------------------
+func NewGeoJsonMultiPoint(points [][]float64) index.GeoJSON {
+	return geojson.NewGeoJsonMultiPoint(points)
+}
 
-func (br *boundedRectangle) IndexTokens(s *S2SpatialAnalyzerPlugin) []string {
-	return nil
+func NewGeoJsonLinestring(points [][]float64) index.GeoJSON {
+	return geojson.NewGeoJsonLinestring(points)
 }
 
-func (br *boundedRectangle) QueryTokens(s *S2SpatialAnalyzerPlugin) []string {
-	rect := s2.RectFromDegrees(br.minLat, br.minLon, br.maxLat, br.maxLon)
+func NewGeoJsonMultilinestring(points [][][]float64) index.GeoJSON {
+	return geojson.NewGeoJsonMultilinestring(points)
+}
 
-	// obtain the terms to be searched for the given bounding box.
-	terms := s.s2Indexer.GetQueryTermsForRegion(rect, "")
+func NewGeoJsonPolygon(points [][][]float64) index.GeoJSON {
+	return geojson.NewGeoJsonPolygon(points)
+}
+
+func NewGeoJsonMultiPolygon(points [][][][]float64) index.GeoJSON {
+	return geojson.NewGeoJsonMultiPolygon(points)
+}
+
+func NewGeoCircle(points []float64, radius float64) index.GeoJSON {
+	return geojson.NewGeoCircle(points, radius)
+}
+
+func NewGeoEnvelope(points [][]float64) index.GeoJSON {
+	return geojson.NewGeoEnvelope(points)
+}
 
-	return s2.FilterOutCoveringTerms(terms)
+func ParseGeoJSONShape(input json.RawMessage) (index.GeoJSON, error) {
+	return geojson.ParseGeoJSONShape(input)
 }
diff --git a/geo/geojson_shapes_impl.go b/geo/geojson_shapes_impl.go
deleted file mode 100644
index b4c7f581..00000000
--- a/geo/geojson_shapes_impl.go
+++ /dev/null
@@ -1,1746 +0,0 @@
-//  Copyright (c) 2022 Couchbase, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 		http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package geo
-
-import (
-	"encoding/json"
-	"fmt"
-	"strings"
-
-	index "github.com/blevesearch/bleve_index_api"
-
-	"github.com/blevesearch/geo/s2"
-)
-
-const (
-	PointType              = "point"
-	MultiPointType         = "multipoint"
-	LineStringType         = "linestring"
-	MultiLineStringType    = "multilinestring"
-	PolygonType            = "polygon"
-	MultiPolygonType       = "multipolygon"
-	GeometryCollectionType = "geometrycollection"
-	CircleType             = "circle"
-	EnvelopeType           = "envelope"
-)
-
-// compositeShape is an optional interface for the
-// composite geoJSON shapes which is composed of
-// multiple spatial shapes within it. Composite shapes
-// like multipoint, multilinestring, multipolygon and
-// geometrycollection shapes are supposed to implement
-// this interface.
-type compositeShape interface {
-	// Members implementation returns the
-	// geoJSON shapes composed within the shape.
-	Members() []index.GeoJSON
-}
-
-//--------------------------------------------------------
-// point represents the geoJSON point type and it
-// implements the index.GeoJSON interface.
-type point struct {
-	Typ      string    `json:"type"`
-	Vertices []float64 `json:"coordinates"`
-	s2cell   *s2.Cell
-}
-
-func (p *point) Type() string {
-	return strings.ToLower(p.Typ)
-}
-
-func NewGeoJsonPoint(v []float64) index.GeoJSON {
-	return &point{Typ: PointType, Vertices: v}
-}
-
-func (p *point) Intersects(other index.GeoJSON) (bool, error) {
-	if p.s2cell == nil {
-		s2point := s2.PointFromLatLng(s2.LatLngFromDegrees(
-			p.Vertices[1], p.Vertices[0]))
-		cell := s2.CellFromPoint(s2point)
-		p.s2cell = &cell
-	}
-
-	return checkCellIntersectsShape(p.s2cell, p, other)
-}
-
-func (p *point) Contains(other index.GeoJSON) (bool, error) {
-	if p.s2cell == nil {
-		s2point := s2.PointFromLatLng(s2.LatLngFromDegrees(
-			p.Vertices[1], p.Vertices[0]))
-		cell := s2.CellFromPoint(s2point)
-		p.s2cell = &cell
-	}
-
-	return checkCellContainsShape([]*s2.Cell{p.s2cell}, other)
-}
-
-func (p *point) Coordinates() []float64 {
-	return p.Vertices
-}
-
-//--------------------------------------------------------
-// multipoint represents the geoJSON multipoint type and it
-// implements the index.GeoJSON interface as well as the
-// compositeShap interface.
-type multipoint struct {
-	Typ      string      `json:"type"`
-	Vertices [][]float64 `json:"coordinates"`
-	s2cells  []*s2.Cell
-}
-
-func NewGeoJsonMultiPoint(v [][]float64) index.GeoJSON {
-	return &multipoint{Typ: MultiPointType, Vertices: v}
-}
-
-func (p *multipoint) Type() string {
-	return strings.ToLower(p.Typ)
-}
-
-func (p *multipoint) Intersects(other index.GeoJSON) (bool, error) {
-	if p.s2cells == nil {
-		p.s2cells = make([]*s2.Cell, len(p.Vertices))
-
-		for i, point := range p.Vertices {
-			s2point := s2.PointFromLatLng(s2.LatLngFromDegrees(
-				point[1], point[0]))
-			cell := s2.CellFromPoint(s2point)
-			p.s2cells[i] = &cell
-		}
-	}
-
-	for _, cell := range p.s2cells {
-		rv, err := checkCellIntersectsShape(cell, p, other)
-		if rv && err == nil {
-			return rv, nil
-		}
-	}
-
-	return false, nil
-}
-
-func (p *multipoint) Contains(other index.GeoJSON) (bool, error) {
-	if p.s2cells == nil {
-		p.s2cells = make([]*s2.Cell, len(p.Vertices))
-
-		for i, point := range p.Vertices {
-			s2point := s2.PointFromLatLng(s2.LatLngFromDegrees(
-				point[1], point[0]))
-			cell := s2.CellFromPoint(s2point)
-			p.s2cells[i] = &cell
-		}
-	}
-
-	return checkCellContainsShape(p.s2cells, other)
-}
-
-func (p *multipoint) Coordinates() [][]float64 {
-	return p.Vertices
-}
-
-func (p *multipoint) Members() []index.GeoJSON {
-	points := make([]index.GeoJSON, len(p.Vertices))
-	for pos, vertices := range p.Vertices {
-		points[pos] = NewGeoJsonPoint(vertices)
-	}
-	return points
-}
-
-//--------------------------------------------------------
-// linestring represents the geoJSON linestring type and it
-// implements the index.GeoJSON interface.
-type linestring struct {
-	Typ      string      `json:"type"`
-	Vertices [][]float64 `json:"coordinates"`
-	pl       *s2.Polyline
-}
-
-func NewGeoJsonLinestring(points [][]float64) index.GeoJSON {
-	return &linestring{Typ: LineStringType, Vertices: points}
-}
-
-func (p *linestring) Type() string {
-	return strings.ToLower(p.Typ)
-}
-
-func (p *linestring) Intersects(other index.GeoJSON) (bool, error) {
-	if p.pl == nil {
-		latlngs := make([]s2.LatLng, 2)
-		latlngs[0] = s2.LatLngFromDegrees(p.Vertices[0][1], p.Vertices[0][0])
-		latlngs[1] = s2.LatLngFromDegrees(p.Vertices[1][1], p.Vertices[1][0])
-
-		p.pl = s2.PolylineFromLatLngs(latlngs)
-	}
-
-	return checkLineStringsIntersectsShape([]*s2.Polyline{p.pl}, p, other)
-}
-
-func (p *linestring) Contains(other index.GeoJSON) (bool, error) {
-	return checkLineStringsContainsShape([][][]float64{p.Vertices}, other)
-}
-
-func (p *linestring) Coordinates() [][]float64 {
-	return p.Vertices
-}
-
-//--------------------------------------------------------
-// multilinestring represents the geoJSON multilinestring type
-// and it implements the index.GeoJSON interface as well as the
-// compositeShap interface.
-type multilinestring struct {
-	Typ      string        `json:"type"`
-	Vertices [][][]float64 `json:"coordinates"`
-	pls      []*s2.Polyline
-}
-
-func NewGeoJsonMultilinestring(points [][][]float64) index.GeoJSON {
-	return &multilinestring{Typ: MultiLineStringType, Vertices: points}
-}
-
-func (p *multilinestring) Type() string {
-	return strings.ToLower(p.Typ)
-}
-
-func (p *multilinestring) Intersects(other index.GeoJSON) (bool, error) {
-	if p.pls == nil {
-		p.pls = s2PolylinesFromCoordinates(p.Vertices)
-	}
-
-	return checkLineStringsIntersectsShape(p.pls, p, other)
-}
-
-func (p *multilinestring) Contains(other index.GeoJSON) (bool, error) {
-	return checkLineStringsContainsShape(p.Vertices, other)
-}
-
-func (p *multilinestring) Coordinates() [][][]float64 {
-	return p.Vertices
-}
-
-func (p *multilinestring) Members() []index.GeoJSON {
-	lines := make([]index.GeoJSON, len(p.Vertices))
-	for pos, vertices := range p.Vertices {
-		lines[pos] = NewGeoJsonLinestring(vertices)
-	}
-	return lines
-}
-
-//--------------------------------------------------------
-// polygon represents the geoJSON polygon type
-// and it implements the index.GeoJSON interface.
-type polygon struct {
-	Typ      string        `json:"type"`
-	Vertices [][][]float64 `json:"coordinates"`
-	s2pgn    *s2.Polygon
-}
-
-func NewGeoJsonPolygon(points [][][]float64) index.GeoJSON {
-	return &polygon{Typ: PolygonType, Vertices: points}
-}
-
-func (p *polygon) Type() string {
-	return strings.ToLower(p.Typ)
-}
-
-func (p *polygon) Intersects(other index.GeoJSON) (bool, error) {
-	// make an s2polygon for reuse.
-	if p.s2pgn == nil {
-		p.s2pgn = s2PolygonFromCoordinates(p.Vertices)
-	}
-
-	return checkPolygonIntersectsShape(p.s2pgn, p.Vertices, p, other)
-}
-
-func (p *polygon) Contains(other index.GeoJSON) (bool, error) {
-	// make an s2polygon for reuse.
-	if p.s2pgn == nil {
-		p.s2pgn = s2PolygonFromCoordinates(p.Vertices)
-	}
-
-	return checkMultiPolygonContainsShape([]*s2.Polygon{p.s2pgn}, p, other)
-}
-
-func (p *polygon) Coordinates() [][][]float64 {
-	return p.Vertices
-}
-
-//--------------------------------------------------------
-// multipolygon represents the geoJSON multipolygon type
-// and it implements the index.GeoJSON interface as well as the
-// compositeShap interface.
-type multipolygon struct {
-	Typ      string          `json:"type"`
-	Vertices [][][][]float64 `json:"coordinates"`
-	s2pgns   []*s2.Polygon
-}
-
-func NewGeoJsonMultiPolygon(points [][][][]float64) index.GeoJSON {
-	return &multipolygon{Typ: MultiPolygonType, Vertices: points}
-}
-
-func (p *multipolygon) Type() string {
-	return strings.ToLower(p.Typ)
-}
-
-func (p *multipolygon) Intersects(other index.GeoJSON) (bool, error) {
-	if p.s2pgns == nil {
-		p.s2pgns = make([]*s2.Polygon, len(p.Vertices))
-		for i, vertices := range p.Vertices {
-			pgn := s2PolygonFromCoordinates(vertices)
-			p.s2pgns[i] = pgn
-		}
-	}
-
-	for i, pgn := range p.s2pgns {
-		rv, err := checkPolygonIntersectsShape(pgn, p.Vertices[i], p, other)
-		if rv && err == nil {
-			return true, nil
-		}
-	}
-
-	return false, nil
-}
-
-func (p *multipolygon) Contains(other index.GeoJSON) (bool, error) {
-	if p.s2pgns == nil {
-		p.s2pgns = make([]*s2.Polygon, len(p.Vertices))
-		for i, vertices := range p.Vertices {
-			pgn := s2PolygonFromCoordinates(vertices)
-			p.s2pgns[i] = pgn
-		}
-	}
-
-	return checkMultiPolygonContainsShape(p.s2pgns, p, other)
-}
-
-func (p *multipolygon) Coordinates() [][][][]float64 {
-	return p.Vertices
-}
-
-func (p *multipolygon) Members() []index.GeoJSON {
-	polygons := make([]index.GeoJSON, len(p.Vertices))
-	for pos, vertices := range p.Vertices {
-		polygons[pos] = NewGeoJsonPolygon(vertices)
-	}
-	return polygons
-}
-
-//--------------------------------------------------------
-// geometryCollection represents the geoJSON geometryCollection type
-// and it implements the index.GeoJSON interface as well as the
-// compositeShap interface.
-type geometryCollection struct {
-	Typ    string          `json:"type"`
-	Shapes []index.GeoJSON `json:"geometries"`
-}
-
-func (gc *geometryCollection) Type() string {
-	return strings.ToLower(gc.Typ)
-}
-
-func (gc *geometryCollection) Members() []index.GeoJSON {
-	shapes := make([]index.GeoJSON, 0, len(gc.Shapes))
-	for _, shape := range gc.Shapes {
-		if cs, ok := shape.(compositeShape); ok {
-			shapes = append(shapes, cs.Members()...)
-		} else {
-			shapes = append(shapes, shape)
-		}
-	}
-	return shapes
-}
-
-func (gc *geometryCollection) Intersects(other index.GeoJSON) (bool, error) {
-	for _, shape := range gc.Members() {
-
-		intersects, err := shape.Intersects(other)
-		if intersects && err == nil {
-			return true, nil
-		}
-	}
-	return false, nil
-}
-
-func (gc *geometryCollection) Contains(other index.GeoJSON) (bool, error) {
-	// handle composite target shapes explicitly
-	if cs, ok := other.(compositeShape); ok {
-		otherShapes := cs.Members()
-		shapesFoundWithIn := make(map[int]struct{})
-
-	nextShape:
-		for pos, shapeInDoc := range otherShapes {
-			for _, shape := range gc.Members() {
-				within, err := shape.Contains(shapeInDoc)
-				if within && err == nil {
-					shapesFoundWithIn[pos] = struct{}{}
-					continue nextShape
-				}
-			}
-		}
-
-		return len(shapesFoundWithIn) == len(otherShapes), nil
-	}
-
-	for _, shape := range gc.Members() {
-		within, err := shape.Contains(other)
-		if within && err == nil {
-			return true, nil
-		}
-	}
-
-	return false, nil
-}
-
-func (gc *geometryCollection) UnmarshalJSON(data []byte) error {
-	tmp := struct {
-		Typ    string            `json:"type"`
-		Shapes []json.RawMessage `json:"geometries"`
-	}{}
-
-	err := json.Unmarshal(data, &tmp)
-	if err != nil {
-		return err
-	}
-	gc.Typ = tmp.Typ
-
-	for _, shape := range tmp.Shapes {
-		var t map[string]interface{}
-		err := json.Unmarshal(shape, &t)
-		if err != nil {
-			return err
-		}
-
-		var typ string
-
-		if val, ok := t["type"]; ok {
-			typ = strings.ToLower(val.(string))
-		} else {
-			continue
-		}
-
-		switch typ {
-		case PointType:
-			var p point
-			err := json.Unmarshal(shape, &p)
-			if err != nil {
-				return err
-			}
-			gc.Shapes = append(gc.Shapes, &p)
-
-		case MultiPointType:
-			var mp multipoint
-			err := json.Unmarshal(shape, &mp)
-			if err != nil {
-				return err
-			}
-			gc.Shapes = append(gc.Shapes, &mp)
-
-		case LineStringType:
-			var ls linestring
-			err := json.Unmarshal(shape, &ls)
-			if err != nil {
-				return err
-			}
-			gc.Shapes = append(gc.Shapes, &ls)
-
-		case MultiLineStringType:
-			var mls multilinestring
-			err := json.Unmarshal(shape, &mls)
-			if err != nil {
-				return err
-			}
-			gc.Shapes = append(gc.Shapes, &mls)
-
-		case PolygonType:
-			var pgn polygon
-			err := json.Unmarshal(shape, &pgn)
-			if err != nil {
-				return err
-			}
-			gc.Shapes = append(gc.Shapes, &pgn)
-
-		case MultiPolygonType:
-			var pgn multipolygon
-			err := json.Unmarshal(shape, &pgn)
-			if err != nil {
-				return err
-			}
-			gc.Shapes = append(gc.Shapes, &pgn)
-		}
-	}
-
-	return nil
-}
-
-//--------------------------------------------------------
-// circle represents a custom circle type and it
-// implements the index.GeoJSON interface.
-type circle struct {
-	Typ            string    `json:"type"`
-	Vertices       []float64 `json:"coordinates"`
-	RadiusInMeters float64   `json:"radiusInMeters"`
-	s2cap          *s2.Cap
-}
-
-func NewGeoCircle(points []float64,
-	radiusInMeter float64) index.GeoJSON {
-	return &circle{Typ: CircleType,
-		Vertices:       points,
-		RadiusInMeters: radiusInMeter}
-}
-
-func (c *circle) Type() string {
-	return strings.ToLower(c.Typ)
-}
-
-func (c *circle) Intersects(other index.GeoJSON) (bool, error) {
-	if c.s2cap == nil {
-		c.s2cap = s2Cap(c.Vertices, c.RadiusInMeters)
-	}
-
-	return checkCircleIntersectsShape(c.s2cap, c, other)
-}
-
-func (c *circle) Contains(other index.GeoJSON) (bool, error) {
-	if c.s2cap == nil {
-		c.s2cap = s2Cap(c.Vertices, c.RadiusInMeters)
-	}
-
-	return checkCircleContainsShape(c.s2cap, c, other)
-}
-
-func (c *circle) UnmarshalJSON(data []byte) error {
-	tmp := struct {
-		Typ            string    `json:"type"`
-		Vertices       []float64 `json:"coordinates"`
-		Radius         string    `json:"radius"`
-		RadiusInMeters float64   `json:"radiusInMeters"`
-	}{}
-
-	err := json.Unmarshal(data, &tmp)
-	if err != nil {
-		return err
-	}
-	c.Typ = tmp.Typ
-	c.Vertices = tmp.Vertices
-	c.RadiusInMeters = tmp.RadiusInMeters
-	if tmp.Radius != "" {
-		c.RadiusInMeters, err = ParseDistance(tmp.Radius)
-	}
-
-	return err
-}
-
-//--------------------------------------------------------
-// envelope represents the  envelope/bounding box type and it
-// implements the index.GeoJSON interface.
-type envelope struct {
-	Typ      string      `json:"type"`
-	Vertices [][]float64 `json:"coordinates"`
-	r        *s2.Rect
-}
-
-func NewGeoEnvelope(points [][]float64) index.GeoJSON {
-	return &envelope{Vertices: points, Typ: EnvelopeType}
-}
-
-func (e *envelope) Type() string {
-	return strings.ToLower(e.Typ)
-}
-
-func (e *envelope) Intersects(other index.GeoJSON) (bool, error) {
-	if e.r == nil {
-		e.r = s2RectFromBounds(e.Vertices[0], e.Vertices[1])
-	}
-
-	return checkEnvelopeIntersectsShape(e.r, e, other)
-}
-
-func (e *envelope) Contains(other index.GeoJSON) (bool, error) {
-	if e.r == nil {
-		e.r = s2RectFromBounds(e.Vertices[0], e.Vertices[1])
-	}
-
-	return checkEnvelopeContainsShape(e.r, e, other)
-}
-
-//--------------------------------------------------------
-
-type boundedRectangle struct {
-	minLat float64
-	maxLat float64
-	minLon float64
-	maxLon float64
-}
-
-func NewBoundedRectangle(minLat, minLon, maxLat,
-	maxLon float64) *boundedRectangle {
-	return &boundedRectangle{minLat: minLat,
-		maxLat: maxLat, minLon: minLon, maxLon: maxLon}
-}
-
-func (br *boundedRectangle) Type() string {
-	// placeholder implementation
-	return "boundedRectangle"
-}
-
-func (p *boundedRectangle) Intersects(s index.GeoJSON) (bool, error) {
-	// placeholder implementation
-	return false, nil
-}
-
-func (p *boundedRectangle) Contains(s index.GeoJSON) (bool, error) {
-	// placeholder implementation
-	return false, nil
-}
-
-//--------------------------------------------------------
-
-type boundedPolygon struct {
-	coordinates []Point
-}
-
-func NewBoundedPolygon(coordinates []Point) *boundedPolygon {
-	return &boundedPolygon{coordinates: coordinates}
-}
-
-func (bp *boundedPolygon) Type() string {
-	// placeholder implementation
-	return "boundedPolygon"
-}
-
-func (p *boundedPolygon) Intersects(s index.GeoJSON) (bool, error) {
-	// placeholder implementation
-	return false, nil
-}
-
-func (p *boundedPolygon) Contains(s index.GeoJSON) (bool, error) {
-	// placeholder implementation
-	return false, nil
-}
-
-//--------------------------------------------------------
-
-type pointDistance struct {
-	dist      float64
-	centerLat float64
-	centerLon float64
-}
-
-func (p *pointDistance) Type() string {
-	// placeholder implementation
-	return "pointDistance"
-}
-
-func NewPointDistance(centerLat, centerLon,
-	dist float64) *pointDistance {
-	return &pointDistance{centerLat: centerLat,
-		centerLon: centerLon, dist: dist}
-}
-
-func (p *pointDistance) Intersects(s index.GeoJSON) (bool, error) {
-	// placeholder implementation
-	return false, nil
-}
-
-func (p *pointDistance) Contains(s index.GeoJSON) (bool, error) {
-	// placeholder implementation
-	return false, nil
-}
-
-//--------------------------------------------------------
-
-func (p *Point) Type() string {
-	return PointType
-}
-
-func (p *Point) Intersects(s index.GeoJSON) (bool, error) {
-	// placeholder implementation
-	return false, nil
-}
-
-func (p *Point) Contains(s index.GeoJSON) (bool, error) {
-	// placeholder implementation
-	return false, nil
-}
-
-//--------------------------------------------------------
-
-// checkCellIntersectsShape checks for intersection between
-// the s2cell and the shape in the document.
-func checkCellIntersectsShape(cell *s2.Cell, shapeIn,
-	other index.GeoJSON) (bool, error) {
-	// check if the other shape is a point.
-	if p2, ok := other.(*point); ok {
-		s2cell := s2.CellFromLatLng(s2.LatLngFromDegrees(
-			p2.Vertices[1], p2.Vertices[0]))
-
-		if cell.IntersectsCell(s2cell) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipoint.
-	if p2, ok := other.(*multipoint); ok {
-		// check the intersection for any point in the array.
-		for _, point := range p2.Vertices {
-			s2cell := s2.CellFromLatLng(s2.LatLngFromDegrees(
-				point[1], point[0]))
-
-			if cell.IntersectsCell(s2cell) {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a polygon.
-	if p2, ok := other.(*polygon); ok {
-		s2pgn2 := s2PolygonFromCoordinates(p2.Coordinates())
-
-		if s2pgn2.IntersectsCell(*cell) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipolygon.
-	if p2, ok := other.(*multipolygon); ok {
-		// check the intersection for any polygon in the collection.
-		for _, coordinates := range p2.Vertices {
-			s2pgn2 := s2PolygonFromCoordinates(coordinates)
-
-			if s2pgn2.IntersectsCell(*cell) {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a linestring.
-	if p2, ok := other.(*linestring); ok {
-		for _, point := range p2.Vertices {
-			s2cell := s2.CellFromLatLng(s2.LatLngFromDegrees(
-				point[1], point[0]))
-			if cell.IntersectsCell(s2cell) {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multilinestring.
-	if p2, ok := other.(*multilinestring); ok {
-		// check the intersection for any linestring in the array.
-		for _, linestrings := range p2.Vertices {
-			for _, point := range linestrings {
-				s2cell := s2.CellFromLatLng(s2.LatLngFromDegrees(
-					point[1], point[0]))
-				if cell.IntersectsCell(s2cell) {
-					return true, nil
-				}
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a geometrycollection.
-	if gc, ok := other.(*geometryCollection); ok {
-		// check for intersection across every member shape.
-		if geometryCollectionIntersectsShape(gc, shapeIn) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a circle.
-	if c, ok := other.(*circle); ok {
-		s2cap := s2Cap(c.Vertices, c.RadiusInMeters)
-		if s2cap.IntersectsCell(*cell) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is an envelope.
-	if e, ok := other.(*envelope); ok {
-		s2rectInDoc := s2RectFromBounds(e.Vertices[0],
-			e.Vertices[1])
-
-		if s2rectInDoc.IntersectsCell(*cell) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	return false, fmt.Errorf("unknown geojson type: %s "+
-		" found in document", other.Type())
-}
-
-// checkCellContainsShape checks whether the given shape in
-// in the document is contained with the s2cell.
-func checkCellContainsShape(cells []*s2.Cell,
-	other index.GeoJSON) (bool, error) {
-	// check if the other shape is a point.
-	if p2, ok := other.(*point); ok {
-		for _, cell := range cells {
-			s2point := s2.PointFromLatLng(s2.LatLngFromDegrees(
-				p2.Vertices[1], p2.Vertices[0]))
-
-			if cell.ContainsPoint(s2point) {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipoint, if so containment is
-	// checked for every point in the multipoint with every given cells.
-	if p2, ok := other.(*multipoint); ok {
-		// check the containment for every point in the collection.
-		lookup := make(map[int]struct{})
-		for _, cell := range cells {
-			for pos, point := range p2.Vertices {
-				if _, done := lookup[pos]; done {
-					continue
-				}
-				// already processed all the points in the multipoint.
-				if len(lookup) == len(p2.Vertices) {
-					return true, nil
-				}
-
-				s2point := s2.PointFromLatLng(s2.LatLngFromDegrees(point[1],
-					point[0]))
-
-				if cell.ContainsPoint(s2point) {
-					lookup[pos] = struct{}{}
-				}
-			}
-		}
-
-		return len(lookup) == len(p2.Vertices), nil
-	}
-
-	// as point is a non closed shape, containment isn't feasible
-	// for other higher dimensions.
-	return false, nil
-}
-
-// ------------------------------------------------------------------------
-
-// checkLineStringsIntersectsShape checks whether the given linestrings
-// intersects with the shape in the document.
-func checkLineStringsIntersectsShape(pls []*s2.Polyline, shapeIn,
-	other index.GeoJSON) (bool, error) {
-	// check if the other shape is a point.
-	if p2, ok := other.(*point); ok {
-
-		if polylineIntersectsPoint(pls, p2.Vertices) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipoint.
-	if p2, ok := other.(*multipoint); ok {
-		// check the intersection for any point in the collection.
-		for _, point := range p2.Vertices {
-
-			if polylineIntersectsPoint(pls, point) {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a polygon.
-	if p2, ok := other.(*polygon); ok {
-
-		if polylineIntersectsPolygons(pls,
-			[][][][]float64{p2.Coordinates()}) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipolygon.
-	if p2, ok := other.(*multipolygon); ok {
-		// check the intersection for any polygon in the collection.
-		if polylineIntersectsPolygons(pls, p2.Coordinates()) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a linestring.
-	if ls, ok := other.(*linestring); ok {
-
-		if polylineIntersectsPolylines(pls, [][][]float64{ls.Vertices}) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multilinestring.
-	if mls, ok := other.(*multilinestring); ok {
-
-		if polylineIntersectsPolylines(pls, mls.Vertices) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	if gc, ok := other.(*geometryCollection); ok {
-		// check whether the linestring intersects with any of the
-		// shapes Contains a geometrycollection.
-		if geometryCollectionIntersectsShape(gc, shapeIn) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a circle.
-	if c, ok := other.(*circle); ok {
-		centre := s2.PointFromLatLng(
-			s2.LatLngFromDegrees(c.Vertices[1], c.Vertices[0]))
-
-		for _, pl := range pls {
-			edge := pl.Edge(0)
-			distance := s2.DistanceFromSegment(centre, edge.V0, edge.V1)
-			r := radiusInMetersToS1Angle(float64(c.RadiusInMeters))
-			return distance <= r, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a envelope.
-	if e, ok := other.(*envelope); ok {
-		s2rectInDoc := s2RectFromBounds(e.Vertices[0], e.Vertices[1])
-
-		for _, pl := range pls {
-			edge := pl.Edge(0)
-			latlng1 := s2.LatLngFromPoint(edge.V0)
-			latlng2 := s2.LatLngFromPoint(edge.V1)
-			a := []float64{latlng1.Lng.Degrees(), latlng1.Lat.Degrees()}
-			b := []float64{latlng2.Lng.Degrees(), latlng2.Lat.Degrees()}
-			for j := 0; j < 4; j++ {
-				v1 := s2rectInDoc.Vertex(j)
-				v2 := s2rectInDoc.Vertex((j + 1) % 4)
-				c := []float64{v1.Lng.Degrees(), v1.Lat.Degrees()}
-				d := []float64{v2.Lng.Degrees(), v2.Lat.Degrees()}
-				if doIntersect(a, b, c, d) {
-					return true, nil
-				}
-			}
-		}
-
-		return false, nil
-	}
-
-	return false, fmt.Errorf("unknown geojson type: %s "+
-		"found in document", other.Type())
-}
-
-// checkLineStringsContainsShape checks the containment for
-// points and multipoints for the linestring vertices.
-func checkLineStringsContainsShape(vertices [][][]float64,
-	other index.GeoJSON) (bool, error) {
-	// check if the other shape is a point.
-	if p, ok := other.(*point); ok {
-
-		if polylinesContainPoints(vertices, [][]float64{p.Vertices}) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipoint.
-	if mp, ok := other.(*multipoint); ok {
-
-		if polylinesContainPoints(vertices, mp.Vertices) {
-			return true, nil
-		}
-	}
-
-	return false, nil
-}
-
-// ------------------------------------------------------------------------
-
-// checkPolygonIntersectsShape checks the intersection between the
-// s2 polygon and the other shapes in the documents.
-func checkPolygonIntersectsShape(s2pgn *s2.Polygon, vertices [][][]float64,
-	shapeIn, other index.GeoJSON) (bool, error) {
-	// check if the other shape is a point.
-	if p2, ok := other.(*point); ok {
-		s2cell := s2.CellFromLatLng(s2.LatLngFromDegrees(p2.Vertices[1],
-			p2.Vertices[0]))
-
-		if s2pgn.IntersectsCell(s2cell) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipoint.
-	if p2, ok := other.(*multipoint); ok {
-		// check the intersection for any point in the collection.
-		for _, point := range p2.Vertices {
-			s2cell := s2.CellFromLatLng(s2.LatLngFromDegrees(point[1],
-				point[0]))
-
-			if s2pgn.IntersectsCell(s2cell) {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a polygon.
-	if p2, ok := other.(*polygon); ok {
-		s2p2 := s2PolygonFromCoordinates(p2.Coordinates())
-
-		if s2pgn.Intersects(s2p2) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipolygon.
-	if p2, ok := other.(*multipolygon); ok {
-		// check the intersection for any polygon in the collection.
-		for _, coordinates := range p2.Vertices {
-			s2p2 := s2PolygonFromCoordinates(coordinates)
-
-			if s2pgn.Intersects(s2p2) {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a linestring.
-	if ls, ok := other.(*linestring); ok {
-
-		if polygonsIntersectsLinestrings(s2pgn,
-			[][][]float64{ls.Vertices}) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multilinestring.
-	if mls, ok := other.(*multilinestring); ok {
-
-		if polygonsIntersectsLinestrings(s2pgn, mls.Vertices) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	if gc, ok := other.(*geometryCollection); ok {
-		// check whether the polygon intersects with any of the
-		// member shapes of the geometry collection.
-		if geometryCollectionIntersectsShape(gc, shapeIn) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a circle.
-	if c, ok := other.(*circle); ok {
-
-		cp := s2.PointFromLatLng(s2.LatLngFromDegrees(
-			c.Vertices[1], c.Vertices[0]))
-		radius := radiusInMetersToS1Angle(float64(c.RadiusInMeters))
-		projected := s2pgn.Project(&cp)
-		distance := projected.Distance(cp)
-
-		return distance <= radius, nil
-	}
-
-	// check if the other shape is a envelope.
-	if e, ok := other.(*envelope); ok {
-
-		s2rectInDoc := s2RectFromBounds(e.Vertices[0], e.Vertices[1])
-		s2pgnInDoc := s2PolygonFromS2Rectangle(s2rectInDoc)
-		if s2pgn.Intersects(s2pgnInDoc) {
-			return true, nil
-		}
-		return false, nil
-	}
-
-	return false, fmt.Errorf("unknown geojson type: %s "+
-		" found in document", other.Type())
-}
-
-// checkMultiPolygonContainsShape checks whether the given polygons
-// collectively contains the shape in the document.
-func checkMultiPolygonContainsShape(s2pgns []*s2.Polygon,
-	shapeIn, other index.GeoJSON) (bool, error) {
-	// check if the other shape is a point.
-	if p2, ok := other.(*point); ok {
-		s2point := s2.PointFromLatLng(
-			s2.LatLngFromDegrees(p2.Vertices[1], p2.Vertices[0]))
-
-		for _, s2pgn := range s2pgns {
-			if s2pgn.ContainsPoint(s2point) {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipoint.
-	if p2, ok := other.(*multipoint); ok {
-		// check the containment for every point in the collection.
-		pointsWithIn := make(map[int]struct{})
-	nextPoint:
-		for pointIndex, point := range p2.Vertices {
-			s2point := s2.PointFromLatLng(s2.LatLngFromDegrees(point[1],
-				point[0]))
-
-			for _, s2pgn := range s2pgns {
-				if s2pgn.ContainsPoint(s2point) {
-					pointsWithIn[pointIndex] = struct{}{}
-					continue nextPoint
-				}
-			}
-		}
-
-		return len(p2.Vertices) == len(pointsWithIn), nil
-	}
-
-	// check if the other shape is a polygon.
-	if p2, ok := other.(*polygon); ok {
-		s2p2 := s2PolygonFromCoordinates(p2.Coordinates())
-
-		for _, s2pgn := range s2pgns {
-			if s2pgn.Contains(s2p2) {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipolygon.
-	if p2, ok := other.(*multipolygon); ok {
-		// check the intersection for every polygon in the collection.
-		polygonsWithIn := make(map[int]struct{})
-	nextPolygon:
-		for pgnIndex, coordinates := range p2.Vertices {
-			s2p2 := s2PolygonFromCoordinates(coordinates)
-
-			for _, s2pgn := range s2pgns {
-				if s2pgn.Contains(s2p2) {
-					polygonsWithIn[pgnIndex] = struct{}{}
-					continue nextPolygon
-				}
-			}
-		}
-
-		return len(p2.Vertices) == len(polygonsWithIn), nil
-	}
-
-	// check if the other shape is a linestring.
-	if ls, ok := other.(*linestring); ok {
-
-		if polygonsContainsLineStrings(s2pgns,
-			[][][]float64{ls.Vertices}) {
-			return true, nil
-		}
-		return false, nil
-	}
-
-	// check if the other shape is a multilinestring.
-	if mls, ok := other.(*multilinestring); ok {
-		// check whether any of the linestring is inside the polygon.
-		if polygonsContainsLineStrings(s2pgns, mls.Vertices) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	if gc, ok := other.(*geometryCollection); ok {
-		shapesWithIn := make(map[int]struct{})
-	nextShape:
-		for pos, shape := range gc.Members() {
-			for _, s2pgn := range s2pgns {
-				contains, err := checkMultiPolygonContainsShape(
-					[]*s2.Polygon{s2pgn}, shapeIn, shape)
-				if err == nil && contains {
-					shapesWithIn[pos] = struct{}{}
-					continue nextShape
-				}
-			}
-		}
-		return len(shapesWithIn) == len(gc.Members()), nil
-	}
-
-	// check if the other shape is a circle.
-	if c, ok := other.(*circle); ok {
-		cp := s2.PointFromLatLng(
-			s2.LatLngFromDegrees(c.Vertices[1], c.Vertices[0]))
-		radius := radiusInMetersToS1Angle(float64(c.RadiusInMeters))
-
-		for _, s2pgn := range s2pgns {
-
-			if s2pgn.ContainsPoint(cp) {
-				projected := s2pgn.ProjectToBoundary(&cp)
-				distance := projected.Distance(cp)
-				if distance >= radius {
-					return true, nil
-				}
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a envelope.
-	if e, ok := other.(*envelope); ok {
-		s2rectInDoc := s2RectFromBounds(e.Vertices[0], e.Vertices[1])
-		// create a polygon from the rectangle and checks the containment.
-		s2pgnInDoc := s2PolygonFromS2Rectangle(s2rectInDoc)
-		for _, s2pgn := range s2pgns {
-			if s2pgn.Contains(s2pgnInDoc) {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	return false, fmt.Errorf("unknown geojson type: %s"+
-		" found in document", other.Type())
-}
-
-// ------------------------------------------------------------------------
-
-// checkCircleIntersectsShape checks for intersection of the
-// shape in the document with the circle.
-func checkCircleIntersectsShape(s2cap *s2.Cap, shapeIn,
-	other index.GeoJSON) (bool, error) {
-	// check if the other shape is a point.
-	if p2, ok := other.(*point); ok {
-		s2cell := s2.CellFromLatLng(s2.LatLngFromDegrees(p2.Vertices[1],
-			p2.Vertices[0]))
-
-		if s2cap.IntersectsCell(s2cell) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipoint.
-	if p2, ok := other.(*multipoint); ok {
-		// check the intersection for any point in the collection.
-		for _, point := range p2.Vertices {
-			s2cell := s2.CellFromLatLng(s2.LatLngFromDegrees(point[1],
-				point[0]))
-
-			if s2cap.IntersectsCell(s2cell) {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a polygon.
-	if p2, ok := other.(*polygon); ok {
-		s2pgn := s2PolygonFromCoordinates(p2.Coordinates())
-		centerPoint := s2cap.Center()
-		projected := s2pgn.Project(&centerPoint)
-		distance := projected.Distance(centerPoint)
-		return distance <= s2cap.Radius(), nil
-	}
-
-	// check if the other shape is a multipolygon.
-	if p2, ok := other.(*multipolygon); ok {
-		// check the intersection for any polygon in the collection.
-		for _, coordinates := range p2.Vertices {
-			s2pgn := s2PolygonFromCoordinates(coordinates)
-			centerPoint := s2cap.Center()
-			projected := s2pgn.Project(&centerPoint)
-			distance := projected.Distance(centerPoint)
-			return distance <= s2cap.Radius(), nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a linestring.
-	if p2, ok := other.(*linestring); ok {
-		start := s2.LatLngFromDegrees(p2.Vertices[0][1], p2.Vertices[0][0])
-		end := s2.LatLngFromDegrees(p2.Vertices[1][1], p2.Vertices[1][0])
-		pl := s2.PolylineFromLatLngs([]s2.LatLng{start, end})
-		projected, _ := pl.Project(s2cap.Center())
-		distance := projected.Distance(s2cap.Center())
-		return distance <= s2cap.Radius(), nil
-	}
-
-	// check if the other shape is a multilinestring.
-	if p2, ok := other.(*multilinestring); ok {
-		polylines := s2PolylinesFromCoordinates(p2.Vertices)
-		for _, pl := range polylines {
-			projected, _ := pl.Project(s2cap.Center())
-			distance := projected.Distance(s2cap.Center())
-			if distance <= s2cap.Radius() {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	if gc, ok := other.(*geometryCollection); ok {
-		// check whether the circle intersects with any of the
-		// member shapes Contains the geometrycollection.
-		if geometryCollectionIntersectsShape(gc, shapeIn) {
-			return true, nil
-		}
-		return false, nil
-	}
-
-	// check if the other shape is a circle.
-	if c, ok := other.(*circle); ok {
-
-		s2capInDoc := s2Cap(c.Vertices, c.RadiusInMeters)
-
-		if s2cap.Intersects(*s2capInDoc) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a envelope.
-	if e, ok := other.(*envelope); ok {
-		s2rectInDoc := s2RectFromBounds(e.Vertices[0], e.Vertices[1])
-
-		if s2rectInDoc.ContainsPoint(s2cap.Center()) {
-			return true, nil
-		}
-
-		latlngs := []s2.LatLng{s2rectInDoc.Vertex(0), s2rectInDoc.Vertex(1),
-			s2rectInDoc.Vertex(2), s2rectInDoc.Vertex(3), s2rectInDoc.Vertex(0)}
-		pl := s2.PolylineFromLatLngs(latlngs)
-		projected, _ := pl.Project(s2cap.Center())
-		distance := projected.Distance(s2cap.Center())
-		if distance <= s2cap.Radius() {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	return false, fmt.Errorf("unknown geojson type: %s"+
-		" found in document", other.Type())
-}
-
-// checkCircleContainsShape checks for containment of the
-// shape in the document with the circle.
-func checkCircleContainsShape(s2cap *s2.Cap,
-	shapeIn, other index.GeoJSON) (bool, error) {
-	// check if the other shape is a point.
-	if p2, ok := other.(*point); ok {
-		s2point := s2.PointFromLatLng(s2.LatLngFromDegrees(
-			p2.Vertices[1], p2.Vertices[0]))
-
-		if s2cap.ContainsPoint(s2point) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipoint.
-	if p2, ok := other.(*multipoint); ok {
-		// check the intersection for every point in the collection.
-		for _, point := range p2.Vertices {
-			s2point := s2.PointFromLatLng(s2.LatLngFromDegrees(
-				point[1], point[0]))
-
-			if !s2cap.ContainsPoint(s2point) {
-				return false, nil
-			}
-		}
-
-		return true, nil
-	}
-
-	// check if the other shape is a polygon.
-	if p2, ok := other.(*polygon); ok {
-		for _, vertex := range p2.Vertices {
-			for _, v := range vertex {
-				if !s2cap.ContainsPoint(s2.PointFromLatLng(
-					s2.LatLngFromDegrees(v[1], v[0]))) {
-					return false, nil
-				}
-			}
-		}
-
-		return true, nil
-	}
-
-	// check if the other shape is a multipolygon.
-	if p2, ok := other.(*multipolygon); ok {
-		// check the containment for every polygon in the collection.
-		for _, coordinates := range p2.Vertices {
-			for _, vertex := range coordinates {
-				for _, v := range vertex {
-					if !s2cap.ContainsPoint(s2.PointFromLatLng(
-						s2.LatLngFromDegrees(v[1], v[0]))) {
-						return false, nil
-					}
-				}
-			}
-		}
-
-		return true, nil
-	}
-
-	// check if the other shape is a linestring.
-	if p2, ok := other.(*linestring); ok {
-		start := s2.PointFromLatLng(s2.LatLngFromDegrees(
-			p2.Vertices[0][1], p2.Vertices[0][0]))
-		end := s2.PointFromLatLng(s2.LatLngFromDegrees(
-			p2.Vertices[1][1], p2.Vertices[1][0]))
-		// check whether both the end vertices are inside the circle.
-		if s2cap.ContainsPoint(start) && s2cap.ContainsPoint(end) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multilinestring.
-	if p2, ok := other.(*multilinestring); ok {
-		for _, lines := range p2.Vertices {
-			start := s2.PointFromLatLng(s2.LatLngFromDegrees(
-				lines[0][1], lines[0][0]))
-			end := s2.PointFromLatLng(s2.LatLngFromDegrees(
-				lines[1][1], lines[1][0]))
-			// check whether both the end vertices are inside the circle.
-			if !(s2cap.ContainsPoint(start) && s2cap.ContainsPoint(end)) {
-				return false, nil
-			}
-		}
-
-		return true, nil
-	}
-
-	if gc, ok := other.(*geometryCollection); ok {
-		for _, shape := range gc.Members() {
-			contains, err := shapeIn.Contains(shape)
-			if err == nil && !contains {
-				return false, nil
-			}
-		}
-		return true, nil
-	}
-
-	// check if the other shape is a circle.
-	if c, ok := other.(*circle); ok {
-		s2capInDoc := s2Cap(c.Vertices, c.RadiusInMeters)
-
-		if s2cap.Contains(*s2capInDoc) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a envelope.
-	if e, ok := other.(*envelope); ok {
-		s2rectInDoc := s2RectFromBounds(e.Vertices[0], e.Vertices[1])
-
-		for i := 0; i < 4; i++ {
-			if !s2cap.ContainsPoint(
-				s2.PointFromLatLng(s2rectInDoc.Vertex(i))) {
-				return false, nil
-			}
-		}
-
-		return true, nil
-	}
-
-	return false, fmt.Errorf("unknown geojson type: %s"+
-		" found in document", other.Type())
-}
-
-// ------------------------------------------------------------------------
-
-// checkEnvelopeIntersectsShape checks whether the given shape in
-// the document is intersecting Contains the envelope/rectangle.
-func checkEnvelopeIntersectsShape(s2rect *s2.Rect, shapeIn,
-	other index.GeoJSON) (bool, error) {
-	// check if the other shape is a point.
-	if p2, ok := other.(*point); ok {
-		s2cell := s2.CellFromLatLng(s2.LatLngFromDegrees(p2.Vertices[1],
-			p2.Vertices[0]))
-
-		if s2rect.IntersectsCell(s2cell) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipoint.
-	if p2, ok := other.(*multipoint); ok {
-		// check the intersection for any point in the collection.
-		for _, point := range p2.Vertices {
-			s2cell := s2.CellFromLatLng(s2.LatLngFromDegrees(point[1],
-				point[0]))
-
-			if s2rect.IntersectsCell(s2cell) {
-				return true, nil
-			}
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a polygon.
-	if pgn, ok := other.(*polygon); ok {
-
-		if rectangleIntersectsWithPolygons(s2rect,
-			[][][][]float64{pgn.Vertices}) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipolygon.
-	if mpgn, ok := other.(*multipolygon); ok {
-		// check the intersection for any polygon in the collection.
-		if rectangleIntersectsWithPolygons(s2rect, mpgn.Vertices) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a linestring.
-	if ls, ok := other.(*linestring); ok {
-
-		if rectangleIntersectsWithLineStrings(s2rect,
-			[][][]float64{ls.Vertices}) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multilinestring.
-	if mls, ok := other.(*multilinestring); ok {
-
-		if rectangleIntersectsWithLineStrings(s2rect, mls.Vertices) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	if gc, ok := other.(*geometryCollection); ok {
-		// check for the intersection of every member shape
-		// within the geometrycollection.
-		if geometryCollectionIntersectsShape(gc, shapeIn) {
-			return true, nil
-		}
-		return false, nil
-	}
-
-	// check if the other shape is a circle.
-	if c, ok := other.(*circle); ok {
-		s2capInDoc := s2Cap(c.Vertices, c.RadiusInMeters)
-		s2pgn := s2PolygonFromS2Rectangle(s2rect)
-		cp := s2capInDoc.Center()
-		projected := s2pgn.Project(&cp)
-		distance := projected.Distance(s2capInDoc.Center())
-		return distance <= s2capInDoc.Radius(), nil
-	}
-
-	// check if the other shape is a envelope.
-	if e, ok := other.(*envelope); ok {
-		s2rectInDoc := s2RectFromBounds(e.Vertices[0], e.Vertices[1])
-
-		if s2rect.Intersects(*s2rectInDoc) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	return false, fmt.Errorf("unknown geojson type: %s"+
-		" found in document", other.Type())
-}
-
-// checkEnvelopeContainsShape checks whether the given shape in
-// the document is contained Contains the envelope/rectangle.
-func checkEnvelopeContainsShape(s2rect *s2.Rect, shapeIn,
-	other index.GeoJSON) (bool, error) {
-	// check if the other shape is a point.
-	if p2, ok := other.(*point); ok {
-		s2LatLng := s2.LatLngFromDegrees(p2.Vertices[1], p2.Vertices[0])
-
-		if s2rect.ContainsLatLng(s2LatLng) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a multipoint.
-	if p2, ok := other.(*multipoint); ok {
-		// check the intersection for any point in the collection.
-		for _, point := range p2.Vertices {
-			s2LatLng := s2.LatLngFromDegrees(point[1], point[0])
-
-			if !s2rect.ContainsLatLng(s2LatLng) {
-				return false, nil
-			}
-		}
-
-		return true, nil
-	}
-
-	// check if the other shape is a polygon.
-	if p2, ok := other.(*polygon); ok {
-		for _, points := range p2.Vertices {
-			for _, point := range points {
-				if !s2rect.ContainsLatLng(s2.LatLngFromDegrees(point[1],
-					point[0])) {
-					return false, nil
-				}
-			}
-		}
-
-		return true, nil
-	}
-
-	// check if the other shape is a multipolygon.
-	if p2, ok := other.(*multipolygon); ok {
-		// check the intersection for any polygon in the collection.
-		for _, coordinates := range p2.Vertices {
-			for _, points := range coordinates {
-				for _, point := range points {
-					if !s2rect.ContainsLatLng(s2.LatLngFromDegrees(point[1],
-						point[0])) {
-						return false, nil
-					}
-				}
-			}
-		}
-
-		return true, nil
-	}
-
-	// check if the other shape is a linestring.
-	if p2, ok := other.(*linestring); ok {
-		for _, point := range p2.Vertices {
-			s2LatLng := s2.LatLngFromDegrees(point[1], point[0])
-			if !s2rect.ContainsLatLng(s2LatLng) {
-				return false, nil
-			}
-		}
-		return true, nil
-	}
-
-	// check if the other shape is a multilinestring.
-	if p2, ok := other.(*multilinestring); ok {
-		for _, points := range p2.Vertices {
-			for _, point := range points {
-				s2LatLng := s2.LatLngFromDegrees(point[1], point[0])
-				if !s2rect.ContainsLatLng(s2LatLng) {
-					return false, nil
-				}
-			}
-		}
-		return true, nil
-	}
-
-	if gc, ok := other.(*geometryCollection); ok {
-		for _, shape := range gc.Members() {
-			contains, err := shapeIn.Contains(shape)
-			if err == nil && !contains {
-				return false, nil
-			}
-		}
-		return true, nil
-	}
-
-	// check if the other shape is a circle.
-	if c, ok := other.(*circle); ok {
-		cp := s2.PointFromLatLng(s2.LatLngFromDegrees(c.Vertices[1],
-			c.Vertices[0]))
-		angle := radiusInMetersToS1Angle(float64(c.RadiusInMeters))
-		s2capInDoc := s2.CapFromCenterAngle(cp, angle)
-
-		if s2rect.Contains(s2capInDoc.RectBound()) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	// check if the other shape is a envelope.
-	if e, ok := other.(*envelope); ok {
-		s2rectInDoc := s2RectFromBounds(e.Vertices[0], e.Vertices[1])
-
-		if s2rect.Contains(*s2rectInDoc) {
-			return true, nil
-		}
-
-		return false, nil
-	}
-
-	return false, fmt.Errorf("unknown geojson type: %s"+
-		" found in document", other.Type())
-}
diff --git a/geo/geojson_shapes_util.go b/geo/geojson_shapes_util.go
deleted file mode 100644
index ee6d9887..00000000
--- a/geo/geojson_shapes_util.go
+++ /dev/null
@@ -1,354 +0,0 @@
-//  Copyright (c) 2022 Couchbase, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 		http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package geo
-
-import (
-	"encoding/json"
-	"fmt"
-	"strings"
-
-	index "github.com/blevesearch/bleve_index_api"
-)
-
-// FilterGeoShapesOnRelation extracts the shapes in the document, apply
-// the `relation` filter and confirms whether the shape in the document
-//  satisfies the given relation.
-func FilterGeoShapesOnRelation(shape index.GeoJSON, targetShapeBytes []byte,
-	relation string) (bool, error) {
-
-	shapeInDoc, err := extractShapesFromBytes(targetShapeBytes)
-	if err != nil {
-		return false, err
-	}
-
-	return filterShapes(shape, shapeInDoc, relation)
-}
-
-// extractShapesFromBytes unmarshal the bytes to retrieve the
-// embedded geojson shape.
-func extractShapesFromBytes(targetShapeBytes []byte) (
-	index.GeoJSON, error) {
-	meta := struct {
-		Typ string `json:"type"`
-	}{}
-
-	err := json.Unmarshal(targetShapeBytes, &meta)
-	if err != nil {
-		return nil, err
-	}
-
-	var shapeInDoc index.GeoJSON
-
-	switch strings.ToLower(meta.Typ) {
-	case PolygonType:
-		shapeInDoc = &polygon{Typ: meta.Typ}
-		err := json.Unmarshal(targetShapeBytes, shapeInDoc)
-		if err != nil {
-			return nil, err
-		}
-		return shapeInDoc, nil
-
-	case MultiPolygonType:
-		shapeInDoc = &multipolygon{Typ: meta.Typ}
-		err := json.Unmarshal(targetShapeBytes, shapeInDoc)
-		if err != nil {
-			return nil, err
-		}
-		return shapeInDoc, nil
-
-	case PointType:
-		shapeInDoc = &point{Typ: meta.Typ}
-		err := json.Unmarshal(targetShapeBytes, shapeInDoc)
-		if err != nil {
-			return nil, err
-		}
-		return shapeInDoc, nil
-
-	case MultiPointType:
-		shapeInDoc = &multipoint{Typ: meta.Typ}
-		err := json.Unmarshal(targetShapeBytes, shapeInDoc)
-		if err != nil {
-			return nil, err
-		}
-		return shapeInDoc, nil
-
-	case LineStringType:
-		shapeInDoc = &linestring{Typ: meta.Typ}
-		err := json.Unmarshal(targetShapeBytes, shapeInDoc)
-		if err != nil {
-			return nil, err
-		}
-		return shapeInDoc, nil
-
-	case MultiLineStringType:
-		shapeInDoc = &multilinestring{Typ: meta.Typ}
-		err := json.Unmarshal(targetShapeBytes, shapeInDoc)
-		if err != nil {
-			return nil, err
-		}
-		return shapeInDoc, nil
-
-	case GeometryCollectionType:
-		shapeInDoc = &geometryCollection{Typ: meta.Typ}
-		err := json.Unmarshal(targetShapeBytes, shapeInDoc)
-		if err != nil {
-			return nil, err
-		}
-		return shapeInDoc, nil
-
-	case CircleType:
-		shapeInDoc = &circle{Typ: meta.Typ}
-		err := json.Unmarshal(targetShapeBytes, shapeInDoc)
-		if err != nil {
-			return nil, err
-		}
-		return shapeInDoc, nil
-
-	case EnvelopeType:
-		shapeInDoc = &envelope{Typ: meta.Typ}
-		err := json.Unmarshal(targetShapeBytes, shapeInDoc)
-		if err != nil {
-			return nil, err
-		}
-		return shapeInDoc, nil
-
-	}
-
-	return nil, nil
-}
-
-// filterShapes applies the given relation between the query shape
-// and the shape in the document.
-func filterShapes(shape index.GeoJSON,
-	shapeInDoc index.GeoJSON, relation string) (bool, error) {
-
-	if relation == "intersects" {
-		return shape.Intersects(shapeInDoc)
-	}
-
-	if relation == "contains" {
-		return shapeInDoc.Contains(shape)
-	}
-
-	if relation == "within" {
-		return shape.Contains(shapeInDoc)
-	}
-
-	return false, fmt.Errorf("unknown relation: %s", relation)
-}
-
-// ParseGeoJSONShape unmarshals the geojson/circle/envelope shape
-// embedded in the given bytes.
-func ParseGeoJSONShape(input []byte) (index.GeoJSON, error) {
-	var sType string
-	var tmp struct {
-		Typ string `json:"type"`
-	}
-	err := json.Unmarshal(input, &tmp)
-	if err != nil {
-		return nil, err
-	}
-
-	sType = strings.ToLower(tmp.Typ)
-
-	switch sType {
-	case PolygonType:
-		var rv polygon
-		err := json.Unmarshal(input, &rv)
-		if err != nil {
-			return nil, err
-		}
-		return &rv, nil
-
-	case MultiPolygonType:
-		var rv multipolygon
-		err := json.Unmarshal(input, &rv)
-		if err != nil {
-			return nil, err
-		}
-		return &rv, nil
-
-	case PointType:
-		var rv point
-		err := json.Unmarshal(input, &rv)
-		if err != nil {
-			return nil, err
-		}
-		return &rv, nil
-
-	case MultiPointType:
-		var rv multipoint
-		err := json.Unmarshal(input, &rv)
-		if err != nil {
-			return nil, err
-		}
-		return &rv, nil
-
-	case LineStringType:
-		var rv linestring
-		err := json.Unmarshal(input, &rv)
-		if err != nil {
-			return nil, err
-		}
-		return &rv, nil
-
-	case MultiLineStringType:
-		var rv multilinestring
-		err := json.Unmarshal(input, &rv)
-		if err != nil {
-			return nil, err
-		}
-		return &rv, nil
-
-	case GeometryCollectionType:
-		var rv geometryCollection
-		err := json.Unmarshal(input, &rv)
-		if err != nil {
-			return nil, err
-		}
-		return &rv, nil
-
-	case CircleType:
-		var rv circle
-		err := json.Unmarshal(input, &rv)
-		if err != nil {
-			return nil, err
-		}
-		return &rv, nil
-
-	case EnvelopeType:
-		var rv envelope
-		err := json.Unmarshal(input, &rv)
-		if err != nil {
-			return nil, err
-		}
-		return &rv, nil
-
-	default:
-		return nil, fmt.Errorf("unknown shape type: %s", sType)
-	}
-
-	return nil, err
-}
-
-// NewGeoJsonShape instantiate a geojson shape/circle or
-// an envelope from the given coordinates and type.
-func NewGeoJsonShape(coordinates [][][][]float64, typ string) (
-	rv index.GeoJSON, value []byte, err error) {
-	if len(coordinates) == 0 {
-		return nil, nil, fmt.Errorf("missing coordinates")
-	}
-
-	typ = strings.ToLower(typ)
-
-	switch typ {
-	case PointType:
-		rv = &point{Typ: typ, Vertices: coordinates[0][0][0]}
-		value, err = json.Marshal(rv)
-		if err != nil {
-			return rv, nil, err
-		}
-
-	case MultiPointType:
-		rv = &multipoint{Typ: typ, Vertices: coordinates[0][0]}
-		value, err = json.Marshal(rv)
-		if err != nil {
-			return rv, nil, err
-		}
-
-	case LineStringType:
-		rv = &linestring{Typ: typ, Vertices: coordinates[0][0]}
-		value, err = json.Marshal(rv)
-		if err != nil {
-			return rv, nil, err
-		}
-
-	case MultiLineStringType:
-		rv = &multilinestring{Typ: typ, Vertices: coordinates[0]}
-		value, err = json.Marshal(rv)
-		if err != nil {
-			return rv, nil, err
-		}
-
-	case PolygonType:
-		rv = &polygon{Typ: typ, Vertices: coordinates[0]}
-		value, err = json.Marshal(rv)
-		if err != nil {
-			return rv, nil, err
-		}
-
-	case MultiPolygonType:
-		rv = &multipolygon{Typ: typ, Vertices: coordinates}
-		value, err = json.Marshal(rv)
-		if err != nil {
-			return rv, nil, err
-		}
-
-	case EnvelopeType:
-		rv = &envelope{Typ: typ, Vertices: coordinates[0][0]}
-		value, err = json.Marshal(rv)
-		if err != nil {
-			return rv, nil, err
-		}
-
-	default:
-		return rv, nil, fmt.Errorf("unknown shape type: %s", typ)
-	}
-
-	value = append(GlueBytes, value...)
-	return rv, value, nil
-}
-
-// NewGeometryCollection instantiate a geometrycollection
-// and prefix the byte contents with certain glue bytes that
-// can be used later while filering the doc values.
-func NewGeometryCollection(coordinates [][][][][]float64,
-	typs []string) (index.GeoJSON, []byte, error) {
-	shapes := make([]index.GeoJSON, 0, len(coordinates))
-	for i, vertices := range coordinates {
-		s, _, err := NewGeoJsonShape(vertices, typs[i])
-		if err != nil {
-			continue
-		}
-		shapes = append(shapes, s)
-	}
-
-	var gc geometryCollection
-	gc.Typ = GeometryCollectionType
-	gc.Shapes = shapes
-	vbytes, err := json.Marshal(&gc)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	vbytes = append(GlueBytes, vbytes...)
-	return &gc, vbytes, nil
-}
-
-// NewGeoCircleShape instantiate a circle shape and
-// prefix the byte contents with certain glue bytes that
-// can be used later while filering the doc values.
-func NewGeoCircleShape(cp []float64,
-	radiusInMeter float64) (*circle, []byte, error) {
-	rv := &circle{Typ: CircleType, Vertices: cp,
-		RadiusInMeters: radiusInMeter}
-	vbytes, err := json.Marshal(rv)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	vbytes = append(GlueBytes, vbytes...)
-	return rv, vbytes, nil
-}
diff --git a/go.mod b/go.mod
index 4750a341..9aa971c0 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.2-0.20220328092731-fdf526452081
-	github.com/blevesearch/geo v0.1.10
+	github.com/blevesearch/geo v0.1.11
 	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
@@ -23,7 +23,6 @@ require (
 	github.com/blevesearch/zapx/v14 v14.3.3
 	github.com/blevesearch/zapx/v15 v15.3.3
 	github.com/couchbase/moss v0.2.0
-	github.com/golang/geo v0.0.0-20210211234256-740aa86cb551
 	github.com/golang/protobuf v1.3.2
 	github.com/spf13/cobra v0.0.5
 	go.etcd.io/bbolt v1.3.5
diff --git a/go.sum b/go.sum
index c0460cfe..68f37822 100644
--- a/go.sum
+++ b/go.sum
@@ -7,10 +7,8 @@ github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edY
 github.com/blevesearch/bleve_index_api v1.0.1/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
 github.com/blevesearch/bleve_index_api v1.0.2-0.20220328092731-fdf526452081 h1:fNEcBSZemFr2krbmxaEeejjR+31J+12WALApLXEFhPQ=
 github.com/blevesearch/bleve_index_api v1.0.2-0.20220328092731-fdf526452081/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
-github.com/blevesearch/geo v0.1.9 h1:3PkWcg/Os+AVezVKptfHVv6HbIL0qZ9hodQTnoBnLn8=
-github.com/blevesearch/geo v0.1.9/go.mod h1:XqONL2MSA0A3hDWq35mX+dkXbJwq+LNdSgDHvqOhQZM=
-github.com/blevesearch/geo v0.1.10 h1:KgUEYKPPz8uwzIIpUpotWiBAVCqI+xAM8qWad0UKIf0=
-github.com/blevesearch/geo v0.1.10/go.mod h1:XqONL2MSA0A3hDWq35mX+dkXbJwq+LNdSgDHvqOhQZM=
+github.com/blevesearch/geo v0.1.11 h1:+X53A23YCBqu/TwxRNjB7LGu8acC2dG4oY+Zq7IL5yY=
+github.com/blevesearch/geo v0.1.11/go.mod h1:PRmwIBrXhv5neTjdXwpq7WPhgOe1RRK/i1vomza/VAg=
 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=
@@ -65,14 +63,21 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
 github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -94,6 +99,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
diff --git a/search/searcher/search_geoshape.go b/search/searcher/search_geoshape.go
index 871777e7..e835706d 100644
--- a/search/searcher/search_geoshape.go
+++ b/search/searcher/search_geoshape.go
@@ -20,6 +20,7 @@ import (
 	"github.com/blevesearch/bleve/v2/geo"
 	"github.com/blevesearch/bleve/v2/search"
 	index "github.com/blevesearch/bleve_index_api"
+	"github.com/blevesearch/geo/geojson"
 )
 
 func NewGeoShapeSearcher(indexReader index.IndexReader, shape index.GeoJSON,
@@ -56,22 +57,58 @@ func NewGeoShapeSearcher(indexReader index.IndexReader, shape index.GeoJSON,
 
 }
 
+// Using the same term splitter slice used in the doc values in zap.
+// TODO: This needs to be revisited whenever we change the zap
+// implementation of doc values.
+var termSeparatorSplitSlice = []byte{0xff}
+
 func buildRelationFilterOnShapes(dvReader index.DocValueReader, field string,
 	relation string, shape index.GeoJSON) FilterFunc {
+	// this is for accumulating the shape's actual complete value
+	// spread across multiple docvalue visitor callbacks.
+	var dvShapeValue []byte
+	var startReading, finishReading bool
+	var reader *bytes.Reader
 	return func(d *search.DocumentMatch) bool {
 		var found bool
 
 		err := dvReader.VisitDocValues(d.IndexInternalID,
 			func(field string, term []byte) {
+
 				// only consider the values which are GlueBytes prefixed.
 				if len(term) > geo.GlueBytesOffset {
-					if bytes.Equal(geo.GlueBytes, term[:geo.GlueBytesOffset]) {
 
-						v, err := geo.FilterGeoShapesOnRelation(shape,
-							term[geo.GlueBytesOffset:], relation)
+					if !startReading && bytes.Equal(geo.GlueBytes, term[:geo.GlueBytesOffset]) {
+						if bytes.Equal(geo.GlueBytes, term[len(term)-geo.GlueBytesOffset:]) {
+							term = term[:len(term)-geo.GlueBytesOffset]
+							dvShapeValue = append(dvShapeValue, term[geo.GlueBytesOffset:]...)
+							finishReading = true
+						} else {
+							startReading = true
+							dvShapeValue = append(dvShapeValue, term[geo.GlueBytesOffset:]...)
+							dvShapeValue = append(dvShapeValue, termSeparatorSplitSlice...)
+						}
+					} else if startReading && !finishReading {
+						if bytes.Equal(geo.GlueBytes, term[len(term)-geo.GlueBytesOffset:]) {
+							term = term[:len(term)-geo.GlueBytesOffset]
+							dvShapeValue = append(dvShapeValue, term...)
+							finishReading = true
+						} else {
+							dvShapeValue = append(dvShapeValue, term...)
+							dvShapeValue = append(dvShapeValue, termSeparatorSplitSlice...)
+						}
+					}
+
+					// apply the filter once the entire docvalue is finished reading.
+					if finishReading {
+						v, err := geojson.FilterGeoShapesOnRelation(shape,
+							dvShapeValue, relation, &reader)
 						if err == nil && v {
 							found = true
 						}
+						dvShapeValue = dvShapeValue[:0]
+						startReading = false
+						finishReading = false
 					}
 				}
 			})


Test patch:
diff --git a/search/searcher/search_geoshape_points_test.go b/search/searcher/search_geoshape_points_test.go
index 548fc4b7..fae491d0 100644
--- a/search/searcher/search_geoshape_points_test.go
+++ b/search/searcher/search_geoshape_points_test.go
@@ -26,7 +26,7 @@ import (
 	index "github.com/blevesearch/bleve_index_api"
 )
 
-func TestGeoJsonPointWithInQuery(t *testing.T) {
+func TestGeoJsonPointContainsQuery(t *testing.T) {
 	tests := []struct {
 		point []float64
 		field string
@@ -54,11 +54,11 @@ func TestGeoJsonPointWithInQuery(t *testing.T) {
 
 		// test point on the linestring vertex.
 		{[]float64{77.57776737213135, 12.952074805390097},
-			"geometry", []string{"linestring1"}},
+			"geometry", nil},
 
 		// test point on the multilinestring vertex.
 		{[]float64{77.5779390335083, 12.945006535817749},
-			"geometry", []string{"multilinestring1"}},
+			"geometry", nil},
 
 		// test point on the multipoint vertex.
 		{[]float64{77.56407737731932, 12.951614746607163},
@@ -131,7 +131,7 @@ func TestGeoJsonMultiPointWithInQuery(t *testing.T) {
 		// test multipoint on the linestring vertex.
 		{[][]float64{{77.5841188430786, 12.957093573282744},
 			{77.57776737213135, 12.952074805390097}},
-			"geometry", []string{"linestring1"}},
+			"geometry", nil},
 
 		// test multipoint outside the linestring vertex.
 		{[][]float64{{77.5841188430786, 12.957093573282744},
@@ -142,7 +142,7 @@ func TestGeoJsonMultiPointWithInQuery(t *testing.T) {
 		// test multipoint on the multilinestring vertex.
 		{[][]float64{{77.5779390335083, 12.94471376293191},
 			{77.57218837738037, 12.948268838994263}},
-			"geometry", []string{"multilinestring1"}},
+			"geometry", nil},
 
 		// test multipoint outside the multilinestring vertex.
 		{[][]float64{{77.5779390335083, 12.94471376293191},
diff --git a/search/searcher/search_geoshape_polygon_test.go b/search/searcher/search_geoshape_polygon_test.go
index 9fc18184..65cd1fa0 100644
--- a/search/searcher/search_geoshape_polygon_test.go
+++ b/search/searcher/search_geoshape_polygon_test.go
@@ -761,3 +761,103 @@ func TestGeoJsonPolygonContainsQueryS2LoopPortingIssue(t *testing.T) {
 		}
 	}
 }
+
+func TestGeoJsonPolygonIntersectsQuery1(t *testing.T) {
+	tests := []struct {
+		polygon [][][]float64
+		field   string
+		want    []string
+	}{
+
+		// test non-intersecting query polygon.
+		{[][][]float64{{{97.745361328125,
+			68.21644657802169},
+			{97.701416015625,
+				67.97051353559428}, {97.80029296875,
+				67.97875365614591},
+			{97.745361328125,
+				68.21644657802169}}}, "geometry", nil},
+
+		// test intersecting query polygon.
+		{[][][]float64{{{77.59214401245117,
+			12.966043458314124},
+			{77.58853912353516,
+				12.95232574618635}, {77.60943889617919,
+				12.956466232826733},
+			{77.59214401245117,
+				12.966043458314124}}}, "geometry", nil},
+
+		// test intersecting query polygon for polygon1.
+		{[][][]float64{{{97.0806884765625, 61.61423180712503},
+			{96.7510986328125, 61.54625879879804},
+			{97.305908203125, 61.367777577924},
+			{97.0806884765625, 61.61423180712503}}}, "geometry", []string{"polygon1"}},
+	}
+	i := setupGeoJsonShapesIndexForPolygonQuery1(t)
+	indexReader, err := i.Reader()
+	if err != nil {
+		t.Error(err)
+	}
+	defer func() {
+		err = indexReader.Close()
+		if err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	for n, test := range tests {
+		got, err := runGeoShapePolygonQueryWithRelation("intersects",
+			indexReader, test.polygon, test.field)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if !reflect.DeepEqual(got, test.want) {
+			t.Errorf("test %d, expected %v, got %v for polygon: %+v", n,
+				test.want, got, test.polygon)
+		}
+	}
+}
+
+func setupGeoJsonShapesIndexForPolygonQuery1(t *testing.T) index.Index {
+	analysisQueue := index.NewAnalysisQueue(1)
+	i, err := scorch.NewScorch(
+		gtreap.Name,
+		map[string]interface{}{
+			"path":          "",
+			"spatialPlugin": "s2",
+		},
+		analysisQueue)
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = i.Open()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	polygon1 := [][][][]float64{{{{96.69202458735312, 61.59480859768306},
+		{96.79202458735311, 61.39480859768306},
+		{96.79202458735311, 61.59480859768306},
+		{96.69202458735312, 61.59480859768306}}}}
+	doc := document.NewDocument("polygon1")
+	doc.AddField(document.NewGeoShapeFieldWithIndexingOptions("geometry", []uint64{},
+		polygon1, "polygon", document.DefaultGeoShapeIndexingOptions))
+	err = i.Update(doc)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	polygon2 := [][][][]float64{{{{91.35604953911839, 65.11164029408492},
+		{91.45604953911838, 64.91164029408492},
+		{91.45604953911838, 65.11164029408492},
+		{91.35604953911839, 65.11164029408492}}}}
+	doc = document.NewDocument("polygon2")
+	doc.AddField(document.NewGeoShapeFieldWithIndexingOptions("geometry", []uint64{},
+		polygon2, "polygon", document.DefaultGeoShapeIndexingOptions))
+	err = i.Update(doc)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	return i
+}