Repository: gisaia/ARLAS-server (language: python)

Issue:
H3 aggregation



Discussion / hints:

Fix patch:
diff --git a/arlas-admin/src/main/java/io/arlas/server/admin/task/CollectionAutoDiscover.java b/arlas-admin/src/main/java/io/arlas/server/admin/task/CollectionAutoDiscover.java
index 03547bfd1..d8f8bfd18 100644
--- a/arlas-admin/src/main/java/io/arlas/server/admin/task/CollectionAutoDiscover.java
+++ b/arlas-admin/src/main/java/io/arlas/server/admin/task/CollectionAutoDiscover.java
@@ -79,6 +79,7 @@ private CollectionReferenceDescription checkCollectionValidity(CollectionReferen
         List<String> timestampPaths = configuration.getPreferredTimestampFieldNames();
         List<String> centroidPaths = configuration.getPreferredCentroidFieldNames();
         List<String> geometryPaths = configuration.getPreferredGeometryFieldNames();
+        List<String> h3Paths = configuration.getPreferredH3FieldNames();
         for (String path : idPaths) {
             Object field = MapExplorer.getObjectFromPath(path, collection.properties);
             if (field instanceof CollectionReferenceDescriptionProperty) {
@@ -111,6 +112,13 @@ private CollectionReferenceDescription checkCollectionValidity(CollectionReferen
                 break;
             }
         }
+        for (String path : h3Paths) {
+            Object field = MapExplorer.getObjectFromPath(path, collection.properties);
+            if (field instanceof CollectionReferenceDescriptionProperty) {
+                collection.params.h3Path = path;
+                break;
+            }
+        }
         if (collection.params.idPath == null || collection.params.idPath.isEmpty()
                 || collection.params.centroidPath == null || collection.params.centroidPath.isEmpty()
                 || collection.params.geometryPath == null || collection.params.geometryPath.isEmpty()
diff --git a/arlas-core/pom.xml b/arlas-core/pom.xml
index 49728ffa3..157344cf9 100644
--- a/arlas-core/pom.xml
+++ b/arlas-core/pom.xml
@@ -231,6 +231,11 @@
             <version>${jts.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.uber</groupId>
+            <artifactId>h3</artifactId>
+            <version>${h3.version}</version>
+        </dependency>
 
         <!-- ____________________________________________________ -->
         <!-- LANGUAGE -->
diff --git a/arlas-core/src/main/java/io/arlas/server/core/app/CollectionAutoDiscoverConfiguration.java b/arlas-core/src/main/java/io/arlas/server/core/app/CollectionAutoDiscoverConfiguration.java
index 30a9294dc..bdee4b79d 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/app/CollectionAutoDiscoverConfiguration.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/app/CollectionAutoDiscoverConfiguration.java
@@ -40,6 +40,9 @@ public class CollectionAutoDiscoverConfiguration {
     @JsonProperty("preferred-geometry-field-name")
     public String preferredGeometryFieldName;
 
+    @JsonProperty("preferred-h3-field-name")
+    public String preferredH3FieldName;
+
     @JsonProperty("schedule")
     public int schedule;
 
@@ -66,6 +69,10 @@ public List<String> getPreferredGeometryFieldNames() throws ArlasConfigurationEx
         return getFields(preferredGeometryFieldName);
     }
 
+    public List<String> getPreferredH3FieldNames() throws ArlasConfigurationException {
+        return getFields(preferredH3FieldName);
+    }
+
     @Override
     public String toString() {
         return "CollectionAutoDiscoverConfiguration [preferredIdFieldName=" + preferredIdFieldName + ", preferredTimestampFieldName=" + preferredTimestampFieldName + ", preferredCentroidFieldName="
diff --git a/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticExploreService.java b/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticExploreService.java
index eed55d08c..5636154d1 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticExploreService.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticExploreService.java
@@ -35,11 +35,9 @@
 import io.arlas.server.core.services.CollectionReferenceService;
 import io.arlas.server.core.services.ExploreService;
 import io.arlas.server.core.services.FluidSearchService;
-import io.arlas.server.core.utils.BoundingBox;
-import io.arlas.server.core.utils.GeoTileUtil;
-import io.arlas.server.core.utils.MapExplorer;
-import io.arlas.server.core.utils.UriInfoWrapper;
+import io.arlas.server.core.utils.*;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.tuple.Pair;
 import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.geo.GeoPoint;
 import org.elasticsearch.search.SearchHit;
@@ -373,7 +371,29 @@ private AggregationResponse formatAggregationResult(MultiBucketsAggregation aggr
 
                             });
                 }
-            } else if(aggregationResponse.name.startsWith(DATEHISTOGRAM_AGG)){
+            } else if (aggregationResponse.name.equals(H3_AGG)){
+                GeoPoint geoPoint = getH3Centre(element.keyAsString.toString());
+                element.key = geoPoint;
+                if (!CollectionUtils.isEmpty(aggregatedGeometries)) {
+                    aggregatedGeometries.stream()
+                            .filter(g -> g.isCellOrCellCenterAgg())
+                            .forEach(g -> {
+                                ReturnedGeometry returnedGeometry = new ReturnedGeometry();
+                                returnedGeometry.reference = g.value();
+                                returnedGeometry.isRaw = false;
+                                if (g.isCellAgg()) {
+                                    returnedGeometry.geometry = createPolygonFromH3(element.keyAsString.toString());
+                                } else {
+                                    returnedGeometry.geometry = new Point(geoPoint.getLon(), geoPoint.getLat());
+                                }
+                                if (element.geometries == null) {
+                                    element.geometries = new ArrayList<>();
+                                }
+                                element.geometries.add(returnedGeometry);
+
+                            });
+                }
+            } else if (aggregationResponse.name.startsWith(DATEHISTOGRAM_AGG)){
                 element.key = ((ZonedDateTime)bucket.getKey()).withZoneSameInstant(ZoneOffset.UTC).toInstant().toEpochMilli();
             } else {
                 element.key = bucket.getKey();
@@ -395,7 +415,7 @@ private AggregationResponse formatAggregationResult(MultiBucketsAggregation aggr
                                 .map(Arrays::asList)
                                 .map(hitsList -> hitsList.stream().map(SearchHit::getSourceAsMap).collect(Collectors.toList()))
                                 .orElse(new ArrayList());
-                    } else if (Arrays.asList(DATEHISTOGRAM_AGG, HISTOGRAM_AGG, TERM_AGG, GEOHASH_AGG, GEOTILE_AGG).contains(subAggregation.getName())) {
+                    } else if (Arrays.asList(DATEHISTOGRAM_AGG, HISTOGRAM_AGG, TERM_AGG, GEOHASH_AGG, GEOTILE_AGG, H3_AGG).contains(subAggregation.getName())) {
                         subAggregationResponse = formatAggregationResult(((MultiBucketsAggregation) subAggregation), subAggregationResponse, collection, aggregationsRequest, aggTreeDepth+1);
                     } else if (isAggregatedGeometry(subAggregation.getName(), aggregatedGeometries)) {
                         subAggregationResponse = null;
@@ -527,6 +547,11 @@ private GeoPoint getGeohashCentre(String geohash) {
         return new GeoPoint(lat, lon);
     }
 
+    private GeoPoint getH3Centre(String h3) {
+        Pair<Double, Double> latLon = H3Util.getInstance().getCellCenterAsLatLon(h3);
+        return new GeoPoint(latLon.getLeft(), latLon.getRight());
+    }
+
     private GeoPoint getTileCentre(BoundingBox bbox) {
         double lon = (bbox.getEast() + bbox.getWest()) / 2;
         double lat = (bbox.getNorth() + bbox.getSouth()) / 2;
diff --git a/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticFluidSearch.java b/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticFluidSearch.java
index 9ef7192a1..dbd37c6a2 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticFluidSearch.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticFluidSearch.java
@@ -105,6 +105,7 @@ public SearchResponse exec() throws ArlasException {
         SearchResponse result = null;
         request.source(searchSourceBuilder);
         result = client.search(request);
+        LOGGER.debug("RESULT : " + result.toString());
         return result;
     }
 
@@ -432,6 +433,7 @@ private AggregationBuilder aggregateRecursive(List<Aggregation> aggregations, Ag
         Aggregation aggregationModel = aggregations.get(0);
         if (isGeoAggregate && counter == 0) {
             if (!GEO_AGGREGATION_TYPE_ENUMS.contains(aggregationModel.type)
+                    && !aggregationModel.type.equals(AggregationTypeEnum.h3)
                     && aggregationModel.rawGeometries == null
                     && aggregationModel.aggregatedGeometries == null) {
                 throw new NotAllowedException("'" + aggregationModel.type.name() +"' aggregation type is not allowed in _geoaggregate service if at least `aggregated_geometries` or `raw_geometries` parameters are not specified");
@@ -453,6 +455,9 @@ private AggregationBuilder aggregateRecursive(List<Aggregation> aggregations, Ag
             case term:
                 aggregationBuilder = buildTermsAggregation(aggregationModel);
                 break;
+            case h3:
+                aggregationBuilder = buildH3Aggregation(aggregationModel);
+                break;
         }
         aggregations.remove(0);
         if (aggregations.size() == 0) {
@@ -596,6 +601,31 @@ private GeoGridAggregationBuilder buildGeotileAggregation(Aggregation aggregatio
         return geoTileAggregationBuilder;
     }
 
+    // construct and returns the h3 aggregationModel builder
+    private TermsAggregationBuilder buildH3Aggregation(Aggregation aggregationModel) throws ArlasException {
+        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms(H3_AGG);
+        aggregationModel.field = aggregationModel.field + "." + aggregationModel.interval.value;
+        if (aggregationModel.size == null) {
+            aggregationModel.size = "10000"; // by default
+        }
+        //get the field, format, collect_field, collect_fct, order, on
+        termsAggregationBuilder = (TermsAggregationBuilder) setAggregationParameters(aggregationModel, termsAggregationBuilder);
+        termsAggregationBuilder = (TermsAggregationBuilder) setAggregatedGeometries(aggregationModel, termsAggregationBuilder);
+        termsAggregationBuilder = (TermsAggregationBuilder) setRawGeometries(aggregationModel, termsAggregationBuilder);
+        termsAggregationBuilder = (TermsAggregationBuilder) setHitsToFetch(aggregationModel, termsAggregationBuilder);
+        if (aggregationModel.include != null && !aggregationModel.include.isEmpty()) {
+            String[] includeList = aggregationModel.include.split(",");
+            IncludeExclude includeExclude;
+            if (includeList.length > 1) {
+                includeExclude = new IncludeExclude(includeList, null);
+            } else {
+                includeExclude = new IncludeExclude(includeList[0], null);
+            }
+            termsAggregationBuilder = termsAggregationBuilder.includeExclude(includeExclude);
+        }
+        return termsAggregationBuilder;
+    }
+
     // construct and returns the histogram aggregationModel builder
     private HistogramAggregationBuilder buildHistogramAggregation(Aggregation aggregationModel) throws ArlasException {
         HistogramAggregationBuilder histogramAggregationBuilder = AggregationBuilders.histogram(HISTOGRAM_AGG);
diff --git a/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/service/JdbiFluidSearch.java b/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/service/JdbiFluidSearch.java
index 9f529c13b..8bb3c4c8a 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/service/JdbiFluidSearch.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/service/JdbiFluidSearch.java
@@ -266,6 +266,9 @@ public FluidSearchService aggregate(List<Aggregation> aggregations, Boolean isGe
                 case term:
                     buildTermsAggregation(aggName, aggregationModel);
                     break;
+                case h3:
+                    buildH3Aggregation(aggName, aggregationModel);
+                    break;
             }
         }
 
@@ -344,6 +347,21 @@ private void buildGeotileAggregation(String aggName, Aggregation aggregationMode
         setHitsToFetch(aggName, aggregationModel, sp);
     }
 
+    private void buildH3Aggregation(String aggName, Aggregation aggregationModel) throws ArlasException {
+        aggregationModel.field += "." + aggregationModel.interval.value;
+        TermAggregationSelectClause sp = new TermAggregationSelectClause(aggregationModel.field, aggName);
+
+        if (aggregationModel.include != null && !aggregationModel.include.isEmpty()) {
+            req.addOrWhereClauses(Collections.singletonList(req.formatInCondition(aggregationModel.field, aggregationModel.include.split(COMMA))));
+        }
+        req.addSelectClause(sp);
+        //get the field, format, collect_field, collect_fct, order, on
+        setAggregationParameters(aggName, aggregationModel, sp);
+        setHitsToFetch(aggName, aggregationModel, sp);
+        setAggregatedGeometries(aggName, aggregationModel, sp);
+        setRawGeometries(aggName, aggregationModel, sp);
+    }
+
     private void buildHistogramAggregation(String aggName, Aggregation aggregationModel) throws ArlasException {
         HistogramAggregationSelectClause sp = new HistogramAggregationSelectClause(aggregationModel.field,
                 aggName, (Double) aggregationModel.interval.value);
diff --git a/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReference.java b/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReference.java
index 45411dc3a..2d7e54ece 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReference.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReference.java
@@ -28,18 +28,15 @@ public class CollectionReference implements Serializable {
 
     public static final String COLLECTION_NAME = "collection_name";
     public static final String INDEX_NAME = "index_name";
-    public static final String TYPE_NAME = "type_name";
     public static final String ID_PATH = "id_path";
     public static final String GEOMETRY_PATH = "geometry_path";
     public static final String CENTROID_PATH = "centroid_path";
+    public static final String H3_PATH = "h3_path";
     public static final String TIMESTAMP_PATH = "timestamp_path";
     public static final String TIMESTAMP_FORMAT = "timestamp_format";
     public static final String DEFAULT_TIMESTAMP_FORMAT = "strict_date_optional_time||epoch_millis";
     public static final String EXCLUDE_FIELDS = "exclude_fields";
-    public static final String TAGGABLE_FIELDS = "taggable_fields";
-    public static final String UPDATE_MAX_HITS = "update_max_hits";
     public static final String EXCLUDE_WFS_FIELDS = "exclude_wfs_fields";
-    public static final String CUSTOM_PARAMS = "custom_params";
     public static final String INSPIRE_PATH = "inspire";
     public static final String DUBLIN_CORE_PATH = "dublin_core_element_name";
     public static final String INSPIRE_LINEAGE = "lineage";
diff --git a/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReferenceParameters.java b/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReferenceParameters.java
index dfc4b4d8d..433564baf 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReferenceParameters.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReferenceParameters.java
@@ -47,6 +47,9 @@ public class CollectionReferenceParameters implements Serializable {
     @JsonProperty(value = "centroid_path", required = true)
     public String centroidPath;
 
+    @JsonProperty(value = "h3_path", required = false)
+    public String h3Path;
+
     @JsonProperty(value = "timestamp_path", required = true)
     public String timestampPath;
 
diff --git a/arlas-core/src/main/java/io/arlas/server/core/model/enumerations/AggregationTypeEnum.java b/arlas-core/src/main/java/io/arlas/server/core/model/enumerations/AggregationTypeEnum.java
index 9377998d8..7a8eacae4 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/model/enumerations/AggregationTypeEnum.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/model/enumerations/AggregationTypeEnum.java
@@ -20,5 +20,5 @@
 package io.arlas.server.core.model.enumerations;
 
 public enum AggregationTypeEnum {
-    datehistogram, geohash, geotile, histogram, term;
+    datehistogram, geohash, geotile, histogram, term, h3;
 }
\ No newline at end of file
diff --git a/arlas-core/src/main/java/io/arlas/server/core/services/CollectionReferenceService.java b/arlas-core/src/main/java/io/arlas/server/core/services/CollectionReferenceService.java
index bd5e995ae..523d1731f 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/services/CollectionReferenceService.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/services/CollectionReferenceService.java
@@ -263,6 +263,8 @@ protected void checkCollectionReferenceParameters(CollectionReference collection
             fields.add(collectionReference.params.geometryPath);
         if (collectionReference.params.centroidPath != null)
             fields.add(collectionReference.params.centroidPath);
+        if (collectionReference.params.h3Path != null)
+            fields.add(collectionReference.params.h3Path);
         if (collectionReference.params.timestampPath != null)
             fields.add(collectionReference.params.timestampPath);
         if(!StringUtil.isNullOrEmpty(collectionReference.params.excludeFields)){
diff --git a/arlas-core/src/main/java/io/arlas/server/core/services/ExploreService.java b/arlas-core/src/main/java/io/arlas/server/core/services/ExploreService.java
index a8a650d87..4a7138466 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/services/ExploreService.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/services/ExploreService.java
@@ -27,6 +27,7 @@
 import io.arlas.server.core.model.request.*;
 import io.arlas.server.core.model.response.*;
 import io.arlas.server.core.utils.*;
+import org.apache.commons.lang3.tuple.Pair;
 import org.geojson.*;
 import org.locationtech.spatial4j.context.SpatialContext;
 import org.locationtech.spatial4j.io.GeohashUtils;
@@ -264,6 +265,21 @@ protected void searchAfterPage(Page page, String idCollectionField, FluidSearchS
         }
     }
 
+    protected Polygon createPolygonFromH3(String h3) {
+        List<Pair<Double, Double>> latLonList = H3Util.getInstance().getCellBoundaryAsLatLonList(h3);
+        if (latLonList.size() > 7) {
+            LOGGER.debug(h3);
+            LOGGER.debug(latLonList.toString());
+        }
+        Polygon box = new Polygon();
+        List<LngLatAlt> bounds = new ArrayList<>();
+        latLonList.stream().forEach(g -> bounds.add(new LngLatAlt(g.getRight(), g.getLeft())));
+        // add first point in order to close the polygon
+        bounds.add(new LngLatAlt(latLonList.get(0).getRight(), latLonList.get(0).getLeft()));
+        box.add(bounds);
+
+        return box;
+    }
     protected Polygon createPolygonFromGeohash(String geohash) {
         Rectangle rectangle = GeohashUtils.decodeBoundary(geohash, SpatialContext.GEO);
         Polygon polygon = new Polygon();
diff --git a/arlas-core/src/main/java/io/arlas/server/core/services/FluidSearchService.java b/arlas-core/src/main/java/io/arlas/server/core/services/FluidSearchService.java
index 8b46498b6..675b35cff 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/services/FluidSearchService.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/services/FluidSearchService.java
@@ -52,6 +52,7 @@ public abstract class FluidSearchService {
     public static final String TERM_AGG = "Term aggregation";
     public static final String GEOHASH_AGG = "Geohash aggregation";
     public static final String GEOTILE_AGG = "Geotile aggregation";
+    public static final String H3_AGG = "H3 aggregation";
     public static final String FETCH_HITS_AGG = "fetched_hits";
     public static final String GEO_DISTANCE = "geodistance";
     public static final String NO_INCLUDE_TO_SPECIFY = "'include-' should not be specified for this aggregation";
@@ -96,6 +97,7 @@ public List<String> getCollectionPaths() {
         return Arrays.asList(collectionReference.params.idPath,
                 collectionReference.params.geometryPath,
                 collectionReference.params.centroidPath,
+                collectionReference.params.h3Path,
                 collectionReference.params.timestampPath);
     }
 
@@ -172,6 +174,8 @@ public static String getAggregationName(String aggName) {
                 return HISTOGRAM_AGG;
             case term:
                 return TERM_AGG;
+            case h3:
+                return H3_AGG;
             default:
                 LOGGER.warn("Getting name for a non defined aggregation type: " + aggName);
                 return aggName;
diff --git a/arlas-core/src/main/java/io/arlas/server/core/utils/CheckParams.java b/arlas-core/src/main/java/io/arlas/server/core/utils/CheckParams.java
index 07b78982a..2acd650c5 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/utils/CheckParams.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/utils/CheckParams.java
@@ -91,7 +91,7 @@ else if (request != null) {
 
     public static void checkAggregationModel(Aggregation aggregation, CollectionReference collectionReference) throws ArlasException {
         // Check 'type' & 'field'
-        if (aggregation.type == null || (aggregation.field == null && aggregation.type != AggregationTypeEnum.datehistogram)) {
+        if (aggregation.type == null || (StringUtil.isNullOrEmpty(aggregation.field) && aggregation.type != AggregationTypeEnum.datehistogram)) {
             throw new InvalidParameterException(INVALID_AGGREGATION);
         }
         // Check 'order'
@@ -224,15 +224,16 @@ public static void checkAggregatedGeometryParameter(Aggregation aggregationModel
             List<AggregatedGeometryEnum> geometries = aggregationModel.aggregatedGeometries;
             if ((geometries.contains(AggregatedGeometryEnum.CELL) || geometries.contains(AggregatedGeometryEnum.CELLCENTER) ||
                     geometries.contains(AggregatedGeometryEnum.GEOHASH) || geometries.contains(AggregatedGeometryEnum.GEOHASH_CENTER))
-                    && !GEO_AGGREGATION_TYPE_ENUMS.contains(aggregationModel.type)) {
+                    && !(GEO_AGGREGATION_TYPE_ENUMS.contains(aggregationModel.type) || aggregationModel.type.equals(AggregationTypeEnum.h3))) {
                 throw new NotAllowedException(AGGREGATED_GEOMETRY_NOT_SUPPORTED);
             }
-            if (GEO_AGGREGATION_TYPE_ENUMS.contains(aggregationModel.type)
+            if ((GEO_AGGREGATION_TYPE_ENUMS.contains(aggregationModel.type) || aggregationModel.type.equals(AggregationTypeEnum.h3))
                     && geometries.isEmpty() && aggregationModel.rawGeometries == null) {
                 aggregationModel.aggregatedGeometries = new ArrayList<>();
                 aggregationModel.aggregatedGeometries.add(AggregatedGeometryEnum.CELLCENTER);
             }
-        } else if (GEO_AGGREGATION_TYPE_ENUMS.contains(aggregationModel.type) && aggregationModel.rawGeometries == null) {
+        } else if ((GEO_AGGREGATION_TYPE_ENUMS.contains(aggregationModel.type) || aggregationModel.type.equals(AggregationTypeEnum.h3))
+                && aggregationModel.rawGeometries == null) {
             aggregationModel.aggregatedGeometries = new ArrayList<>();
             aggregationModel.aggregatedGeometries.add(AggregatedGeometryEnum.CELLCENTER);
         }
@@ -259,6 +260,7 @@ public static void checkAggregationIntervalParameter(Aggregation aggregationMode
                         case datehistogram:
                         case geohash:
                         case geotile:
+                        case h3:
                             intervalValue = ParamsParser.tryParseInteger(interval.value.toString());
                             break;
                         case histogram:
@@ -270,6 +272,7 @@ public static void checkAggregationIntervalParameter(Aggregation aggregationMode
                             case datehistogram:
                                 throw new InvalidParameterException("The datehistogram interval must be a positive integer.");
                             case geotile:
+                            case h3:
                                 if (intervalValue.doubleValue() < 0) {
                                     throw new InvalidParameterException("The geotile precision is not valid. It must be a positive integer.");
                                 }
@@ -282,8 +285,10 @@ public static void checkAggregationIntervalParameter(Aggregation aggregationMode
                     }
                     if (intervalValue != null && aggregationModel.type == AggregationTypeEnum.geohash && ParamsParser.tryParseInteger(interval.value.toString()) >= 13) {
                         throw new InvalidParameterException("The geohash precision is not valid. It must be an integer between 1 and 12.");
-                    } if (intervalValue != null && aggregationModel.type == AggregationTypeEnum.geotile && ParamsParser.tryParseInteger(interval.value.toString()) > 29) {
+                    } else if (intervalValue != null && aggregationModel.type == AggregationTypeEnum.geotile && ParamsParser.tryParseInteger(interval.value.toString()) > 29) {
                         throw new InvalidParameterException("The geotile precision is not valid. It must be an integer between 0 and 29.");
+                    } else if (intervalValue != null && aggregationModel.type == AggregationTypeEnum.h3 && ParamsParser.tryParseInteger(interval.value.toString()) > 15) {
+                        throw new InvalidParameterException("The h3 precision is not valid. It must be an integer between 0 and 15.");
                     } else if (intervalValue != null) {
                         aggregationModel.interval.value = intervalValue;
                     }
@@ -687,6 +692,8 @@ public static void checkReturnedGeometries(CollectionReference collectionReferen
                 fields.add(collectionReference.params.geometryPath);
             if (collectionReference.params.centroidPath != null)
                 fields.add(collectionReference.params.centroidPath);
+            if (collectionReference.params.h3Path != null)
+                fields.add(collectionReference.params.h3Path);
             if (collectionReference.params.timestampPath != null)
                 fields.add(collectionReference.params.timestampPath);
             List<String> excludeField = Arrays.asList(exclude.split(","));
diff --git a/arlas-core/src/main/java/io/arlas/server/core/utils/ColumnFilterUtil.java b/arlas-core/src/main/java/io/arlas/server/core/utils/ColumnFilterUtil.java
index 485fea826..2a9897c54 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/utils/ColumnFilterUtil.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/utils/ColumnFilterUtil.java
@@ -219,6 +219,23 @@ public static List<String> getCollectionMandatoryPaths(CollectionReference colle
                 collectionReference.params.idPath,
                 collectionReference.params.geometryPath,
                 collectionReference.params.centroidPath,
+                collectionReference.params.h3Path,
+                collectionReference.params.h3Path +".0",
+                collectionReference.params.h3Path +".1",
+                collectionReference.params.h3Path +".2",
+                collectionReference.params.h3Path +".3",
+                collectionReference.params.h3Path +".4",
+                collectionReference.params.h3Path +".5",
+                collectionReference.params.h3Path +".6",
+                collectionReference.params.h3Path +".7",
+                collectionReference.params.h3Path +".8",
+                collectionReference.params.h3Path +".9",
+                collectionReference.params.h3Path +".10",
+                collectionReference.params.h3Path +".11",
+                collectionReference.params.h3Path +".12",
+                collectionReference.params.h3Path +".13",
+                collectionReference.params.h3Path +".14",
+                collectionReference.params.h3Path +".15",
                 collectionReference.params.timestampPath);
     }
 
diff --git a/arlas-core/src/main/java/io/arlas/server/core/utils/H3Util.java b/arlas-core/src/main/java/io/arlas/server/core/utils/H3Util.java
new file mode 100644
index 000000000..e0ece173f
--- /dev/null
+++ b/arlas-core/src/main/java/io/arlas/server/core/utils/H3Util.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to Gisaïa under one or more contributor
+ * license agreements. See the NOTICE.txt file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Gisaïa licenses this file to you 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 io.arlas.server.core.utils;
+
+import com.uber.h3core.H3Core;
+import com.uber.h3core.util.GeoCoord;
+import org.apache.commons.lang3.tuple.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class H3Util {
+    private static Logger LOGGER = LoggerFactory.getLogger(H3Util.class);
+    private H3Core h3Core;
+
+    private final static H3Util h3Util = new H3Util();
+
+    public static H3Util getInstance() {
+        return h3Util;
+    }
+
+    private H3Util() {
+        try {
+            h3Core = H3Core.newInstance();
+        } catch (IOException e) {
+            LOGGER.error("!!! Could not instanciate H3Core. H3 will not be supported in this server instance !!!", e);
+        }
+    }
+
+    public Pair<Double, Double> getCellCenterAsLatLon(String h3AsHex) {
+        GeoCoord gc = h3Core.h3ToGeo(h3AsHex);
+        return Pair.of(gc.lat, gc.lng);
+    }
+
+    public List<Pair<Double, Double>> getCellBoundaryAsLatLonList(String h3AsHex) {
+        List<GeoCoord> gc = h3Core.h3ToGeoBoundary(h3AsHex);
+        return gc.stream().map(g -> Pair.of(g.lat, g.lng)).collect(Collectors.toList());
+    }
+
+}
diff --git a/arlas-core/src/main/java/io/arlas/server/core/utils/ParamsParser.java b/arlas-core/src/main/java/io/arlas/server/core/utils/ParamsParser.java
index 48a23bd9b..363f3f168 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/utils/ParamsParser.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/utils/ParamsParser.java
@@ -101,7 +101,7 @@ public static Aggregation getAggregationModel(CollectionReference collectionRefe
             if (parameter.contains(AGG_INTERVAL_PARAM)) {
                 if (aggregationModel.type.equals(AggregationTypeEnum.datehistogram)) {
                     aggregationModel.interval = getDatehistogramAggregationInterval(parameter.substring(AGG_INTERVAL_PARAM.length()));
-                } else if (GEO_AGGREGATION_TYPE_ENUMS.contains(aggregationModel.type)) {
+                } else if (GEO_AGGREGATION_TYPE_ENUMS.contains(aggregationModel.type) || aggregationModel.type.equals(AggregationTypeEnum.h3)) {
                     aggregationModel.interval = getGeohashAggregationInterval(parameter.substring(AGG_INTERVAL_PARAM.length()));
                 } else if (aggregationModel.type.equals(AggregationTypeEnum.histogram)) {
                     aggregationModel.interval = getHistogramAggregationInterval(parameter.substring(AGG_INTERVAL_PARAM.length()));
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/GeoAggregateRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/GeoAggregateRESTService.java
index 92a3b6472..6429f98e6 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/GeoAggregateRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/GeoAggregateRESTService.java
@@ -496,9 +496,9 @@ private List<BoundingBox> getBoundingBoxes(Integer z, Integer x, Integer y, List
                 .collect(Collectors.toList());
         if (intervals.size() > 0) {
             interval = intervals.get(0).value.intValue();
-        }
-        if (interval - z > 7 || interval - z < 0) {
-            throw new InvalidParameterException("(interval - z) must be > 0 and <= 7");
+            if (interval - z > 7 || interval - z < 0) {
+                throw new InvalidParameterException("(interval - z) must be > 0 and <= 7");
+            }
         }
 
         BoundingBox bbox = GeoTileUtil.getBoundingBox(new Tile(x, y, z));
@@ -748,6 +748,9 @@ private FeatureCollection toGeoJson(AggregationResponse aggregationResponse, Agg
                        if (mainAggregationType == AggregationTypeEnum.geohash) {
                            properties.put("geohash", element.keyAsString);
                            tile.ifPresent(s -> properties.put("parent_geohash", s));
+                       } else if (mainAggregationType == AggregationTypeEnum.h3) {
+                           properties.put("h3", element.keyAsString);
+                           tile.ifPresent(s -> properties.put("parent_cell", s));
                        } else if (mainAggregationType == AggregationTypeEnum.geotile) {
                            properties.put("tile", element.keyAsString);
                            tile.ifPresent(s -> properties.put("parent_tile", s));
diff --git a/conf/configuration.yaml b/conf/configuration.yaml
index 63b72f84c..f63808551 100644
--- a/conf/configuration.yaml
+++ b/conf/configuration.yaml
@@ -103,6 +103,7 @@ collection-auto-discover:
   preferred-timestamp-field-name: params.startdate
   preferred-centroid-field-name: geo_params.centroid
   preferred-geometry-field-name: geo,geo_params.geometry
+  preferred-h3-field-name: geo_params.h3
   schedule: ${ARLAS_COLLECTION_AUTODISCOVER_SCHEDULE:-0} # Set to 0 to disable the discovery
 
 ########################################################
diff --git a/pom.xml b/pom.xml
index dfcf7aee9..17a92a6be 100644
--- a/pom.xml
+++ b/pom.xml
@@ -72,6 +72,7 @@
     <geojson.jackson.version>1.8.1</geojson.jackson.version>
     <spatial4j.version>0.7</spatial4j.version>
     <jts.version>1.18.1</jts.version>
+    <h3.version>3.7.0</h3.version>
 
     <!-- AUTH -->
     <java-jwt.version>3.8.2</java-jwt.version>


Test patch:
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/CollectionTool.java b/arlas-tests/src/test/java/io/arlas/server/tests/CollectionTool.java
index 7576095fe..776d8a991 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/CollectionTool.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/CollectionTool.java
@@ -84,6 +84,7 @@ public  void load(long sleepAfter) throws ArlasException {
         params.idPath = DataSetTool.DATASET_ID_PATH;
         params.geometryPath = DataSetTool.WKT_GEOMETRIES ? DataSetTool.DATASET_WKT_GEOMETRY_PATH:DataSetTool.DATASET_GEOMETRY_PATH;
         params.centroidPath = DataSetTool.DATASET_CENTROID_PATH;
+        params.h3Path = DataSetTool.DATASET_H3_PATH;
         params.timestampPath = DataSetTool.DATASET_TIMESTAMP_PATH;
         params.excludeFields = DataSetTool.DATASET_EXCLUDE_FIELDS;
         params.excludeWfsFields = DataSetTool.DATASET_EXCLUDE_WFS_FIELDS;
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/Data.java b/arlas-tests/src/test/java/io/arlas/server/tests/Data.java
index 763c8fa5d..e8a3e59dd 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/Data.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/Data.java
@@ -21,6 +21,9 @@
 
 import org.geojson.Polygon;
 
+import java.util.HashMap;
+import java.util.Map;
+
 public class Data {
     public String id;
     public String fullname;
@@ -41,6 +44,7 @@ public class GeometryParams {
         public Polygon geometry;
         public String wktgeometry;
         public String centroid;
+        public Map<String, String> h3 = new HashMap<>();
         public Polygon second_geometry;
         public String other_geopoint;
     }
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/DataSetTool.java b/arlas-tests/src/test/java/io/arlas/server/tests/DataSetTool.java
index 60422152b..1a3d50e84 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/DataSetTool.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/DataSetTool.java
@@ -21,6 +21,7 @@
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.uber.h3core.H3Core;
 import io.arlas.server.core.app.ElasticConfiguration;
 import io.arlas.server.core.exceptions.ArlasException;
 import io.arlas.server.core.impl.elastic.utils.ElasticClient;
@@ -52,6 +53,7 @@ public class DataSetTool {
     public final static String DATASET_GEOMETRY_PATH="geo_params.geometry";
     public final static String DATASET_WKT_GEOMETRY_PATH="geo_params.wktgeometry";
     public final static String DATASET_CENTROID_PATH="geo_params.centroid";
+    public final static String DATASET_H3_PATH="geo_params.h3";
     public final static String DATASET_TIMESTAMP_PATH="params.startdate";
     public final static String DATASET_EXCLUDE_FIELDS = "params.ci*";
     public final static String DATASET_EXCLUDE_WFS_FIELDS="params.country";
@@ -126,8 +128,9 @@ private static void addAlias(String index, String alias) throws ArlasException {
         client.aliasIndex(index, alias);
     }
 
-    private static void fillIndex(String indexName, int lonMin, int lonMax, int latMin, int latMax) throws JsonProcessingException, ArlasException {
+    private static void fillIndex(String indexName, int lonMin, int lonMax, int latMin, int latMax) throws IOException, ArlasException {
         Data data;
+        H3Core h3 = H3Core.newInstance();
         ObjectMapper mapper = new ObjectMapper();
 
         for (int i = lonMin; i <= lonMax; i += 10) {
@@ -144,6 +147,9 @@ private static void fillIndex(String indexName, int lonMin, int lonMax, int latM
                 }
                 data.params.stopdate = 1l * (i + 1000) * (j + 1000) + 100;
                 data.geo_params.centroid = j + "," + i;
+                for (int res = 0; res <= 15; res++) {
+                    data.geo_params.h3.put(String.valueOf(res), h3.geoToH3Address(j, i, res));
+                }
                 data.geo_params.other_geopoint = j2 + "," + i2;
                 data.params.job = jobs[((Math.abs(i) + Math.abs(j)) / 10) % (jobs.length - 1)];
                 data.params.country = countries[((Math.abs(i) + Math.abs(j)) / 10) % (countries.length - 1)];
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/data/AbstractDataInjector.java b/arlas-tests/src/test/java/io/arlas/server/tests/data/AbstractDataInjector.java
index 7910f4a1c..a95aadf71 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/data/AbstractDataInjector.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/data/AbstractDataInjector.java
@@ -20,6 +20,7 @@
 package io.arlas.server.tests.data;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.uber.h3core.H3Core;
 import io.arlas.server.tests.Data;
 import io.arlas.server.core.exceptions.ArlasException;
 import io.arlas.server.core.model.RasterTileURL;
@@ -45,6 +46,7 @@ public abstract class AbstractDataInjector {
     public final static String DATASET_GEOMETRY_PATH="geo_params_geometry";
     public final static String DATASET_WKT_GEOMETRY_PATH="geo_params_wktgeometry";
     public final static String DATASET_CENTROID_PATH="geo_params_centroid";
+    public final static String DATASET_H3_PATH="geo_params_h3";
     public final static String DATASET_TIMESTAMP_PATH="params_startdate";
     public final static String DATASET_EXCLUDE_FIELDS = "params_ci*";
     public final static String DATASET_EXCLUDE_WFS_FIELDS="params_country";
@@ -91,8 +93,9 @@ public void clearDataSet() throws IOException, ArlasException {
     public abstract void writeData(String dataSinkName, Data data);
     public abstract void clearDataSink(String dataSinkName);
 
-    public void fillDataSink(String dataSinkName, int lonMin, int lonMax, int latMin, int latMax) throws JsonProcessingException, ArlasException {
+    public void fillDataSink(String dataSinkName, int lonMin, int lonMax, int latMin, int latMax) throws IOException, ArlasException {
         Data data;
+        H3Core h3 = H3Core.newInstance();
 
         for (int i = lonMin; i <= lonMax; i += 10) {
             for (int j = latMin; j <= latMax; j += 10) {
@@ -108,6 +111,9 @@ public void fillDataSink(String dataSinkName, int lonMin, int lonMax, int latMin
                 }
                 data.params.stopdate = 1l * (i + 1000) * (j + 1000) + 100;
                 data.geo_params.centroid = j + "," + i;
+                for (int res = 0; res <= 15; res++) {
+                    data.geo_params.h3.put(String.valueOf(res), h3.geoToH3Address(j, i, res));
+                }
                 data.geo_params.other_geopoint = j2 + "," + i2;
                 data.params.job = jobs[((Math.abs(i) + Math.abs(j)) / 10) % (jobs.length - 1)];
                 data.params.country = countries[((Math.abs(i) + Math.abs(j)) / 10) % (countries.length - 1)];
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/data/CollectionInjector.java b/arlas-tests/src/test/java/io/arlas/server/tests/data/CollectionInjector.java
index 1739a8b02..7d23010f3 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/data/CollectionInjector.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/data/CollectionInjector.java
@@ -151,6 +151,7 @@ public  void loadCsw(long sleepAfter) throws IOException {
                     params.idPath = AbstractDataInjector.DATASET_ID_PATH;
                     params.geometryPath = AbstractDataInjector.DATASET_GEOMETRY_PATH;
                     params.centroidPath = AbstractDataInjector.DATASET_CENTROID_PATH;
+                    params.h3Path = AbstractDataInjector.DATASET_H3_PATH;
                     params.timestampPath = AbstractDataInjector.DATASET_TIMESTAMP_PATH;
                     params.excludeFields = AbstractDataInjector.DATASET_EXCLUDE_FIELDS;
                     params.excludeWfsFields = AbstractDataInjector.DATASET_EXCLUDE_WFS_FIELDS;
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/ogc/wfs/WFSServiceIT.java b/arlas-tests/src/test/java/io/arlas/server/tests/ogc/wfs/WFSServiceIT.java
index d85ad7f6e..ae4b8a1a4 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/ogc/wfs/WFSServiceIT.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/ogc/wfs/WFSServiceIT.java
@@ -367,20 +367,20 @@ public void handleInspireInvalidLanguageGetCapabilities(ValidatableResponse then
     public void handleDescribeFeature(ValidatableResponse then) throws Exception {
         if(!DataSetTool.ALIASED_COLLECTION) {
             then.statusCode(200)
-                    .body("xs:schema.complexType.complexContent.extension.sequence.element.size()", equalTo(9));
+                    .body("xs:schema.complexType.complexContent.extension.sequence.element.size()", equalTo(25));
         } else {
             then.statusCode(200)
-                    .body("xs:schema.complexType.complexContent.extension.sequence.element.size()", equalTo(10));
+                    .body("xs:schema.complexType.complexContent.extension.sequence.element.size()", equalTo(26));
         }
     }
 
     public void handleDescribeFeatureColumnFilter(ValidatableResponse then) throws Exception {
         if(!DataSetTool.ALIASED_COLLECTION) {
             then.statusCode(200)
-                    .body("xs:schema.complexType.complexContent.extension.sequence.element.size()", equalTo(8));
+                    .body("xs:schema.complexType.complexContent.extension.sequence.element.size()", equalTo(24));
         } else {
             then.statusCode(200)
-                    .body("xs:schema.complexType.complexContent.extension.sequence.element.size()", equalTo(9));
+                    .body("xs:schema.complexType.complexContent.extension.sequence.element.size()", equalTo(25));
         }
     }
 
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/collections/CollectionServiceIT.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/collections/CollectionServiceIT.java
index 985f04009..be308e0da 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/collections/CollectionServiceIT.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/collections/CollectionServiceIT.java
@@ -341,6 +341,7 @@ private Map<String, Object> getJsonAsMap() {
         jsonAsMap.put(CollectionReference.ID_PATH, DataSetTool.DATASET_ID_PATH);
         jsonAsMap.put(CollectionReference.GEOMETRY_PATH, DataSetTool.DATASET_GEOMETRY_PATH);
         jsonAsMap.put(CollectionReference.CENTROID_PATH, DataSetTool.DATASET_CENTROID_PATH);
+        jsonAsMap.put(CollectionReference.H3_PATH, DataSetTool.DATASET_H3_PATH);
         jsonAsMap.put(CollectionReference.TIMESTAMP_PATH, DataSetTool.DATASET_TIMESTAMP_PATH);
         jsonAsMap.put(CollectionReference.EXCLUDE_FIELDS, DataSetTool.DATASET_EXCLUDE_FIELDS);
         jsonAsMap.put(CollectionReference.EXCLUDE_WFS_FIELDS, DataSetTool.DATASET_EXCLUDE_WFS_FIELDS);
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractAggregatedTest.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractAggregatedTest.java
index 3f7431a69..df0e00a9c 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractAggregatedTest.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractAggregatedTest.java
@@ -195,6 +195,151 @@ public void testGeohashAggregate() throws Exception {
                 32, 16, 25, 1, -170.00000000931323F, -80.00000000931323F, 169.9999999254942F, 79.99999996740371F);
 
 
+    }
+
+    @Test
+    public void testH3Aggregate() throws Exception {
+        //H3
+        aggregationRequest.aggregations.get(0).type = AggregationTypeEnum.h3;
+        aggregationRequest.aggregations.get(0).field = "geo_params.h3";
+        aggregationRequest.aggregations.get(0).interval = new Interval(3, null); //"3";
+        handleMatchingGeohashAggregate(post(aggregationRequest), 595, 1, 1);
+        handleMatchingGeohashAggregate(get("h3:geo_params.h3:interval-3"), 595, 1, 1);
+
+        aggregationRequest.aggregations.get(0).interval = new Interval(1, null); //"1";
+        handleMatchingGeohashAggregate(post(aggregationRequest), 492, 1, 5);
+        handleMatchingGeohashAggregate(get("h3:geo_params.h3:interval-1"), 492, 1, 5);
+
+        handleMatchingGeohashAggregateCenter(post(aggregationRequest), 492, 1, 5, -177.84439F, -83.64732F, 175.02405F, 8365766F);
+        handleMatchingGeohashAggregateCenter(get("h3:geo_params.h3:interval-1"), 492, 1, 5, -177.84439F, -83.64732F, 175.02405F, 8365766F);
+
+        aggregationRequest.aggregations.get(0).aggregatedGeometries = Arrays.asList(AggregatedGeometryEnum.CELLCENTER);
+        handleMatchingGeohashAggregate(post(aggregationRequest), 492, 1, 5);
+        handleMatchingGeohashAggregate(get("h3:geo_params.h3:interval-1:aggregated_geometries-cell_center"), 492, 1, 5);
+
+        handleMatchingGeohashAggregateCenter(post(aggregationRequest), 492, 1, 5, -177.84439F, -83.64732F, 175.02405F, 8365766F);
+        handleMatchingGeohashAggregateCenter(get("h3:geo_params.h3:interval-1:aggregated_geometries-cell_center"), 492, 1, 5, -177.84439F, -83.64732F, 175.02405F, 8365766F);
+
+        aggregationRequest.aggregations.get(0).aggregatedGeometries = null;
+        aggregationRequest.aggregations.get(0).rawGeometries = Arrays.asList(new RawGeometry("geo_params.geometry"));
+        handleMatchingAggregateWithGeometry(post(aggregationRequest), 492, 1, 5, -171F, -81F, 141F, 81F);
+        handleMatchingAggregateWithGeometry(get("h3:geo_params.h3:interval-1:raw_geometries-geo_params.geometry"), 492, 1, 5, -171F, -81F, 141F, 81F);
+
+        aggregationRequest.aggregations.get(0).rawGeometries = Arrays.asList(new RawGeometry("geo_params.geometry", "-params.startdate"));
+        handleMatchingAggregateWithGeometry(post(aggregationRequest), 492, 1, 5, -141F, -81F, 171F, 81F);
+        handleMatchingAggregateWithGeometry(get("h3:geo_params.h3:interval-1:raw_geometries-geo_params.geometry(-params.startdate)"), 492, 1, 5, -141F, -81F, 171F, 81F);
+
+        aggregationRequest.aggregations.get(0).rawGeometries = Arrays.asList(new RawGeometry("geo_params.geometry", "params.age"));
+        handleMatchingAggregateWithGeometry(post(aggregationRequest), 492, 1, 5, -161F, -99F, 161F, 81F);
+        handleMatchingAggregateWithGeometry(get("h3:geo_params.h3:interval-1:raw_geometries-geo_params.geometry(params.age)"), 492, 1, 5, -161F, -99F, 161F, 81F);
+
+        aggregationRequest.aggregations.get(0).rawGeometries = Arrays.asList(new RawGeometry("geo_params.geometry", "-params.age"));
+        handleMatchingAggregateWithGeometry(post(aggregationRequest), 492, 1, 5, -171F, -81F, 171F, 81F);
+        handleMatchingAggregateWithGeometry(get("h3:geo_params.h3:interval-1:raw_geometries-geo_params.geometry(-params.age)"), 492, 1, 5, -171F, -81F, 171F, 81F);
+
+        aggregationRequest.aggregations.get(0).rawGeometries = null;
+
+        aggregationRequest.aggregations.get(0).fetchHits = new HitsFetcher(3, Arrays.asList("params.country", "params.startdate"));
+        handleMatchingAggregateWithFetchedHits(post(aggregationRequest), 492, 3, "params.country", "params.startdate");
+        handleMatchingAggregateWithFetchedHits(get("h3:geo_params.h3:interval-1:fetch_hits-3(params.country,params.startdate)"), 492, 3, "params.country", "params.startdate");
+
+        aggregationRequest.aggregations.get(0).fetchHits = new HitsFetcher(3, Arrays.asList("-params.startdate"));
+        handleMatchingAggregateWithSortedFetchedDates(post(aggregationRequest), 492, 3, 763600, 1263600, "params.startdate");
+        handleMatchingAggregateWithSortedFetchedDates(get("h3:geo_params.h3:interval-1:fetch_hits-3(-params.startdate)"), 492, 3, 763600, 1263600, "params.startdate");
+        aggregationRequest.aggregations.get(0).fetchHits = null;
+
+        aggregationRequest.aggregations.get(0).metrics = new ArrayList<>();
+        aggregationRequest.aggregations.get(0).metrics.add(new Metric("params.startdate", CollectionFunction.AVG));
+        handleMatchingGeohashAggregateWithCollect(post(aggregationRequest),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField,"avg", 790075F, 1230075F);
+        handleMatchingGeohashAggregateWithCollect(get("h3:geo_params.h3:interval-1:collect_field-params.startdate:collect_fct-avg"),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField,"avg", 790075F, 1230075F);
+
+        aggregationRequest.aggregations.get(0).metrics = new ArrayList<>();
+        aggregationRequest.aggregations.get(0).metrics.add(new Metric("params.startdate", CollectionFunction.CARDINALITY));
+        handleMatchingGeohashAggregateWithCollect(post(aggregationRequest),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField,"cardinality", 5F, 25F);
+        handleMatchingGeohashAggregateWithCollect(get("h3:geo_params.h3:interval-1:collect_field-params.startdate:collect_fct-cardinality"),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField,"cardinality", 5F, 25F);
+
+        aggregationRequest.aggregations.get(0).metrics = new ArrayList<>();
+        aggregationRequest.aggregations.get(0).metrics.add(new Metric("params.startdate", CollectionFunction.MAX));
+        handleMatchingGeohashAggregateWithCollect(post(aggregationRequest),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField, "max", 817000F, 1263600F);
+        handleMatchingGeohashAggregateWithCollect(get("h3:geo_params.h3:interval-1:collect_field-params.startdate:collect_fct-max"),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField,"max", 817000F, 1263600F);
+
+        aggregationRequest.aggregations.get(0).metrics = new ArrayList<>();
+        aggregationRequest.aggregations.get(0).metrics.add(new Metric("params.startdate", CollectionFunction.MIN));
+        handleMatchingGeohashAggregateWithCollect(post(aggregationRequest),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField,"min", 763600F, 1197000F);
+        handleMatchingGeohashAggregateWithCollect(get("h3:geo_params.h3:interval-1:collect_field-params.startdate:collect_fct-min"),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField,"min", 763600F, 1197000F);
+
+        aggregationRequest.aggregations.get(0).metrics = new ArrayList<>();
+        aggregationRequest.aggregations.get(0).metrics.add(new Metric("params.startdate", CollectionFunction.SUM));
+        handleMatchingGeohashAggregateWithCollect(post(aggregationRequest),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField,"sum", 5076000F, 28305000F);
+        handleMatchingGeohashAggregateWithCollect(get("h3:geo_params.h3:interval-1:collect_field-params.startdate:collect_fct-sum"),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField,"sum", 5076000F, 28305000F);
+
+        aggregationRequest.aggregations.get(0).metrics = new ArrayList<>();
+        aggregationRequest.aggregations.get(0).metrics.add(new Metric("params.startdate", CollectionFunction.SUM));
+        aggregationRequest.aggregations.get(0).metrics.add(new Metric("params.age", CollectionFunction.MAX));
+        handleMatchingGeohashAggregateWithMultiCollect(post(aggregationRequest),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField, aggregationRequest.aggregations.get(0).metrics.get(1).collectField, "sum", "max", 12641200F, 28305000F, 1600F,13600F);
+        handleMatchingGeohashAggregateWithMultiCollect(get("h3:geo_params.h3:interval-1:collect_field-params.startdate:collect_fct-sum:collect_field-params.age:collect_fct-max"),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField, aggregationRequest.aggregations.get(0).metrics.get(1).collectField,"sum", "max", 12641200F, 28305000F, 1600F,13600F);
+
+
+        aggregationRequest.aggregations.get(0).metrics = new ArrayList<>();
+        aggregationRequest.aggregations.get(0).metrics.add(new Metric("geo_params.centroid", CollectionFunction.GEOCENTROID));
+        handleMatchingGeohashAggregateWithGeocentroidCollect(post(aggregationRequest),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField, "geocentroid", -155.00000031664968F, -65.00000014901161F, 154.99999981373549F, 64.99999981373549F);
+        handleMatchingGeohashAggregateWithGeocentroidCollect(get("h3:geo_params.h3:interval-1:collect_fct-geocentroid:collect_field-geo_params.centroid"),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField, "geocentroid", -155.00000031664968F, -65.00000014901161F, 154.99999981373549F, 64.99999981373549F);
+
+        aggregationRequest.aggregations.get(0).metrics = new ArrayList<>();
+        aggregationRequest.aggregations.get(0).metrics.add(new Metric("geo_params.centroid", CollectionFunction.GEOBBOX));
+        handleMatchingGeohashAggregateWithGeoBboxCollect(post(aggregationRequest),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField, "geobbox", -170.00000000931323F, -80.00000000931323F, 169.9999999254942F, 79.99999996740371F);
+        handleMatchingGeohashAggregateWithGeoBboxCollect(get("h3:geo_params.h3:interval-1:collect_fct-geobbox:collect_field-geo_params.centroid"),
+                492, 1, 5, aggregationRequest.aggregations.get(0).metrics.get(0).collectField,"geobbox", -170.00000000931323F, -80.00000000931323F, 169.9999999254942F, 79.99999996740371F);
+
+        aggregationRequest.aggregations.get(0).metrics = new ArrayList<>();
+        aggregationRequest.aggregations.get(0).aggregatedGeometries = Arrays.asList(AggregatedGeometryEnum.CENTROID);
+        handleMatchingGeohashAggregateWithGeocentroidBucket(post(aggregationRequest),
+                492, 1, 5, 0, -150F, -80F, 150F, 80F);
+        handleMatchingGeohashAggregateWithGeocentroidBucket(get("h3:geo_params.h3:interval-1:aggregated_geometries-centroid"),
+                492, 1, 5, 0, -150F, -80F, 150F, 80F);
+
+        aggregationRequest.aggregations.get(0).aggregatedGeometries = Arrays.asList(AggregatedGeometryEnum.CELL);
+        handleMatchingAggregateWithGeometry(post(aggregationRequest),
+                492, 1, 5, -180F, -90F, 180F, 90F);
+        handleMatchingAggregateWithGeometry(get("h3:geo_params.h3:interval-1:aggregated_geometries-cell"),
+                492, 1, 5, -180F, -90F, 180F, 90F);
+
+        aggregationRequest.aggregations.get(0).aggregatedGeometries = Arrays.asList(AggregatedGeometryEnum.BBOX);
+        handleMatchingGeohashAggregateWithGeoBboxBucket(post(aggregationRequest),
+                492, 1, 5, 0, -170.00000000931323F, -80.00000000931323F, 169.9999999254942F, 79.99999996740371F);
+        handleMatchingGeohashAggregateWithGeoBboxBucket(get("h3:geo_params.h3:interval-1:aggregated_geometries-bbox"),
+                492, 1, 5, 0, -170.00000000931323F, -80.00000000931323F, 169.9999999254942F, 79.99999996740371F);
+
+        aggregationRequest.aggregations.get(0).metrics = new ArrayList<>();
+        aggregationRequest.aggregations.get(0).metrics.add(new Metric("geo_params.centroid", CollectionFunction.GEOBBOX));
+        handleMatchingGeohashAggregateWithGeoBboxBucket(post(aggregationRequest),
+                492, 1, 5, 1, -170.00000000931323F, -80.00000000931323F, 169.9999999254942F, 79.99999996740371F);
+        handleMatchingGeohashAggregateWithGeoBboxBucket(get("h3:geo_params.h3:interval-1:aggregated_geometries-bbox:collect_fct-geobbox:collect_field-geo_params.centroid"),
+                492, 1, 5, 1, -170.00000000931323F, -80.00000000931323F, 169.9999999254942F, 79.99999996740371F);
+
+        aggregationRequest.aggregations.get(0).metrics = new ArrayList<>();
+        aggregationRequest.aggregations.get(0).metrics.add(new Metric("geo_params.centroid", CollectionFunction.GEOCENTROID));
+        handleMatchingGeohashAggregateWithGeoBboxBucket(post(aggregationRequest),
+                492, 1, 5, 1, -170.00000000931323F, -80.00000000931323F, 169.9999999254942F, 79.99999996740371F);
+        handleMatchingGeohashAggregateWithGeoBboxBucket(get("h3:geo_params.h3:interval-1:aggregated_geometries-bbox:collect_fct-geocentroid:collect_field-geo_params.centroid"),
+                492, 1, 5, 1, -170.00000000931323F, -80.00000000931323F, 169.9999999254942F, 79.99999996740371F);
+
+
     }
 
     @Test
@@ -1075,7 +1220,7 @@ public void testInvalidAggregateParameters() throws Exception {
         handleInvalidParameters(post(aggregationRequest));
         handleInvalidParameters(get("term:params.job:interval-1"));
 
-        // INVALID AGGERAGATED_GEOMETY
+        // INVALID AGGREGATED_GEOMETRY
         aggregationRequest.aggregations.get(0).type = AggregationTypeEnum.term;
         aggregationRequest.aggregations.get(0).interval = null;
         aggregationRequest.aggregations.get(0).aggregatedGeometries = Arrays.asList(AggregatedGeometryEnum.CELL);
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractDescribeTest.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractDescribeTest.java
index e661102ef..474a5930d 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractDescribeTest.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractDescribeTest.java
@@ -30,7 +30,7 @@
 
 public abstract class AbstractDescribeTest extends AbstractTestWithCollection {
 
-    private static final String PARAMS_FIELD_EXCEPT_DUBLIN_CORE = "['index_name', 'type_name', 'id_path', 'geometry_path', 'centroid_path', 'timestamp_path', 'exclude_fields', 'update_max_hits', 'taggable_fields', " +
+    private static final String PARAMS_FIELD_EXCEPT_DUBLIN_CORE = "['index_name', 'type_name', 'id_path', 'geometry_path', 'centroid_path','h3_path', 'timestamp_path', 'exclude_fields', 'update_max_hits', 'taggable_fields', " +
             "'exclude_wfs_fields', 'custom_params', 'inspire', 'raster_tile_url', 'raster_tile_width', 'raster_tile_height', 'filter']";
     private static final String DUBLIN_CORE_FIELDS_EXCEPT_DATE = "['title', 'creator', 'subject', 'description', 'publisher', 'contributor', 'type', 'format', 'identifier', 'source', 'language', 'bbox', 'coverage', " +
             "'coverage_centroid']";
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/opensearch/OpenSearchServiceIT.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/opensearch/OpenSearchServiceIT.java
index 97b18c65e..bb5f5f4ce 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/opensearch/OpenSearchServiceIT.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/opensearch/OpenSearchServiceIT.java
@@ -48,8 +48,9 @@ public class OpenSearchServiceIT extends AbstractTestWithCollection {
         AVAILABLE_COLUMNS.put("params.tags", 2);
         AVAILABLE_COLUMNS.put("params.not_indexed", 0);
         AVAILABLE_COLUMNS.put("params.not_enabled", 0);
+        AVAILABLE_COLUMNS.put("geo_params.h3", 32);
     }
-    private static final List<String> MANDATORY_COLUMNS = Arrays.asList("id", "params.startdate");
+    private static final List<String> MANDATORY_COLUMNS = Arrays.asList("id", "params.startdate", "geo_params.h3");
     private static final List<String> ALIASED_COLUMNS = Arrays.asList("params.keywords");
 
     /**
diff --git a/arlas-tests/src/test/resources/arlas.old.mapping.json b/arlas-tests/src/test/resources/arlas.old.mapping.json
index 990670b5c..2a354ee5e 100644
--- a/arlas-tests/src/test/resources/arlas.old.mapping.json
+++ b/arlas-tests/src/test/resources/arlas.old.mapping.json
@@ -19,6 +19,9 @@
     "centroid_path": {
       "type": "text"
     },
+    "h3_path": {
+      "type": "text"
+    },
     "timestamp_path": {
       "type": "text"
     },
diff --git a/arlas-tests/src/test/resources/dataset.alternate.mapping.json b/arlas-tests/src/test/resources/dataset.alternate.mapping.json
index 170be7c68..ce39fc052 100644
--- a/arlas-tests/src/test/resources/dataset.alternate.mapping.json
+++ b/arlas-tests/src/test/resources/dataset.alternate.mapping.json
@@ -56,6 +56,58 @@
         },
         "centroid": {
           "type": "geo_point"
+        },
+        "h3": {
+          "properties": {
+            "0": {
+              "type": "keyword"
+            },
+            "1": {
+              "type": "keyword"
+            },
+            "2": {
+              "type": "keyword"
+            },
+            "3": {
+              "type": "keyword"
+            },
+            "4": {
+              "type": "keyword"
+            },
+            "5": {
+              "type": "keyword"
+            },
+            "6": {
+              "type": "keyword"
+            },
+            "7": {
+              "type": "keyword"
+            },
+            "8": {
+              "type": "keyword"
+            },
+            "9": {
+              "type": "keyword"
+            },
+            "10": {
+              "type": "keyword"
+            },
+            "11": {
+              "type": "keyword"
+            },
+            "12": {
+              "type": "keyword"
+            },
+            "13": {
+              "type": "keyword"
+            },
+            "14": {
+              "type": "keyword"
+            },
+            "15": {
+              "type": "keyword"
+            }
+          }
         }
       }
     }
diff --git a/arlas-tests/src/test/resources/dataset.mapping.json b/arlas-tests/src/test/resources/dataset.mapping.json
index a4c1ebca5..4223dd4a3 100644
--- a/arlas-tests/src/test/resources/dataset.mapping.json
+++ b/arlas-tests/src/test/resources/dataset.mapping.json
@@ -67,6 +67,58 @@
         },
         "other_geopoint": {
           "type": "geo_point"
+        },
+        "h3": {
+          "properties": {
+            "0": {
+              "type": "keyword"
+            },
+            "1": {
+              "type": "keyword"
+            },
+            "2": {
+              "type": "keyword"
+            },
+            "3": {
+              "type": "keyword"
+            },
+            "4": {
+              "type": "keyword"
+            },
+            "5": {
+              "type": "keyword"
+            },
+            "6": {
+              "type": "keyword"
+            },
+            "7": {
+              "type": "keyword"
+            },
+            "8": {
+              "type": "keyword"
+            },
+            "9": {
+              "type": "keyword"
+            },
+            "10": {
+              "type": "keyword"
+            },
+            "11": {
+              "type": "keyword"
+            },
+            "12": {
+              "type": "keyword"
+            },
+            "13": {
+              "type": "keyword"
+            },
+            "14": {
+              "type": "keyword"
+            },
+            "15": {
+              "type": "keyword"
+            }
+          }
         }
       }
     }
diff --git a/arlas-tests/src/test/resources/results/_describe_base_result.json b/arlas-tests/src/test/resources/results/_describe_base_result.json
index be29b86e5..c6de23fcc 100644
--- a/arlas-tests/src/test/resources/results/_describe_base_result.json
+++ b/arlas-tests/src/test/resources/results/_describe_base_result.json
@@ -91,6 +91,93 @@
           "type": "GEO_SHAPE",
           "taggable": false,
           "indexed": true
+        },
+        "h3": {
+          "type": "OBJECT",
+          "properties": {
+            "0": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "1": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "2": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "3": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "4": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "5": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "6": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "7": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "8": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "9": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "10": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "11": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "12": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "13": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "14": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "15": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            }
+          },
+          "taggable": false,
+          "indexed": true
         }
       },
       "taggable": false,
@@ -104,6 +191,7 @@
     "id_path": "id",
     "geometry_path": "geo_params.geometry",
     "centroid_path": "geo_params.centroid",
+    "h3_path": "geo_params.h3",
     "timestamp_path": "params.startdate",
     "exclude_fields": "params.ci*",
     "update_max_hits": 2147483647,
diff --git a/arlas-tests/src/test/resources/results/_describe_base_result_aliased.json b/arlas-tests/src/test/resources/results/_describe_base_result_aliased.json
index 7bf4bd0ab..acb72e775 100644
--- a/arlas-tests/src/test/resources/results/_describe_base_result_aliased.json
+++ b/arlas-tests/src/test/resources/results/_describe_base_result_aliased.json
@@ -96,6 +96,93 @@
           "type": "GEO_POINT",
           "taggable": false,
           "indexed": true
+        },
+        "h3": {
+          "type": "OBJECT",
+          "properties": {
+            "0": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "1": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "2": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "3": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "4": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "5": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "6": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "7": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "8": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "9": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "10": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "11": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "12": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "13": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "14": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "15": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            }
+          },
+          "taggable": false,
+          "indexed": true
         }
       },
       "taggable": false,
@@ -109,6 +196,7 @@
     "id_path": "id",
     "geometry_path": "geo_params.geometry",
     "centroid_path": "geo_params.centroid",
+    "h3_path": "geo_params.h3",
     "timestamp_path": "params.startdate",
     "exclude_fields": "params.ci*",
     "update_max_hits": 2147483647,
diff --git a/arlas-tests/src/test/resources/results/_describe_filtered_result.json b/arlas-tests/src/test/resources/results/_describe_filtered_result.json
index f8ba164b3..4db274af4 100644
--- a/arlas-tests/src/test/resources/results/_describe_filtered_result.json
+++ b/arlas-tests/src/test/resources/results/_describe_filtered_result.json
@@ -76,6 +76,93 @@
           "type": "GEO_SHAPE",
           "taggable": false,
           "indexed": true
+        },
+        "h3": {
+          "type": "OBJECT",
+          "properties": {
+            "0": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "1": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "2": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "3": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "4": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "5": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "6": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "7": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "8": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "9": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "10": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "11": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "12": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "13": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "14": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "15": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            }
+          },
+          "taggable": false,
+          "indexed": true
         }
       },
       "taggable": false,
@@ -89,6 +176,7 @@
     "id_path": "id",
     "geometry_path": "geo_params.geometry",
     "centroid_path": "geo_params.centroid",
+    "h3_path": "geo_params.h3",
     "timestamp_path": "params.startdate",
     "exclude_fields": "params.ci*",
     "update_max_hits": 2147483647,
diff --git a/arlas-tests/src/test/resources/results/_describe_filtered_result_aliased.json b/arlas-tests/src/test/resources/results/_describe_filtered_result_aliased.json
index e1a22f157..943d26c06 100644
--- a/arlas-tests/src/test/resources/results/_describe_filtered_result_aliased.json
+++ b/arlas-tests/src/test/resources/results/_describe_filtered_result_aliased.json
@@ -81,6 +81,93 @@
           "type": "GEO_SHAPE",
           "taggable": false,
           "indexed": true
+        },
+        "h3": {
+          "type": "OBJECT",
+          "properties": {
+            "0": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "1": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "2": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "3": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "4": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "5": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "6": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "7": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "8": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "9": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "10": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "11": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "12": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "13": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "14": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            },
+            "15": {
+              "type": "KEYWORD",
+              "taggable": false,
+              "indexed": true
+            }
+          },
+          "taggable": false,
+          "indexed": true
         }
       },
       "taggable": false,
@@ -94,6 +181,7 @@
     "id_path": "id",
     "geometry_path": "geo_params.geometry",
     "centroid_path": "geo_params.centroid",
+    "h3_path": "geo_params.h3",
     "timestamp_path": "params.startdate",
     "exclude_fields": "params.ci*",
     "update_max_hits": 2147483647,
diff --git a/arlas-tests/src/test/resources/results/_list_base_result.json b/arlas-tests/src/test/resources/results/_list_base_result.json
index 742898f4a..23f8798d8 100644
--- a/arlas-tests/src/test/resources/results/_list_base_result.json
+++ b/arlas-tests/src/test/resources/results/_list_base_result.json
@@ -92,6 +92,93 @@
             "type": "GEO_SHAPE",
             "taggable": false,
             "indexed": true
+          },
+          "h3": {
+            "type": "OBJECT",
+            "properties": {
+              "0": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "1": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "2": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "3": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "4": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "5": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "6": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "7": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "8": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "9": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "10": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "11": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "12": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "13": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "14": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "15": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              }
+            },
+            "taggable": false,
+            "indexed": true
           }
         },
         "taggable": false,
@@ -105,6 +192,7 @@
       "id_path": "id",
       "geometry_path": "geo_params.geometry",
       "centroid_path": "geo_params.centroid",
+      "h3_path": "geo_params.h3",
       "timestamp_path": "params.startdate",
       "exclude_fields": "params.ci*",
       "update_max_hits": 2147483647,
@@ -288,6 +376,93 @@
             "type": "GEO_SHAPE",
             "taggable": false,
             "indexed": true
+          },
+          "h3": {
+            "type": "OBJECT",
+            "properties": {
+              "0": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "1": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "2": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "3": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "4": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "5": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "6": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "7": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "8": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "9": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "10": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "11": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "12": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "13": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "14": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "15": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              }
+            },
+            "taggable": false,
+            "indexed": true
           }
         },
         "taggable": false,
@@ -301,6 +476,7 @@
       "id_path": "id",
       "geometry_path": "geo_params.geometry",
       "centroid_path": "geo_params.centroid",
+      "h3_path": "geo_params.h3",
       "timestamp_path": "params.startdate",
       "exclude_fields": "params.ci*",
       "update_max_hits": 2147483647,
diff --git a/arlas-tests/src/test/resources/results/_list_base_result_aliased.json b/arlas-tests/src/test/resources/results/_list_base_result_aliased.json
index d1e34c39f..86fab1861 100644
--- a/arlas-tests/src/test/resources/results/_list_base_result_aliased.json
+++ b/arlas-tests/src/test/resources/results/_list_base_result_aliased.json
@@ -97,6 +97,93 @@
             "type": "GEO_POINT",
             "taggable": false,
             "indexed": true
+          },
+          "h3": {
+            "type": "OBJECT",
+            "properties": {
+              "0": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "1": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "2": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "3": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "4": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "5": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "6": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "7": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "8": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "9": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "10": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "11": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "12": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "13": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "14": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "15": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              }
+            },
+            "taggable": false,
+            "indexed": true
           }
         },
         "taggable": false,
@@ -110,6 +197,7 @@
       "id_path": "id",
       "geometry_path": "geo_params.geometry",
       "centroid_path": "geo_params.centroid",
+      "h3_path": "geo_params.h3",
       "timestamp_path": "params.startdate",
       "exclude_fields": "params.ci*",
       "update_max_hits": 2147483647,
@@ -298,6 +386,93 @@
             "type": "GEO_POINT",
             "taggable": false,
             "indexed": true
+          },
+          "h3": {
+            "type": "OBJECT",
+            "properties": {
+              "0": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "1": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "2": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "3": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "4": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "5": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "6": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "7": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "8": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "9": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "10": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "11": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "12": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "13": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "14": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "15": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              }
+            },
+            "taggable": false,
+            "indexed": true
           }
         },
         "taggable": false,
@@ -311,6 +486,7 @@
       "id_path": "id",
       "geometry_path": "geo_params.geometry",
       "centroid_path": "geo_params.centroid",
+      "h3_path": "geo_params.h3",
       "timestamp_path": "params.startdate",
       "exclude_fields": "params.ci*",
       "update_max_hits": 2147483647,
diff --git a/arlas-tests/src/test/resources/results/_list_filtered_result.json b/arlas-tests/src/test/resources/results/_list_filtered_result.json
index f6a2ba1c6..fa0d88d65 100644
--- a/arlas-tests/src/test/resources/results/_list_filtered_result.json
+++ b/arlas-tests/src/test/resources/results/_list_filtered_result.json
@@ -77,6 +77,93 @@
             "type": "GEO_SHAPE",
             "taggable": false,
             "indexed": true
+          },
+          "h3": {
+            "type": "OBJECT",
+            "properties": {
+              "0": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "1": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "2": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "3": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "4": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "5": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "6": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "7": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "8": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "9": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "10": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "11": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "12": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "13": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "14": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "15": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              }
+            },
+            "taggable": false,
+            "indexed": true
           }
         },
         "taggable": false,
@@ -90,6 +177,7 @@
       "id_path": "id",
       "geometry_path": "geo_params.geometry",
       "centroid_path": "geo_params.centroid",
+      "h3_path": "geo_params.h3",
       "timestamp_path": "params.startdate",
       "exclude_fields": "params.ci*",
       "update_max_hits": 2147483647,
@@ -259,6 +347,93 @@
             "type": "GEO_SHAPE",
             "taggable": false,
             "indexed": true
+          },
+          "h3": {
+            "type": "OBJECT",
+            "properties": {
+              "0": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "1": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "2": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "3": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "4": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "5": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "6": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "7": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "8": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "9": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "10": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "11": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "12": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "13": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "14": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "15": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              }
+            },
+            "taggable": false,
+            "indexed": true
           }
         },
         "taggable": false,
@@ -272,6 +447,7 @@
       "id_path": "id",
       "geometry_path": "geo_params.geometry",
       "centroid_path": "geo_params.centroid",
+      "h3_path": "geo_params.h3",
       "timestamp_path": "params.startdate",
       "exclude_fields": "params.ci*",
       "update_max_hits": 2147483647,
diff --git a/arlas-tests/src/test/resources/results/_list_filtered_result_aliased.json b/arlas-tests/src/test/resources/results/_list_filtered_result_aliased.json
index c832a1e84..f785a4976 100644
--- a/arlas-tests/src/test/resources/results/_list_filtered_result_aliased.json
+++ b/arlas-tests/src/test/resources/results/_list_filtered_result_aliased.json
@@ -82,6 +82,93 @@
             "type": "GEO_SHAPE",
             "taggable": false,
             "indexed": true
+          },
+          "h3": {
+            "type": "OBJECT",
+            "properties": {
+              "0": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "1": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "2": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "3": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "4": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "5": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "6": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "7": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "8": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "9": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "10": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "11": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "12": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "13": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "14": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "15": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              }
+            },
+            "taggable": false,
+            "indexed": true
           }
         },
         "taggable": false,
@@ -95,6 +182,7 @@
       "id_path": "id",
       "geometry_path": "geo_params.geometry",
       "centroid_path": "geo_params.centroid",
+      "h3_path": "geo_params.h3",
       "timestamp_path": "params.startdate",
       "exclude_fields": "params.ci*",
       "update_max_hits": 2147483647,
@@ -269,6 +357,93 @@
             "type": "GEO_SHAPE",
             "taggable": false,
             "indexed": true
+          },
+          "h3": {
+            "type": "OBJECT",
+            "properties": {
+              "0": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "1": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "2": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "3": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "4": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "5": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "6": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "7": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "8": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "9": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "10": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "11": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "12": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "13": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "14": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              },
+              "15": {
+                "type": "KEYWORD",
+                "taggable": false,
+                "indexed": true
+              }
+            },
+            "taggable": false,
+            "indexed": true
           }
         },
         "taggable": false,
@@ -282,6 +457,7 @@
       "id_path": "id",
       "geometry_path": "geo_params.geometry",
       "centroid_path": "geo_params.centroid",
+      "h3_path": "geo_params.h3",
       "timestamp_path": "params.startdate",
       "exclude_fields": "params.ci*",
       "update_max_hits": 2147483647,