Browse Source

Allow docvalues-only search on geo_shape (#94396)

allows searching on a geo_shape field type when the field is not indexed (index: false) but just doc values are enabled.
Ignacio Vera 2 years ago
parent
commit
397d52e24b

+ 5 - 0
docs/changelog/94396.yaml

@@ -0,0 +1,5 @@
+pr: 94396
+summary: Allow docvalues-only search on `geo_shape`
+area: Geo
+type: enhancement
+issues: []

+ 8 - 0
docs/reference/mapping/types/geo-shape.asciidoc

@@ -70,6 +70,14 @@ and reject the whole document.
 |`coerce` |If `true` unclosed linear rings in polygons will be automatically closed.
 | `false`
 
+|`index` |Should the field be quickly searchable? Accepts `true` (default) and `false`.
+Fields that only have <<doc-values,`doc_values`>> enabled can still be queried, albeit slower.
+| `true`
+
+|`doc_values` |Should the field be stored on disk in a column-stride fashion,
+so that it can later be used for aggregations or scripting?
+| `true`
+
 |=======================================================================
 
 

+ 10 - 4
x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldMapper.java

@@ -197,6 +197,7 @@ public class GeoShapeWithDocValuesFieldMapper extends AbstractShapeGeometryField
 
         @Override
         public Query geoShapeQuery(SearchExecutionContext context, String fieldName, ShapeRelation relation, LatLonGeometry... geometries) {
+            failIfNotIndexedNorDocValuesFallback(context);
             // CONTAINS queries are not supported by VECTOR strategy for indices created before version 7.5.0 (Lucene 8.3.0)
             if (relation == ShapeRelation.CONTAINS && context.indexVersionCreated().before(Version.V_7_5_0)) {
                 throw new QueryShardException(
@@ -204,10 +205,15 @@ public class GeoShapeWithDocValuesFieldMapper extends AbstractShapeGeometryField
                     ShapeRelation.CONTAINS + " query relation not supported for Field [" + fieldName + "]."
                 );
             }
-            Query query = LatLonShape.newGeometryQuery(fieldName, relation.getLuceneRelation(), geometries);
-            if (hasDocValues()) {
-                final Query queryDocValues = new LatLonShapeDocValuesQuery(fieldName, relation.getLuceneRelation(), geometries);
-                query = new IndexOrDocValuesQuery(query, queryDocValues);
+            Query query;
+            if (isIndexed()) {
+                query = LatLonShape.newGeometryQuery(fieldName, relation.getLuceneRelation(), geometries);
+                if (hasDocValues()) {
+                    final Query queryDocValues = new LatLonShapeDocValuesQuery(fieldName, relation.getLuceneRelation(), geometries);
+                    query = new IndexOrDocValuesQuery(query, queryDocValues);
+                }
+            } else {
+                query = new LatLonShapeDocValuesQuery(fieldName, relation.getLuceneRelation(), geometries);
             }
             return query;
         }

+ 2 - 0
x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/ingest/CircleProcessorTests.java

@@ -13,6 +13,7 @@ import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.tests.index.RandomIndexWriter;
+import org.elasticsearch.Version;
 import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.geo.GeoJson;
 import org.elasticsearch.common.geo.Orientation;
@@ -210,6 +211,7 @@ public class CircleProcessorTests extends ESTestCase {
 
         SearchExecutionContext mockedContext = mock(SearchExecutionContext.class);
         when(mockedContext.getFieldType(any())).thenReturn(shapeType);
+        when(mockedContext.indexVersionCreated()).thenReturn(Version.CURRENT);
         Query sameShapeQuery = shapeType.geoShapeQuery(mockedContext, fieldName, ShapeRelation.INTERSECTS, geometry);
         Query pointOnDatelineQuery = shapeType.geoShapeQuery(
             mockedContext,

+ 127 - 0
x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/spatial/110_geo_shape_parameters.yml

@@ -0,0 +1,127 @@
+setup:
+  - do:
+      indices.create:
+        index: shapes
+        body:
+          mappings:
+            properties:
+              default:
+                type: geo_shape
+              no_doc_values:
+                type: geo_shape
+                doc_values: false
+              no_index:
+                type: geo_shape
+                index: false
+              no_doc_values_no_index:
+                 type: geo_shape
+                 doc_values: false
+                 index: false
+  - do:
+      bulk:
+        refresh: true
+        body:
+          - index:
+              _index: shapes
+              _id: 1
+          - '{"default": "POINT(4.912350 52.374081)", "no_doc_values": "POINT(4.912350 52.374081)", "no_index": "POINT(4.912350 52.374081)", "no_doc_values_no_index": "POINT(4.912350 52.374081)"}'
+          - index:
+              _index: shapes
+              _id: 2
+          - '{"default": "POINT(2.327000 48.860000)", "no_doc_values": "POINT(2.327000 48.860000)", "no_index": "POINT(2.327000 48.860000)", "no_doc_values_no_index": "POINT(2.327000 48.860000)"}'
+  - do:
+      indices.refresh: {}
+
+---
+"Test field mapping":
+  - do:
+      indices.get_mapping:
+        index: shapes
+
+  - match: {shapes.mappings.properties.default.type: geo_shape }
+  - match: {shapes.mappings.properties.no_doc_values.type: geo_shape }
+  - match: {shapes.mappings.properties.no_doc_values.doc_values: false }
+  - match: {shapes.mappings.properties.no_index.type: geo_shape }
+  - match: {shapes.mappings.properties.no_index.index: false }
+  - match: {shapes.mappings.properties.no_doc_values_no_index.type: geo_shape }
+  - match: {shapes.mappings.properties.no_doc_values_no_index.index: false }
+  - match: {shapes.mappings.properties.no_doc_values_no_index.doc_values: false }
+
+---
+"Test query default field":
+  - do:
+      search:
+        index: shapes
+        body:
+          query:
+            geo_bounding_box:
+              default:
+                top_left:
+                  lat: 55
+                  lon: 4
+                bottom_right:
+                  lat: 50
+                  lon: 5
+
+  - match: { hits.total.value: 1 }
+
+
+---
+"Test query no_doc_values field":
+  - do:
+      search:
+        index: shapes
+        body:
+          query:
+            geo_bounding_box:
+              no_doc_values:
+                top_left:
+                  lat: 55
+                  lon: 4
+                bottom_right:
+                  lat: 50
+                  lon: 5
+
+  - match: { hits.total.value: 1 }
+
+
+---
+"Test query no_index field":
+  - do:
+      search:
+        index: shapes
+        body:
+          query:
+            geo_bounding_box:
+              no_index:
+                top_left:
+                  lat: 55
+                  lon: 4
+                bottom_right:
+                  lat: 50
+                  lon: 5
+
+  - match: { hits.total.value: 1 }
+
+---
+"Test query no_doc_values_no_index field":
+  - do:
+      catch: bad_request
+      search:
+        index: shapes
+        body:
+          query:
+            geo_bounding_box:
+              no_doc_values_no_index:
+                top_left:
+                  lat: 55
+                  lon: 4
+                bottom_right:
+                  lat: 50
+                  lon: 5
+
+  - match: {error.type: search_phase_execution_exception}
+  - match: {error.reason: "all shards failed"}
+  - match: {error.phase: query}
+  - match: {error.failed_shards.0.reason.type:  query_shard_exception}
+  - match: {error.failed_shards.0.reason.reason: "failed to create query: Cannot search on field [no_doc_values_no_index] since it is not indexed nor has doc values."}