LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/elastic - ogrelasticlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1989 2118 93.9 %
Date: 2024-05-18 15:15:27 Functions: 52 52 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Elasticsearch Translator
       4             :  * Purpose:
       5             :  * Author:
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2011, Adam Estrada
       9             :  * Copyright (c) 2012-2016, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "ogr_elastic.h"
      31             : #include "cpl_conv.h"
      32             : #include "cpl_minixml.h"
      33             : #include "cpl_http.h"
      34             : #include "ogr_api.h"
      35             : #include "ogr_p.h"
      36             : #include "ogr_swq.h"
      37             : #include "../geojson/ogrgeojsonwriter.h"
      38             : #include "../geojson/ogrgeojsonreader.h"
      39             : #include "../geojson/ogrgeojsonutils.h"
      40             : #include "ogr_geo_utils.h"
      41             : 
      42             : #include <cstdlib>
      43             : #include <set>
      44             : 
      45             : /************************************************************************/
      46             : /*                        CPLGettimeofday()                             */
      47             : /************************************************************************/
      48             : 
      49             : #if defined(_WIN32) && !defined(__CYGWIN__)
      50             : #include <sys/timeb.h>
      51             : 
      52             : struct CPLTimeVal
      53             : {
      54             :     time_t tv_sec; /* seconds */
      55             :     long tv_usec;  /* and microseconds */
      56             : };
      57             : 
      58             : static void CPLGettimeofday(struct CPLTimeVal *tp, void * /* timezonep*/)
      59             : {
      60             :     struct _timeb theTime;
      61             : 
      62             :     _ftime(&theTime);
      63             :     tp->tv_sec = static_cast<time_t>(theTime.time);
      64             :     tp->tv_usec = theTime.millitm * 1000;
      65             : }
      66             : #else
      67             : #include <sys/time.h> /* for gettimeofday() */
      68             : #define CPLTimeVal timeval
      69             : #define CPLGettimeofday(t, u) gettimeofday(t, u)
      70             : #endif
      71             : 
      72          16 : static double GetTimestamp()
      73             : {
      74             :     struct CPLTimeVal tv;
      75          16 :     CPLGettimeofday(&tv, nullptr);
      76          16 :     return tv.tv_sec + tv.tv_usec * 1e-6;
      77             : }
      78             : 
      79             : /************************************************************************/
      80             : /*                           OGRElasticLayer()                          */
      81             : /************************************************************************/
      82             : 
      83          48 : OGRElasticLayer::OGRElasticLayer(const char *pszLayerName,
      84             :                                  const char *pszIndexName,
      85             :                                  const char *pszMappingName,
      86             :                                  OGRElasticDataSource *poDS,
      87             :                                  CSLConstList papszOptions,
      88          48 :                                  const char *pszESSearch)
      89             :     :
      90             : 
      91             :       m_poDS(poDS), m_osIndexName(pszIndexName ? pszIndexName : ""),
      92             :       // Types are no longer supported in Elasticsearch 7+.
      93          48 :       m_osMappingName(poDS->m_nMajorVersion < 7
      94          44 :                           ? pszMappingName ? pszMappingName : ""
      95             :                           : ""),
      96          48 :       m_poFeatureDefn(new OGRFeatureDefn(pszLayerName)),
      97             :       m_osWriteMapFilename(
      98             :           CSLFetchNameValueDef(papszOptions, "WRITE_MAPPING",
      99          48 :                                poDS->m_pszWriteMap ? poDS->m_pszWriteMap : "")),
     100          48 :       m_bStoreFields(CPLFetchBool(papszOptions, "STORE_FIELDS", false)),
     101             :       m_osESSearch(pszESSearch ? pszESSearch : ""),
     102          48 :       m_nBulkUpload(poDS->m_nBulkUpload),
     103             :       m_osPrecision(CSLFetchNameValueDef(papszOptions, "GEOM_PRECISION", "")),
     104             :       // Undocumented. Only useful for developers.
     105          48 :       m_bAddPretty(CPLTestBool(CPLGetConfigOption("ES_ADD_PRETTY", "FALSE"))),
     106          96 :       m_bGeoShapeAsGeoJSON(EQUAL(
     107             :           CSLFetchNameValueDef(papszOptions, "GEO_SHAPE_ENCODING", "GeoJSON"),
     108         236 :           "GeoJSON"))
     109             : {
     110             :     const char *pszESGeomType =
     111          48 :         CSLFetchNameValue(papszOptions, "GEOM_MAPPING_TYPE");
     112          48 :     if (pszESGeomType != nullptr)
     113             :     {
     114           2 :         if (EQUAL(pszESGeomType, "GEO_POINT"))
     115           1 :             m_eGeomTypeMapping = ES_GEOMTYPE_GEO_POINT;
     116           1 :         else if (EQUAL(pszESGeomType, "GEO_SHAPE"))
     117           1 :             m_eGeomTypeMapping = ES_GEOMTYPE_GEO_SHAPE;
     118             :     }
     119             : 
     120          48 :     if (CPLFetchBool(papszOptions, "BULK_INSERT", true))
     121             :     {
     122          40 :         m_nBulkUpload =
     123          40 :             atoi(CSLFetchNameValueDef(papszOptions, "BULK_SIZE", "1000000"));
     124             :     }
     125             : 
     126             :     const char *pszStoredFields =
     127          48 :         CSLFetchNameValue(papszOptions, "STORED_FIELDS");
     128          48 :     if (pszStoredFields)
     129           1 :         m_papszStoredFields = CSLTokenizeString2(pszStoredFields, ",", 0);
     130             : 
     131             :     const char *pszNotAnalyzedFields =
     132          48 :         CSLFetchNameValue(papszOptions, "NOT_ANALYZED_FIELDS");
     133          48 :     if (pszNotAnalyzedFields)
     134           1 :         m_papszNotAnalyzedFields =
     135           1 :             CSLTokenizeString2(pszNotAnalyzedFields, ",", 0);
     136             : 
     137             :     const char *pszNotIndexedFields =
     138          48 :         CSLFetchNameValue(papszOptions, "NOT_INDEXED_FIELDS");
     139          48 :     if (pszNotIndexedFields)
     140           1 :         m_papszNotIndexedFields =
     141           1 :             CSLTokenizeString2(pszNotIndexedFields, ",", 0);
     142             : 
     143             :     const char *pszFieldsWithRawValue =
     144          48 :         CSLFetchNameValue(papszOptions, "FIELDS_WITH_RAW_VALUE");
     145          48 :     if (pszFieldsWithRawValue)
     146           0 :         m_papszFieldsWithRawValue =
     147           0 :             CSLTokenizeString2(pszFieldsWithRawValue, ",", 0);
     148             : 
     149             :     const char *pszSingleQueryTimeout =
     150          48 :         CSLFetchNameValue(papszOptions, "SINGLE_QUERY_TIMEOUT");
     151          48 :     if (pszSingleQueryTimeout)
     152             :     {
     153           2 :         m_dfSingleQueryTimeout = CPLAtof(pszSingleQueryTimeout);
     154           2 :         if (m_dfSingleQueryTimeout < 1 && m_dfSingleQueryTimeout >= 1e-3)
     155           2 :             m_osSingleQueryTimeout = CPLSPrintf(
     156           2 :                 "%dms", static_cast<int>(m_dfSingleQueryTimeout * 1000));
     157           0 :         else if (m_dfSingleQueryTimeout >= 1)
     158             :             m_osSingleQueryTimeout =
     159           0 :                 CPLSPrintf("%ds", static_cast<int>(m_dfSingleQueryTimeout));
     160             :     }
     161             : 
     162             :     m_osSingleQueryTerminateAfter =
     163          48 :         CSLFetchNameValueDef(papszOptions, "SINGLE_QUERY_TERMINATE_AFTER", "");
     164          48 :     m_nSingleQueryTerminateAfter = CPLAtoGIntBig(m_osSingleQueryTerminateAfter);
     165             : 
     166             :     const char *pszFeatureIterationTimeout =
     167          48 :         CSLFetchNameValue(papszOptions, "FEATURE_ITERATION_TIMEOUT");
     168          48 :     if (pszFeatureIterationTimeout)
     169             :     {
     170           2 :         m_dfFeatureIterationTimeout = CPLAtof(pszFeatureIterationTimeout);
     171             :     }
     172          48 :     m_nFeatureIterationTerminateAfter = CPLAtoGIntBig(CSLFetchNameValueDef(
     173             :         papszOptions, "FEATURE_ITERATION_TERMINATE_AFTER", ""));
     174             : 
     175          48 :     SetDescription(m_poFeatureDefn->GetName());
     176          48 :     m_poFeatureDefn->Reference();
     177          48 :     m_poFeatureDefn->SetGeomType(wkbNone);
     178             : 
     179          48 :     AddFieldDefn("_id", OFTString, std::vector<CPLString>());
     180             : 
     181          48 :     if (!m_osESSearch.empty())
     182             :     {
     183           3 :         AddFieldDefn("_index", OFTString, std::vector<CPLString>());
     184           3 :         AddFieldDefn("_type", OFTString, std::vector<CPLString>());
     185             :     }
     186             : 
     187          48 :     OGRElasticLayer::ResetReading();
     188          48 : }
     189             : 
     190             : /************************************************************************/
     191             : /*                           OGRElasticLayer()                          */
     192             : /************************************************************************/
     193             : 
     194           2 : OGRElasticLayer::OGRElasticLayer(const char *pszLayerName,
     195           2 :                                  OGRElasticLayer *poReferenceLayer)
     196             :     : OGRElasticLayer(pszLayerName, pszLayerName,
     197             :                       poReferenceLayer->m_osMappingName,
     198           2 :                       poReferenceLayer->m_poDS, nullptr)
     199             : {
     200           2 :     m_bAddSourceIndexName = poReferenceLayer->m_poDS->m_bAddSourceIndexName;
     201             : 
     202           2 :     poReferenceLayer->CopyMembersTo(this);
     203           2 :     auto poFeatureDefn = new OGRFeatureDefn(pszLayerName);
     204           2 :     if (m_bAddSourceIndexName)
     205             :     {
     206           2 :         OGRFieldDefn oFieldDefn("_index", OFTString);
     207           1 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
     208             : #if defined(__GNUC__)
     209             : #pragma GCC diagnostic push
     210             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     211             : #endif
     212           1 :         m_aaosFieldPaths.insert(m_aaosFieldPaths.begin(),
     213           2 :                                 std::vector<CPLString>());
     214             : #if defined(__GNUC__)
     215             : #pragma GCC diagnostic pop
     216             : #endif
     217           3 :         for (auto &kv : m_aosMapToFieldIndex)
     218             :         {
     219           2 :             kv.second++;
     220             :         }
     221             :     }
     222             : 
     223             :     {
     224           2 :         const int nFieldCount = m_poFeatureDefn->GetFieldCount();
     225           8 :         for (int i = 0; i < nFieldCount; i++)
     226           6 :             poFeatureDefn->AddFieldDefn(m_poFeatureDefn->GetFieldDefn(i));
     227             :     }
     228             : 
     229             :     {
     230             :         // Remove the default geometry field created instantiation.
     231           2 :         poFeatureDefn->DeleteGeomFieldDefn(0);
     232           2 :         const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
     233           4 :         for (int i = 0; i < nGeomFieldCount; i++)
     234           2 :             poFeatureDefn->AddGeomFieldDefn(
     235           2 :                 m_poFeatureDefn->GetGeomFieldDefn(i));
     236             :     }
     237             : 
     238           2 :     m_poFeatureDefn->Release();
     239           2 :     m_poFeatureDefn = poFeatureDefn;
     240           2 :     m_poFeatureDefn->Reference();
     241             : 
     242           2 :     CPLAssert(static_cast<int>(m_aaosFieldPaths.size()) ==
     243             :               m_poFeatureDefn->GetFieldCount());
     244           2 :     CPLAssert(static_cast<int>(m_aaosGeomFieldPaths.size()) ==
     245             :               m_poFeatureDefn->GetGeomFieldCount());
     246           2 : }
     247             : 
     248             : /************************************************************************/
     249             : /*                            CopyMembersTo()                           */
     250             : /************************************************************************/
     251             : 
     252           5 : void OGRElasticLayer::CopyMembersTo(OGRElasticLayer *poNew)
     253             : {
     254           5 :     FinalizeFeatureDefn();
     255             : 
     256           5 :     poNew->m_poFeatureDefn->Release();
     257           5 :     poNew->m_poFeatureDefn =
     258           5 :         const_cast<OGRElasticLayer *>(this)->GetLayerDefn()->Clone();
     259           5 :     poNew->m_poFeatureDefn->Reference();
     260           5 :     poNew->m_bFeatureDefnFinalized = true;
     261           5 :     poNew->m_osBulkContent = m_osBulkContent;
     262           5 :     poNew->m_nBulkUpload = m_nBulkUpload;
     263           5 :     poNew->m_osFID = m_osFID;
     264           5 :     poNew->m_aaosFieldPaths = m_aaosFieldPaths;
     265           5 :     poNew->m_aosMapToFieldIndex = m_aosMapToFieldIndex;
     266           5 :     poNew->m_aaosGeomFieldPaths = m_aaosGeomFieldPaths;
     267           5 :     poNew->m_aosMapToGeomFieldIndex = m_aosMapToGeomFieldIndex;
     268           5 :     poNew->m_abIsGeoPoint = m_abIsGeoPoint;
     269           5 :     poNew->m_eGeomTypeMapping = m_eGeomTypeMapping;
     270           5 :     poNew->m_osPrecision = m_osPrecision;
     271           5 :     poNew->m_papszNotAnalyzedFields = CSLDuplicate(m_papszNotAnalyzedFields);
     272           5 :     poNew->m_papszNotIndexedFields = CSLDuplicate(m_papszNotIndexedFields);
     273           5 :     poNew->m_papszFieldsWithRawValue = CSLDuplicate(m_papszFieldsWithRawValue);
     274           5 :     poNew->m_bGeoShapeAsGeoJSON = m_bGeoShapeAsGeoJSON;
     275           5 :     poNew->m_osSingleQueryTimeout = m_osSingleQueryTimeout;
     276           5 :     poNew->m_dfSingleQueryTimeout = m_dfSingleQueryTimeout;
     277           5 :     poNew->m_dfFeatureIterationTimeout = m_dfFeatureIterationTimeout;
     278           5 :     poNew->m_nSingleQueryTerminateAfter = m_nSingleQueryTerminateAfter;
     279           5 :     poNew->m_nFeatureIterationTerminateAfter =
     280           5 :         m_nFeatureIterationTerminateAfter;
     281           5 :     poNew->m_osSingleQueryTerminateAfter = m_osSingleQueryTerminateAfter;
     282           5 : }
     283             : 
     284             : /************************************************************************/
     285             : /*                              Clone()                                 */
     286             : /************************************************************************/
     287             : 
     288           3 : OGRElasticLayer *OGRElasticLayer::Clone()
     289             : {
     290             :     OGRElasticLayer *poNew =
     291           3 :         new OGRElasticLayer(m_poFeatureDefn->GetName(), m_osIndexName,
     292           3 :                             m_osMappingName, m_poDS, nullptr);
     293           3 :     CopyMembersTo(poNew);
     294           3 :     return poNew;
     295             : }
     296             : 
     297             : /************************************************************************/
     298             : /*                         ~OGRElasticLayer()                           */
     299             : /************************************************************************/
     300             : 
     301          96 : OGRElasticLayer::~OGRElasticLayer()
     302             : {
     303          48 :     OGRElasticLayer::SyncToDisk();
     304             : 
     305          48 :     OGRElasticLayer::ResetReading();
     306             : 
     307          48 :     json_object_put(m_poSpatialFilter);
     308          48 :     json_object_put(m_poJSONFilter);
     309             : 
     310         104 :     for (int i = 0; i < (int)m_apoCT.size(); i++)
     311          56 :         delete m_apoCT[i];
     312             : 
     313          48 :     m_poFeatureDefn->Release();
     314             : 
     315          48 :     CSLDestroy(m_papszStoredFields);
     316          48 :     CSLDestroy(m_papszNotAnalyzedFields);
     317          48 :     CSLDestroy(m_papszNotIndexedFields);
     318          48 :     CSLDestroy(m_papszFieldsWithRawValue);
     319          96 : }
     320             : 
     321             : /************************************************************************/
     322             : /*                              AddFieldDefn()                          */
     323             : /************************************************************************/
     324             : 
     325         175 : void OGRElasticLayer::AddFieldDefn(const char *pszName, OGRFieldType eType,
     326             :                                    const std::vector<CPLString> &aosPath,
     327             :                                    OGRFieldSubType eSubType)
     328             : {
     329         350 :     OGRFieldDefn oFieldDefn(pszName, eType);
     330         175 :     oFieldDefn.SetSubType(eSubType);
     331         175 :     if (eSubType == OFSTBoolean)
     332           6 :         oFieldDefn.SetWidth(1);
     333         175 :     m_aaosFieldPaths.push_back(aosPath);
     334         175 :     if (!aosPath.empty())
     335         120 :         m_aosMapToFieldIndex[BuildPathFromArray(aosPath)] =
     336         120 :             m_poFeatureDefn->GetFieldCount();
     337         175 :     m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     338         175 : }
     339             : 
     340             : /************************************************************************/
     341             : /*                           AddGeomFieldDefn()                         */
     342             : /************************************************************************/
     343             : 
     344          37 : void OGRElasticLayer::AddGeomFieldDefn(const char *pszName,
     345             :                                        OGRwkbGeometryType eType,
     346             :                                        const std::vector<CPLString> &aosPath,
     347             :                                        int bIsGeoPoint)
     348             : {
     349          37 :     OGRGeomFieldDefn oFieldDefn(pszName, eType);
     350          37 :     m_aaosGeomFieldPaths.push_back(aosPath);
     351          37 :     m_aosMapToGeomFieldIndex[BuildPathFromArray(aosPath)] =
     352          37 :         m_poFeatureDefn->GetGeomFieldCount();
     353          37 :     m_abIsGeoPoint.push_back(bIsGeoPoint);
     354             : 
     355          37 :     OGRSpatialReference *poSRS_WGS84 = new OGRSpatialReference();
     356          37 :     poSRS_WGS84->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
     357          37 :     poSRS_WGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     358          37 :     oFieldDefn.SetSpatialRef(poSRS_WGS84);
     359          37 :     poSRS_WGS84->Dereference();
     360             : 
     361          37 :     m_poFeatureDefn->AddGeomFieldDefn(&oFieldDefn);
     362             : 
     363          37 :     m_apoCT.push_back(nullptr);
     364          37 : }
     365             : 
     366             : /************************************************************************/
     367             : /*                       GetGeomFieldProperties()                       */
     368             : /************************************************************************/
     369             : 
     370           3 : void OGRElasticLayer::GetGeomFieldProperties(int iGeomField,
     371             :                                              std::vector<CPLString> &aosPath,
     372             :                                              bool &bIsGeoPoint)
     373             : {
     374           3 :     aosPath = m_aaosGeomFieldPaths[iGeomField];
     375           3 :     bIsGeoPoint = CPL_TO_BOOL(m_abIsGeoPoint[iGeomField]);
     376           3 : }
     377             : 
     378             : /************************************************************************/
     379             : /*                     InitFeatureDefnFromMapping()                     */
     380             : /************************************************************************/
     381             : 
     382          36 : void OGRElasticLayer::InitFeatureDefnFromMapping(
     383             :     json_object *poSchema, const char *pszPrefix,
     384             :     const std::vector<CPLString> &aosPath)
     385             : {
     386             :     json_object *poTopProperties =
     387          36 :         CPL_json_object_object_get(poSchema, "properties");
     388          72 :     if (poTopProperties == nullptr ||
     389          36 :         json_object_get_type(poTopProperties) != json_type_object)
     390           0 :         return;
     391             :     json_object_iter it;
     392          36 :     it.key = nullptr;
     393          36 :     it.val = nullptr;
     394          36 :     it.entry = nullptr;
     395         198 :     json_object_object_foreachC(poTopProperties, it)
     396             :     {
     397             :         json_object *poProperties =
     398         162 :             CPL_json_object_object_get(it.val, "properties");
     399         191 :         if (poProperties &&
     400          29 :             json_object_get_type(poProperties) == json_type_object)
     401             :         {
     402             :             json_object *poType =
     403          29 :                 json_ex_get_object_by_path(poProperties, "coordinates.type");
     404          43 :             if (poType && json_object_get_type(poType) == json_type_string &&
     405          14 :                 strcmp(json_object_get_string(poType), "geo_point") == 0)
     406             :             {
     407          28 :                 CPLString osFieldName;
     408          14 :                 if (pszPrefix[0])
     409             :                 {
     410           0 :                     osFieldName = pszPrefix;
     411           0 :                     osFieldName += ".";
     412             :                 }
     413          14 :                 osFieldName += it.key;
     414             : 
     415          14 :                 if (m_poFeatureDefn->GetGeomFieldIndex(osFieldName) < 0)
     416             :                 {
     417          28 :                     std::vector<CPLString> aosNewPaths = aosPath;
     418          14 :                     aosNewPaths.push_back(osFieldName);
     419          14 :                     aosNewPaths.push_back("coordinates");
     420             : 
     421          14 :                     AddGeomFieldDefn(osFieldName, wkbPoint, aosNewPaths, TRUE);
     422             :                 }
     423             : 
     424          14 :                 continue;
     425             :             }
     426             : 
     427          23 :             if (aosPath.empty() && m_osMappingName == "FeatureCollection" &&
     428           8 :                 strcmp(it.key, "properties") == 0)
     429             :             {
     430          16 :                 std::vector<CPLString> aosNewPaths = aosPath;
     431           8 :                 aosNewPaths.push_back(it.key);
     432             : 
     433           8 :                 InitFeatureDefnFromMapping(it.val, pszPrefix, aosNewPaths);
     434             : 
     435           8 :                 continue;
     436             :             }
     437           7 :             else if (m_poDS->m_bFlattenNestedAttributes)
     438             :             {
     439          12 :                 std::vector<CPLString> aosNewPaths = aosPath;
     440           6 :                 aosNewPaths.push_back(it.key);
     441             : 
     442          12 :                 CPLString osPrefix;
     443           6 :                 if (pszPrefix[0])
     444             :                 {
     445           3 :                     osPrefix = pszPrefix;
     446           3 :                     osPrefix += ".";
     447             :                 }
     448           6 :                 osPrefix += it.key;
     449             : 
     450           6 :                 InitFeatureDefnFromMapping(it.val, osPrefix, aosNewPaths);
     451             : 
     452           6 :                 continue;
     453             :             }
     454             :         }
     455             : 
     456         134 :         if (aosPath.empty() && EQUAL(it.key, m_poDS->GetFID()))
     457             :         {
     458           0 :             m_osFID = it.key;
     459             :         }
     460             :         else
     461             :         {
     462         134 :             CreateFieldFromSchema(it.key, pszPrefix, aosPath, it.val);
     463             :         }
     464             :     }
     465             : 
     466          36 :     if (aosPath.empty())
     467             :     {
     468          22 :         json_object *poMeta = CPL_json_object_object_get(poSchema, "_meta");
     469          22 :         if (poMeta && json_object_get_type(poMeta) == json_type_object)
     470             :         {
     471           5 :             json_object *poFID = CPL_json_object_object_get(poMeta, "fid");
     472           5 :             if (poFID && json_object_get_type(poFID) == json_type_string)
     473           5 :                 m_osFID = json_object_get_string(poFID);
     474             : 
     475             :             json_object *poGeomFields =
     476           5 :                 CPL_json_object_object_get(poMeta, "geomfields");
     477          10 :             if (poGeomFields &&
     478           5 :                 json_object_get_type(poGeomFields) == json_type_object)
     479             :             {
     480          15 :                 for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
     481             :                 {
     482          10 :                     json_object *poObj = CPL_json_object_object_get(
     483             :                         poGeomFields,
     484          10 :                         m_poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef());
     485          15 :                     if (poObj &&
     486           5 :                         json_object_get_type(poObj) == json_type_string)
     487             :                     {
     488             :                         OGRwkbGeometryType eType =
     489           5 :                             OGRFromOGCGeomType(json_object_get_string(poObj));
     490           5 :                         if (eType != wkbUnknown)
     491           5 :                             m_poFeatureDefn->GetGeomFieldDefn(i)->SetType(
     492             :                                 eType);
     493             :                     }
     494             :                 }
     495             :             }
     496             : 
     497             :             json_object *poFields =
     498           5 :                 CPL_json_object_object_get(poMeta, "fields");
     499           5 :             if (poFields && json_object_get_type(poFields) == json_type_object)
     500             :             {
     501          80 :                 for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
     502             :                 {
     503          75 :                     json_object *poObj = CPL_json_object_object_get(
     504             :                         poFields,
     505          75 :                         m_poFeatureDefn->GetFieldDefn(i)->GetNameRef());
     506          95 :                     if (poObj &&
     507          20 :                         json_object_get_type(poObj) == json_type_string)
     508             :                     {
     509         130 :                         for (int j = 0; j <= OFTMaxType; j++)
     510             :                         {
     511         130 :                             if (EQUAL(OGR_GetFieldTypeName((OGRFieldType)j),
     512             :                                       json_object_get_string(poObj)))
     513             :                             {
     514          20 :                                 m_poFeatureDefn->GetFieldDefn(i)->SetType(
     515             :                                     (OGRFieldType)j);
     516          20 :                                 break;
     517             :                             }
     518             :                         }
     519             :                     }
     520             :                 }
     521             :             }
     522             :         }
     523             :     }
     524             : }
     525             : 
     526             : /************************************************************************/
     527             : /*                        CreateFieldFromSchema()                       */
     528             : /************************************************************************/
     529             : 
     530         134 : void OGRElasticLayer::CreateFieldFromSchema(const char *pszName,
     531             :                                             const char *pszPrefix,
     532             :                                             std::vector<CPLString> aosPath,
     533             :                                             json_object *poObj)
     534             : {
     535         134 :     const char *pszType = "";
     536         134 :     json_object *poType = CPL_json_object_object_get(poObj, "type");
     537         134 :     if (poType && json_object_get_type(poType) == json_type_string)
     538             :     {
     539         133 :         pszType = json_object_get_string(poType);
     540             :     }
     541             : 
     542         134 :     CPLString osFieldName;
     543         134 :     if (pszPrefix[0])
     544             :     {
     545           9 :         osFieldName = pszPrefix;
     546           9 :         osFieldName += ".";
     547             :     }
     548         134 :     osFieldName += pszName;
     549             : 
     550         134 :     if (EQUAL(pszType, "geo_point") || EQUAL(pszType, "geo_shape"))
     551             :     {
     552          21 :         if (m_poFeatureDefn->GetGeomFieldIndex(osFieldName) >= 0)
     553           0 :             return;
     554             : 
     555          21 :         aosPath.push_back(pszName);
     556          21 :         AddGeomFieldDefn(osFieldName,
     557          21 :                          EQUAL(pszType, "geo_point") ? wkbPoint : wkbUnknown,
     558          21 :                          aosPath, EQUAL(pszType, "geo_point"));
     559             :     }
     560         113 :     else if (!(aosPath.empty() && m_osMappingName == "FeatureCollection"))
     561             :     {
     562         100 :         if (m_poFeatureDefn->GetFieldIndex(osFieldName) >= 0)
     563           0 :             return;
     564             : 
     565         100 :         OGRFieldType eType = OFTString;
     566         100 :         OGRFieldSubType eSubType = OFSTNone;
     567         100 :         if (EQUAL(pszType, "integer"))
     568          11 :             eType = OFTInteger;
     569          89 :         else if (EQUAL(pszType, "boolean"))
     570             :         {
     571           5 :             eType = OFTInteger;
     572           5 :             eSubType = OFSTBoolean;
     573             :         }
     574          84 :         else if (EQUAL(pszType, "long"))
     575          11 :             eType = OFTInteger64;
     576          73 :         else if (EQUAL(pszType, "float"))
     577           5 :             eType = OFTReal;
     578          68 :         else if (EQUAL(pszType, "double"))
     579          11 :             eType = OFTReal;
     580          57 :         else if (EQUAL(pszType, "date"))
     581             :         {
     582          18 :             eType = OFTDateTime;
     583          18 :             json_object *poFormat = CPL_json_object_object_get(poObj, "format");
     584          18 :             if (poFormat && json_object_get_type(poFormat) == json_type_string)
     585             :             {
     586          12 :                 const char *pszFormat = json_object_get_string(poFormat);
     587          12 :                 if (EQUAL(pszFormat, "HH:mm:ss.SSS") ||
     588           6 :                     EQUAL(pszFormat, "time"))
     589           6 :                     eType = OFTTime;
     590           6 :                 else if (EQUAL(pszFormat, "yyyy/MM/dd") ||
     591           0 :                          EQUAL(pszFormat, "date"))
     592           6 :                     eType = OFTDate;
     593             :             }
     594             :         }
     595          39 :         else if (EQUAL(pszType, "binary"))
     596           5 :             eType = OFTBinary;
     597          34 :         else if (EQUAL(pszType, "string"))  // ES < 5.0
     598             :         {
     599          28 :             json_object *poIndex = CPL_json_object_object_get(poObj, "index");
     600          28 :             if (poIndex && json_object_get_type(poIndex) == json_type_string)
     601             :             {
     602           2 :                 const char *pszIndex = json_object_get_string(poIndex);
     603           2 :                 if (EQUAL(pszIndex, "not_analyzed"))
     604             :                 {
     605           2 :                     m_papszNotAnalyzedFields =
     606           2 :                         CSLAddString(m_papszNotAnalyzedFields, osFieldName);
     607             :                 }
     608             :             }
     609             :         }
     610           6 :         else if (EQUAL(pszType, "keyword"))  // ES >= 5.0
     611             :         {
     612           1 :             m_papszNotAnalyzedFields =
     613           1 :                 CSLAddString(m_papszNotAnalyzedFields, osFieldName);
     614             :         }
     615             : 
     616         100 :         aosPath.push_back(pszName);
     617         100 :         AddFieldDefn(osFieldName, eType, aosPath, eSubType);
     618             : 
     619         100 :         json_object *poFields = CPL_json_object_object_get(poObj, "fields");
     620         100 :         if (poFields && json_object_get_type(poFields) == json_type_object)
     621             :         {
     622           1 :             json_object *poRaw = CPL_json_object_object_get(poFields, "raw");
     623           1 :             if (poRaw && json_object_get_type(poRaw) == json_type_object)
     624             :             {
     625             :                 json_object *poRawType =
     626           1 :                     CPL_json_object_object_get(poRaw, "type");
     627           2 :                 if (poRawType &&
     628           1 :                     json_object_get_type(poRawType) == json_type_string)
     629             :                 {
     630           1 :                     const char *pszRawType = json_object_get_string(poRawType);
     631           1 :                     if (EQUAL(pszRawType, "keyword"))  // ES >= 5.0
     632             :                     {
     633           1 :                         m_papszFieldsWithRawValue = CSLAddString(
     634             :                             m_papszFieldsWithRawValue, osFieldName);
     635             :                     }
     636           0 :                     else if (EQUAL(pszRawType, "string"))  // ES < 5.0
     637             :                     {
     638             :                         json_object *poRawIndex =
     639           0 :                             CPL_json_object_object_get(poRaw, "index");
     640           0 :                         if (poRawIndex && json_object_get_type(poRawIndex) ==
     641             :                                               json_type_string)
     642             :                         {
     643             :                             const char *pszRawIndex =
     644           0 :                                 json_object_get_string(poRawIndex);
     645           0 :                             if (EQUAL(pszRawIndex, "not_analyzed"))
     646             :                             {
     647           0 :                                 m_papszFieldsWithRawValue = CSLAddString(
     648             :                                     m_papszFieldsWithRawValue, osFieldName);
     649             :                             }
     650             :                         }
     651             :                     }
     652             :                 }
     653             :             }
     654             :         }
     655             :     }
     656             : }
     657             : 
     658             : /************************************************************************/
     659             : /*                        FinalizeFeatureDefn()                         */
     660             : /************************************************************************/
     661             : 
     662         649 : void OGRElasticLayer::FinalizeFeatureDefn(bool bReadFeatures)
     663             : {
     664         649 :     if (m_bFeatureDefnFinalized)
     665         612 :         return;
     666             : 
     667          37 :     m_bFeatureDefnFinalized = true;
     668             : 
     669          37 :     int nFeatureCountToEstablishFeatureDefn =
     670          37 :         m_poDS->m_nFeatureCountToEstablishFeatureDefn;
     671          37 :     if (!m_osESSearch.empty() && nFeatureCountToEstablishFeatureDefn <= 0)
     672           0 :         nFeatureCountToEstablishFeatureDefn = 1;
     673          74 :     std::set<std::pair<CPLString, CPLString>> oVisited;
     674             : 
     675          37 :     if (bReadFeatures && nFeatureCountToEstablishFeatureDefn != 0)
     676             :     {
     677             :         // CPLDebug("ES", "Try to get %d features to establish feature
     678             :         // definition",
     679             :         //          FeatureCountToEstablishFeatureDefn);
     680          14 :         bool bFirst = true;
     681          14 :         int nAlreadyQueried = 0;
     682             :         while (true)
     683             :         {
     684          26 :             CPLString osRequest;
     685          26 :             CPLString osPostData;
     686          26 :             if (bFirst)
     687             :             {
     688          14 :                 bFirst = false;
     689          14 :                 if (!m_osESSearch.empty())
     690             :                 {
     691             :                     osRequest =
     692             :                         CPLSPrintf("%s/_search?scroll=1m&size=%d",
     693           3 :                                    m_poDS->GetURL(), m_poDS->m_nBatchSize);
     694           3 :                     osPostData = m_osESSearch;
     695             :                 }
     696             :                 else
     697             :                 {
     698          11 :                     osRequest = BuildMappingURL(false);
     699             :                     osRequest += CPLSPrintf("/_search?scroll=1m&size=%d",
     700          11 :                                             m_poDS->m_nBatchSize);
     701             :                 }
     702             :             }
     703             :             else
     704             :             {
     705          12 :                 if (m_osScrollID.empty())
     706           7 :                     break;
     707             :                 osRequest =
     708             :                     CPLSPrintf("%s/_search/scroll?scroll=1m&scroll_id=%s",
     709           5 :                                m_poDS->GetURL(), m_osScrollID.c_str());
     710             :             }
     711             : 
     712          19 :             if (m_bAddPretty)
     713           0 :                 osRequest += "&pretty";
     714          19 :             json_object *poResponse = m_poDS->RunRequest(osRequest, osPostData);
     715          19 :             if (poResponse == nullptr)
     716             :             {
     717           1 :                 break;
     718             :             }
     719             :             json_object *poScrollID =
     720          18 :                 CPL_json_object_object_get(poResponse, "_scroll_id");
     721          18 :             if (poScrollID)
     722             :             {
     723           5 :                 const char *pszScrollID = json_object_get_string(poScrollID);
     724           5 :                 if (pszScrollID)
     725           5 :                     m_osScrollID = pszScrollID;
     726             :             }
     727             : 
     728             :             json_object *poHits =
     729          18 :                 json_ex_get_object_by_path(poResponse, "hits.hits");
     730          30 :             if (poHits == nullptr ||
     731          12 :                 json_object_get_type(poHits) != json_type_array)
     732             :             {
     733           6 :                 json_object_put(poResponse);
     734           6 :                 break;
     735             :             }
     736          12 :             const auto nHits = json_object_array_length(poHits);
     737          12 :             if (nHits == 0)
     738             :             {
     739           0 :                 m_osScrollID = "";
     740           0 :                 json_object_put(poResponse);
     741           0 :                 break;
     742             :             }
     743          37 :             for (auto i = decltype(nHits){0}; i < nHits; i++)
     744             :             {
     745          25 :                 json_object *poHit = json_object_array_get_idx(poHits, i);
     746          50 :                 if (poHit == nullptr ||
     747          25 :                     json_object_get_type(poHit) != json_type_object)
     748             :                 {
     749           0 :                     continue;
     750             :                 }
     751             :                 json_object *poSource =
     752          25 :                     CPL_json_object_object_get(poHit, "_source");
     753          50 :                 if (poSource == nullptr ||
     754          25 :                     json_object_get_type(poSource) != json_type_object)
     755             :                 {
     756           0 :                     continue;
     757             :                 }
     758             : 
     759          25 :                 if (!m_osESSearch.empty())
     760             :                 {
     761             :                     json_object *poIndex =
     762           3 :                         CPL_json_object_object_get(poHit, "_index");
     763           6 :                     if (poIndex == nullptr ||
     764           3 :                         json_object_get_type(poIndex) != json_type_string)
     765           0 :                         break;
     766           3 :                     if (m_poDS->m_nMajorVersion < 7)
     767             :                     {
     768             :                         json_object *poType =
     769           2 :                             CPL_json_object_object_get(poHit, "_type");
     770           4 :                         if (poType == nullptr ||
     771           2 :                             json_object_get_type(poType) != json_type_string)
     772           0 :                             break;
     773           2 :                         m_osMappingName = json_object_get_string(poType);
     774             :                     }
     775           6 :                     CPLString osIndex(json_object_get_string(poIndex));
     776             : 
     777           3 :                     if (oVisited.find(std::pair<CPLString, CPLString>(
     778           6 :                             osIndex, m_osMappingName)) == oVisited.end())
     779             :                     {
     780           3 :                         oVisited.insert(std::pair<CPLString, CPLString>(
     781           3 :                             osIndex, m_osMappingName));
     782             : 
     783             :                         CPLString osURL =
     784           3 :                             CPLSPrintf("%s/%s/_mapping", m_poDS->GetURL(),
     785           6 :                                        osIndex.c_str());
     786           3 :                         if (m_poDS->m_nMajorVersion < 7)
     787           2 :                             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
     788           3 :                         osURL += "?pretty";
     789             : 
     790           3 :                         json_object *poMappingRes = m_poDS->RunRequest(osURL);
     791           3 :                         if (poMappingRes)
     792             :                         {
     793             :                             json_object *poLayerObj =
     794           3 :                                 CPL_json_object_object_get(poMappingRes,
     795             :                                                            osIndex);
     796           3 :                             json_object *poMappings = nullptr;
     797           6 :                             if (poLayerObj &&
     798           3 :                                 json_object_get_type(poLayerObj) ==
     799             :                                     json_type_object)
     800           3 :                                 poMappings = CPL_json_object_object_get(
     801             :                                     poLayerObj, "mappings");
     802           6 :                             if (poMappings &&
     803           3 :                                 json_object_get_type(poMappings) ==
     804             :                                     json_type_object)
     805             :                             {
     806             :                                 json_object *poMapping =
     807           3 :                                     m_poDS->m_nMajorVersion < 7
     808           3 :                                         ? CPL_json_object_object_get(
     809             :                                               poMappings, m_osMappingName)
     810           3 :                                         : poMappings;
     811           3 :                                 if (poMapping)
     812             :                                 {
     813           3 :                                     InitFeatureDefnFromMapping(
     814             :                                         poMapping, "",
     815           6 :                                         std::vector<CPLString>());
     816             :                                 }
     817             :                             }
     818           3 :                             json_object_put(poMappingRes);
     819             :                         }
     820             :                     }
     821             :                 }
     822             : 
     823             :                 json_object_iter it;
     824          25 :                 it.key = nullptr;
     825          25 :                 it.val = nullptr;
     826          25 :                 it.entry = nullptr;
     827          76 :                 json_object_object_foreachC(poSource, it)
     828             :                 {
     829          51 :                     if (!m_osFID.empty())
     830             :                     {
     831           5 :                         if (EQUAL(it.key, m_osFID))
     832           1 :                             continue;
     833             :                     }
     834          46 :                     else if (EQUAL(it.key, m_poDS->GetFID()))
     835             :                     {
     836           0 :                         m_osFID = it.key;
     837           0 :                         continue;
     838             :                     }
     839             : 
     840          50 :                     if (m_osMappingName == "FeatureCollection")
     841             :                     {
     842          17 :                         if (strcmp(it.key, "properties") == 0 &&
     843           5 :                             json_object_get_type(it.val) == json_type_object)
     844             :                         {
     845             :                             json_object_iter it2;
     846           5 :                             it2.key = nullptr;
     847           5 :                             it2.val = nullptr;
     848           5 :                             it2.entry = nullptr;
     849          23 :                             json_object_object_foreachC(it.val, it2)
     850             :                             {
     851          36 :                                 std::vector<CPLString> aosPath;
     852          18 :                                 aosPath.push_back("properties");
     853          18 :                                 AddOrUpdateField(it2.key, it2.key, it2.val, '.',
     854             :                                                  aosPath);
     855             :                             }
     856             :                         }
     857             :                     }
     858             :                     else
     859             :                     {
     860          76 :                         std::vector<CPLString> aosPath;
     861          38 :                         AddOrUpdateField(it.key, it.key, it.val, '.', aosPath);
     862             :                     }
     863             :                 }
     864             : 
     865          25 :                 nAlreadyQueried++;
     866          25 :                 if (nFeatureCountToEstablishFeatureDefn > 0 &&
     867             :                     nAlreadyQueried >= nFeatureCountToEstablishFeatureDefn)
     868             :                 {
     869           0 :                     break;
     870             :                 }
     871             :             }
     872             : 
     873          12 :             json_object_put(poResponse);
     874             : 
     875          12 :             if (nFeatureCountToEstablishFeatureDefn > 0 &&
     876             :                 nAlreadyQueried >= nFeatureCountToEstablishFeatureDefn)
     877             :             {
     878           0 :                 break;
     879             :             }
     880          12 :         }
     881             : 
     882          14 :         ResetReading();
     883             :     }
     884             : 
     885          37 :     if (m_poDS->m_bJSonField)
     886             :     {
     887           1 :         AddFieldDefn("_json", OFTString, std::vector<CPLString>());
     888             :     }
     889             : }
     890             : 
     891             : /************************************************************************/
     892             : /*                         BuildPathFromArray()                         */
     893             : /************************************************************************/
     894             : 
     895             : CPLString
     896         215 : OGRElasticLayer::BuildPathFromArray(const std::vector<CPLString> &aosPath)
     897             : {
     898         215 :     CPLString osPath(aosPath[0]);
     899         381 :     for (size_t i = 1; i < aosPath.size(); i++)
     900             :     {
     901         166 :         osPath += ".";
     902         166 :         osPath += aosPath[i];
     903             :     }
     904         215 :     return osPath;
     905             : }
     906             : 
     907             : /************************************************************************/
     908             : /*                         GetOGRGeomTypeFromES()                       */
     909             : /************************************************************************/
     910             : 
     911          15 : static OGRwkbGeometryType GetOGRGeomTypeFromES(const char *pszType)
     912             : {
     913          15 :     if (EQUAL(pszType, "envelope"))
     914           1 :         return wkbPolygon;
     915          14 :     if (EQUAL(pszType, "circle"))
     916           3 :         return wkbPolygon;
     917          11 :     return OGRFromOGCGeomType(pszType);
     918             : }
     919             : 
     920             : /************************************************************************/
     921             : /*                         AddOrUpdateField()                           */
     922             : /************************************************************************/
     923             : 
     924          68 : void OGRElasticLayer::AddOrUpdateField(const char *pszAttrName,
     925             :                                        const char *pszKey, json_object *poObj,
     926             :                                        char chNestedAttributeSeparator,
     927             :                                        std::vector<CPLString> &aosPath)
     928             : {
     929          68 :     json_type eJSONType = json_object_get_type(poObj);
     930          68 :     if (eJSONType == json_type_null)
     931          32 :         return;
     932             : 
     933          67 :     if (eJSONType == json_type_object)
     934             :     {
     935          21 :         json_object *poType = CPL_json_object_object_get(poObj, "type");
     936             :         OGRwkbGeometryType eGeomType;
     937          15 :         if (poType && json_object_get_type(poType) == json_type_string &&
     938          15 :             (eGeomType = GetOGRGeomTypeFromES(
     939          36 :                  json_object_get_string(poType))) != wkbUnknown &&
     940          15 :             CPL_json_object_object_get(
     941             :                 poObj, (eGeomType == wkbGeometryCollection) ? "geometries"
     942             :                                                             : "coordinates"))
     943             :         {
     944          15 :             int nIndex = m_poFeatureDefn->GetGeomFieldIndex(pszAttrName);
     945          15 :             if (nIndex < 0)
     946             :             {
     947           2 :                 aosPath.push_back(pszKey);
     948           2 :                 AddGeomFieldDefn(pszAttrName, eGeomType, aosPath, FALSE);
     949             :             }
     950             :             else
     951             :             {
     952             :                 OGRGeomFieldDefn *poFDefn =
     953          13 :                     m_poFeatureDefn->GetGeomFieldDefn(nIndex);
     954          13 :                 if (poFDefn->GetType() != eGeomType)
     955           9 :                     poFDefn->SetType(wkbUnknown);
     956             :             }
     957             :         }
     958           6 :         else if (m_poDS->m_bFlattenNestedAttributes)
     959             :         {
     960           6 :             if (m_poFeatureDefn->GetGeomFieldIndex(pszAttrName) >= 0)
     961           2 :                 return;
     962           4 :             aosPath.push_back(pszKey);
     963             : 
     964             :             json_object_iter it;
     965           4 :             it.key = nullptr;
     966           4 :             it.val = nullptr;
     967           4 :             it.entry = nullptr;
     968          16 :             json_object_object_foreachC(poObj, it)
     969             :             {
     970             :                 char szSeparator[2];
     971          12 :                 szSeparator[0] = chNestedAttributeSeparator;
     972          12 :                 szSeparator[1] = 0;
     973             :                 CPLString osAttrName(
     974          24 :                     CPLSPrintf("%s%s%s", pszAttrName, szSeparator, it.key));
     975             : 
     976          24 :                 std::vector<CPLString> aosNewPaths(aosPath);
     977          12 :                 AddOrUpdateField(osAttrName, it.key, it.val,
     978             :                                  chNestedAttributeSeparator, aosNewPaths);
     979             :             }
     980           4 :             return;
     981             :         }
     982             :     }
     983             :     /*else if( eJSONType == json_type_array )
     984             :     {
     985             :         if( m_poFeatureDefn->GetGeomFieldIndex(pszAttrName) >= 0 )
     986             :             return;
     987             :     }*/
     988             : 
     989          61 :     if (m_poFeatureDefn->GetGeomFieldIndex(pszAttrName) >= 0)
     990          25 :         return;
     991             : 
     992             :     OGRFieldSubType eNewSubType;
     993          36 :     OGRFieldType eNewType = GeoJSONPropertyToFieldType(poObj, eNewSubType);
     994             : 
     995          36 :     int nIndex = m_poFeatureDefn->GetFieldIndex(pszAttrName);
     996          36 :     OGRFieldDefn *poFDefn = nullptr;
     997          36 :     if (nIndex >= 0)
     998          32 :         poFDefn = m_poFeatureDefn->GetFieldDefn(nIndex);
     999          68 :     if ((poFDefn == nullptr && eNewType == OFTString) ||
    1000          32 :         (poFDefn != nullptr &&
    1001          93 :          (poFDefn->GetType() == OFTDate || poFDefn->GetType() == OFTDateTime ||
    1002          30 :           poFDefn->GetType() == OFTTime)))
    1003             :     {
    1004           5 :         int nYear = 0;
    1005           5 :         int nMonth = 0;
    1006           5 :         int nDay = 0;
    1007           5 :         int nHour = 0;
    1008           5 :         int nMinute = 0;
    1009           5 :         float fSecond = 0.0f;
    1010           5 :         if (sscanf(json_object_get_string(poObj), "%04d/%02d/%02d %02d:%02d",
    1011           9 :                    &nYear, &nMonth, &nDay, &nHour, &nMinute) == 5 ||
    1012           4 :             sscanf(json_object_get_string(poObj), "%04d-%02d-%02dT%02d:%02d",
    1013             :                    &nYear, &nMonth, &nDay, &nHour, &nMinute) == 5)
    1014             :         {
    1015           1 :             eNewType = OFTDateTime;
    1016             :         }
    1017           4 :         else if (sscanf(json_object_get_string(poObj), "%04d/%02d/%02d", &nYear,
    1018           7 :                         &nMonth, &nDay) == 3 ||
    1019           3 :                  sscanf(json_object_get_string(poObj), "%04d-%02d-%02d", &nYear,
    1020             :                         &nMonth, &nDay) == 3)
    1021             :         {
    1022           1 :             eNewType = OFTDate;
    1023             :         }
    1024           3 :         else if (sscanf(json_object_get_string(poObj), "%02d:%02d:%f", &nHour,
    1025           3 :                         &nMinute, &fSecond) == 3)
    1026             :         {
    1027           1 :             eNewType = OFTTime;
    1028             :         }
    1029             :     }
    1030             : 
    1031          36 :     if (poFDefn == nullptr)
    1032             :     {
    1033           4 :         aosPath.push_back(pszKey);
    1034           4 :         AddFieldDefn(pszAttrName, eNewType, aosPath, eNewSubType);
    1035             :     }
    1036             :     else
    1037             :     {
    1038          32 :         OGRUpdateFieldType(poFDefn, eNewType, eNewSubType);
    1039             :     }
    1040             : }
    1041             : 
    1042             : /************************************************************************/
    1043             : /*                              SyncToDisk()                            */
    1044             : /************************************************************************/
    1045             : 
    1046          59 : OGRErr OGRElasticLayer::SyncToDisk()
    1047             : {
    1048          59 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    1049           0 :         return OGRERR_FAILURE;
    1050             : 
    1051          59 :     if (!PushIndex())
    1052           1 :         return OGRERR_FAILURE;
    1053             : 
    1054          58 :     return OGRERR_NONE;
    1055             : }
    1056             : 
    1057             : /************************************************************************/
    1058             : /*                            GetLayerDefn()                            */
    1059             : /************************************************************************/
    1060             : 
    1061         457 : OGRFeatureDefn *OGRElasticLayer::GetLayerDefn()
    1062             : {
    1063             : 
    1064         457 :     FinalizeFeatureDefn();
    1065             : 
    1066         457 :     return m_poFeatureDefn;
    1067             : }
    1068             : 
    1069             : /************************************************************************/
    1070             : /*                            GetFIDColumn()                            */
    1071             : /************************************************************************/
    1072             : 
    1073          35 : const char *OGRElasticLayer::GetFIDColumn()
    1074             : {
    1075          35 :     GetLayerDefn();
    1076          35 :     return m_osFID.c_str();
    1077             : }
    1078             : 
    1079             : /************************************************************************/
    1080             : /*                            ResetReading()                            */
    1081             : /************************************************************************/
    1082             : 
    1083         208 : void OGRElasticLayer::ResetReading()
    1084             : {
    1085         208 :     if (!m_osScrollID.empty())
    1086             :     {
    1087             :         char **papszOptions =
    1088          41 :             CSLAddNameValue(nullptr, "CUSTOMREQUEST", "DELETE");
    1089          41 :         CPLHTTPResult *psResult = m_poDS->HTTPFetch(
    1090          82 :             (m_poDS->GetURL() + CPLString("/_search/scroll?scroll_id=") +
    1091             :              m_osScrollID)
    1092             :                 .c_str(),
    1093             :             papszOptions);
    1094          41 :         CSLDestroy(papszOptions);
    1095          41 :         CPLHTTPDestroyResult(psResult);
    1096             : 
    1097          41 :         m_osScrollID = "";
    1098             :     }
    1099         282 :     for (int i = 0; i < (int)m_apoCachedFeatures.size(); i++)
    1100          74 :         delete m_apoCachedFeatures[i];
    1101         208 :     m_apoCachedFeatures.resize(0);
    1102         208 :     m_iCurID = 0;
    1103         208 :     m_iCurFeatureInPage = 0;
    1104         208 :     m_bEOF = false;
    1105             : 
    1106         208 :     m_nReadFeaturesSinceResetReading = 0;
    1107         208 :     m_dfEndTimeStamp = 0;
    1108         416 :     const double dfTimeout = m_bUseSingleQueryParams
    1109         208 :                                  ? m_dfSingleQueryTimeout
    1110             :                                  : m_dfFeatureIterationTimeout;
    1111         208 :     if (dfTimeout > 0)
    1112          10 :         m_dfEndTimeStamp = GetTimestamp() + dfTimeout;
    1113         208 : }
    1114             : 
    1115             : /************************************************************************/
    1116             : /*                           GetNextFeature()                           */
    1117             : /************************************************************************/
    1118             : 
    1119          80 : OGRFeature *OGRElasticLayer::GetNextFeature()
    1120             : 
    1121             : {
    1122          80 :     FinalizeFeatureDefn();
    1123             : 
    1124             :     while (true)
    1125             :     {
    1126          83 :         OGRFeature *poFeature = GetNextRawFeature();
    1127          83 :         if (poFeature == nullptr)
    1128          15 :             return nullptr;
    1129             : 
    1130         149 :         if ((m_poFilterGeom == nullptr ||
    1131         133 :              FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
    1132          65 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
    1133          65 :             return poFeature;
    1134             : 
    1135           3 :         delete poFeature;
    1136           3 :     }
    1137             : }
    1138             : 
    1139             : /************************************************************************/
    1140             : /*                           BuildSort()                                */
    1141             : /************************************************************************/
    1142             : 
    1143           3 : json_object *OGRElasticLayer::BuildSort()
    1144             : {
    1145           3 :     json_object *poRet = json_object_new_array();
    1146           8 :     for (size_t i = 0; i < m_aoSortColumns.size(); ++i)
    1147             :     {
    1148             :         const int nIdx =
    1149           5 :             m_poFeatureDefn->GetFieldIndex(m_aoSortColumns[i].osColumn);
    1150             :         CPLString osFieldName(
    1151          10 :             nIdx == 0 ? "_uid" : BuildPathFromArray(m_aaosFieldPaths[nIdx]));
    1152           5 :         if (CSLFindString(m_papszFieldsWithRawValue,
    1153          10 :                           m_aoSortColumns[i].osColumn) >= 0)
    1154             :         {
    1155           1 :             osFieldName += ".raw";
    1156             :         }
    1157           5 :         json_object *poSortCol = json_object_new_object();
    1158           5 :         json_object *poSortProp = json_object_new_object();
    1159           5 :         json_object_array_add(poRet, poSortCol);
    1160           5 :         json_object_object_add(
    1161             :             poSortProp, "order",
    1162           5 :             json_object_new_string(m_aoSortColumns[i].bAsc ? "asc" : "desc"));
    1163           5 :         json_object_object_add(poSortCol, osFieldName, poSortProp);
    1164             :     }
    1165           3 :     return poRet;
    1166             : }
    1167             : 
    1168             : /************************************************************************/
    1169             : /*                           BuildQuery()                               */
    1170             : /************************************************************************/
    1171             : 
    1172          28 : CPLString OGRElasticLayer::BuildQuery(bool bCountOnly)
    1173             : {
    1174          28 :     CPLString osRet = "{ ";
    1175          31 :     if (bCountOnly &&
    1176           3 :         (m_poDS->m_nMajorVersion < 5 || !m_osSingleQueryTimeout.empty()))
    1177             :     {
    1178           2 :         osRet += "\"size\": 0, ";
    1179             :     }
    1180          28 :     if (m_poSpatialFilter && m_poJSONFilter)
    1181             :     {
    1182           1 :         osRet += CPLSPrintf("\"query\": { \"constant_score\" : { \"filter\": "
    1183             :                             "{ \"bool\" : { \"must\" : [%s, %s] } } } }",
    1184             :                             json_object_to_json_string(m_poSpatialFilter),
    1185           1 :                             json_object_to_json_string(m_poJSONFilter));
    1186             :     }
    1187             :     else
    1188             :     {
    1189             :         osRet += CPLSPrintf(
    1190             :             "\"query\": { \"constant_score\" : { \"filter\": %s } }",
    1191          27 :             json_object_to_json_string(m_poSpatialFilter ? m_poSpatialFilter
    1192          27 :                                                          : m_poJSONFilter));
    1193             :     }
    1194          28 :     if (!bCountOnly && !m_aoSortColumns.empty())
    1195             :     {
    1196           1 :         json_object *poSort = BuildSort();
    1197             :         osRet +=
    1198           1 :             CPLSPrintf(", \"sort\" : %s", json_object_to_json_string(poSort));
    1199           1 :         json_object_put(poSort);
    1200             :     }
    1201          28 :     osRet += " }";
    1202          28 :     return osRet;
    1203             : }
    1204             : 
    1205             : /************************************************************************/
    1206             : /*                         GetNextRawFeature()                          */
    1207             : /************************************************************************/
    1208             : 
    1209          83 : OGRFeature *OGRElasticLayer::GetNextRawFeature()
    1210             : {
    1211          83 :     json_object *poResponse = nullptr;
    1212             : 
    1213          83 :     if (m_dfEndTimeStamp > 0 && GetTimestamp() >= m_dfEndTimeStamp)
    1214             :     {
    1215           1 :         CPLDebug("ES", "Terminating request due to timeout");
    1216           1 :         return nullptr;
    1217             :     }
    1218         164 :     const auto nTerminateAfter = m_bUseSingleQueryParams
    1219          82 :                                      ? m_nSingleQueryTerminateAfter
    1220             :                                      : m_nFeatureIterationTerminateAfter;
    1221          82 :     if (nTerminateAfter > 0 &&
    1222           5 :         m_nReadFeaturesSinceResetReading >= nTerminateAfter)
    1223             :     {
    1224           1 :         CPLDebug("ES", "Terminating request due to terminate_after reached");
    1225           1 :         return nullptr;
    1226             :     }
    1227             : 
    1228          81 :     if (m_bEOF)
    1229           1 :         return nullptr;
    1230             : 
    1231          80 :     if (m_iCurFeatureInPage < (int)m_apoCachedFeatures.size())
    1232             :     {
    1233          15 :         OGRFeature *poRet = m_apoCachedFeatures[m_iCurFeatureInPage];
    1234          15 :         m_apoCachedFeatures[m_iCurFeatureInPage] = nullptr;
    1235          15 :         m_iCurFeatureInPage++;
    1236          15 :         m_nReadFeaturesSinceResetReading++;
    1237          15 :         return poRet;
    1238             :     }
    1239             : 
    1240          74 :     for (int i = 0; i < (int)m_apoCachedFeatures.size(); i++)
    1241           9 :         delete m_apoCachedFeatures[i];
    1242          65 :     m_apoCachedFeatures.resize(0);
    1243          65 :     m_iCurFeatureInPage = 0;
    1244             : 
    1245         130 :     CPLString osRequest, osPostData;
    1246          65 :     if (m_nReadFeaturesSinceResetReading == 0)
    1247             :     {
    1248          60 :         if (!m_osESSearch.empty())
    1249             :         {
    1250             :             osRequest = CPLSPrintf("%s/_search?scroll=1m&size=%d",
    1251           3 :                                    m_poDS->GetURL(), m_poDS->m_nBatchSize);
    1252           3 :             osPostData = m_osESSearch;
    1253             :         }
    1254         110 :         else if ((m_poSpatialFilter && m_osJSONFilter.empty()) ||
    1255          53 :                  m_poJSONFilter)
    1256             :         {
    1257          25 :             osPostData = BuildQuery(false);
    1258          25 :             osRequest = BuildMappingURL(false);
    1259             :             osRequest +=
    1260          25 :                 CPLSPrintf("/_search?scroll=1m&size=%d", m_poDS->m_nBatchSize);
    1261             :         }
    1262          32 :         else if (!m_aoSortColumns.empty() && m_osJSONFilter.empty())
    1263             :         {
    1264           2 :             osRequest = BuildMappingURL(false);
    1265             :             osRequest +=
    1266           2 :                 CPLSPrintf("/_search?scroll=1m&size=%d", m_poDS->m_nBatchSize);
    1267           2 :             json_object *poSort = BuildSort();
    1268             :             osPostData = CPLSPrintf("{ \"sort\": %s }",
    1269           2 :                                     json_object_to_json_string(poSort));
    1270           2 :             json_object_put(poSort);
    1271             :         }
    1272             :         else
    1273             :         {
    1274          30 :             osRequest = BuildMappingURL(false);
    1275             :             osRequest +=
    1276          30 :                 CPLSPrintf("/_search?scroll=1m&size=%d", m_poDS->m_nBatchSize);
    1277          30 :             osPostData = m_osJSONFilter;
    1278             :         }
    1279             :     }
    1280             :     else
    1281             :     {
    1282           5 :         if (m_osScrollID.empty())
    1283             :         {
    1284           1 :             m_bEOF = true;
    1285           1 :             return nullptr;
    1286             :         }
    1287             :         osRequest = CPLSPrintf("%s/_search/scroll?scroll=1m&scroll_id=%s",
    1288           4 :                                m_poDS->GetURL(), m_osScrollID.c_str());
    1289             :     }
    1290             : 
    1291          64 :     if (m_bAddPretty)
    1292           0 :         osRequest += "&pretty";
    1293          64 :     poResponse = m_poDS->RunRequest(osRequest, osPostData);
    1294          64 :     if (poResponse == nullptr)
    1295             :     {
    1296           4 :         m_bEOF = true;
    1297           4 :         return nullptr;
    1298             :     }
    1299          60 :     m_osScrollID.clear();
    1300             :     json_object *poScrollID =
    1301          60 :         CPL_json_object_object_get(poResponse, "_scroll_id");
    1302          60 :     if (poScrollID)
    1303             :     {
    1304          40 :         const char *pszScrollID = json_object_get_string(poScrollID);
    1305          40 :         if (pszScrollID)
    1306          40 :             m_osScrollID = pszScrollID;
    1307             :     }
    1308             : 
    1309          60 :     json_object *poHits = CPL_json_object_object_get(poResponse, "hits");
    1310          60 :     if (poHits == nullptr || json_object_get_type(poHits) != json_type_object)
    1311             :     {
    1312           2 :         m_bEOF = true;
    1313           2 :         json_object_put(poResponse);
    1314           2 :         return nullptr;
    1315             :     }
    1316          58 :     poHits = CPL_json_object_object_get(poHits, "hits");
    1317          58 :     if (poHits == nullptr || json_object_get_type(poHits) != json_type_array)
    1318             :     {
    1319           1 :         m_bEOF = true;
    1320           1 :         json_object_put(poResponse);
    1321           1 :         return nullptr;
    1322             :     }
    1323          57 :     const auto nHits = json_object_array_length(poHits);
    1324          57 :     if (nHits == 0)
    1325             :     {
    1326           3 :         m_osScrollID = "";
    1327           3 :         m_bEOF = true;
    1328           3 :         json_object_put(poResponse);
    1329           3 :         return nullptr;
    1330             :     }
    1331         140 :     for (auto i = decltype(nHits){0}; i < nHits; i++)
    1332             :     {
    1333          86 :         json_object *poHit = json_object_array_get_idx(poHits, i);
    1334          86 :         if (poHit == nullptr || json_object_get_type(poHit) != json_type_object)
    1335             :         {
    1336           3 :             continue;
    1337             :         }
    1338          85 :         json_object *poSource = CPL_json_object_object_get(poHit, "_source");
    1339         168 :         if (poSource == nullptr ||
    1340          83 :             json_object_get_type(poSource) != json_type_object)
    1341             :         {
    1342           2 :             continue;
    1343             :         }
    1344             : 
    1345          83 :         const char *pszId = nullptr;
    1346          83 :         json_object *poId = CPL_json_object_object_get(poHit, "_id");
    1347          83 :         if (poId != nullptr && json_object_get_type(poId) == json_type_string)
    1348          43 :             pszId = json_object_get_string(poId);
    1349             : 
    1350          83 :         OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
    1351          83 :         if (pszId)
    1352          43 :             poFeature->SetField("_id", pszId);
    1353             : 
    1354          83 :         if (m_bAddSourceIndexName)
    1355             :         {
    1356           2 :             json_object *poIndex = CPL_json_object_object_get(poHit, "_index");
    1357           4 :             if (poId != nullptr &&
    1358           2 :                 json_object_get_type(poId) == json_type_string)
    1359           2 :                 poFeature->SetField("_index", json_object_get_string(poIndex));
    1360             :         }
    1361             : 
    1362          83 :         if (!m_osESSearch.empty())
    1363             :         {
    1364           3 :             json_object *poIndex = CPL_json_object_object_get(poHit, "_index");
    1365           6 :             if (poIndex != nullptr &&
    1366           3 :                 json_object_get_type(poIndex) == json_type_string)
    1367           3 :                 poFeature->SetField("_index", json_object_get_string(poIndex));
    1368             : 
    1369           3 :             json_object *poType = CPL_json_object_object_get(poHit, "_type");
    1370           5 :             if (poType != nullptr &&
    1371           2 :                 json_object_get_type(poType) == json_type_string)
    1372           2 :                 poFeature->SetField("_type", json_object_get_string(poType));
    1373             :         }
    1374             : 
    1375          83 :         if (m_poDS->m_bJSonField)
    1376           4 :             poFeature->SetField("_json", json_object_to_json_string(poSource));
    1377             : 
    1378          83 :         BuildFeature(poFeature, poSource, CPLString());
    1379          83 :         if (poFeature->GetFID() < 0)
    1380          76 :             poFeature->SetFID(++m_iCurID);
    1381          83 :         m_apoCachedFeatures.push_back(poFeature);
    1382             :     }
    1383             : 
    1384          54 :     json_object_put(poResponse);
    1385          54 :     if (!m_apoCachedFeatures.empty())
    1386             :     {
    1387          53 :         OGRFeature *poRet = m_apoCachedFeatures[0];
    1388          53 :         m_apoCachedFeatures[0] = nullptr;
    1389          53 :         m_iCurFeatureInPage++;
    1390          53 :         m_nReadFeaturesSinceResetReading++;
    1391          53 :         return poRet;
    1392             :     }
    1393           1 :     return nullptr;
    1394             : }
    1395             : 
    1396             : /************************************************************************/
    1397             : /*                      decode_geohash_bbox()                           */
    1398             : /************************************************************************/
    1399             : 
    1400             : /* Derived from routine from
    1401             :  * https://github.com/davetroy/geohash/blob/master/ext/geohash_native.c */
    1402             : /* (c) 2008-2010 David Troy, davetroy@gmail.com, (The MIT License) */
    1403             : 
    1404             : static const char BASE32[] = "0123456789bcdefghjkmnpqrstuvwxyz";
    1405             : 
    1406           4 : static void decode_geohash_bbox(const char *geohash, double lat[2],
    1407             :                                 double lon[2])
    1408             : {
    1409             :     int i;
    1410             :     int j;
    1411             :     int hashlen;
    1412             :     char c;
    1413             :     char cd;
    1414             :     char mask;
    1415           4 :     char is_even = 1;
    1416             :     static const char bits[] = {16, 8, 4, 2, 1};
    1417           4 :     lat[0] = -90.0;
    1418           4 :     lat[1] = 90.0;
    1419           4 :     lon[0] = -180.0;
    1420           4 :     lon[1] = 180.0;
    1421           4 :     hashlen = static_cast<int>(strlen(geohash));
    1422          84 :     for (i = 0; i < hashlen; i++)
    1423             :     {
    1424          80 :         c = static_cast<char>(
    1425          80 :             CPLTolower(static_cast<unsigned char>(geohash[i])));
    1426          80 :         cd = static_cast<char>(strchr(BASE32, c) - BASE32);
    1427         480 :         for (j = 0; j < 5; j++)
    1428             :         {
    1429         400 :             mask = bits[j];
    1430         400 :             if (is_even)
    1431             :             {
    1432         200 :                 lon[!(cd & mask)] = (lon[0] + lon[1]) / 2;
    1433             :             }
    1434             :             else
    1435             :             {
    1436         200 :                 lat[!(cd & mask)] = (lat[0] + lat[1]) / 2;
    1437             :             }
    1438         400 :             is_even = !is_even;
    1439             :         }
    1440             :     }
    1441           4 : }
    1442             : 
    1443             : /************************************************************************/
    1444             : /*                            BuildFeature()                            */
    1445             : /************************************************************************/
    1446             : 
    1447         155 : void OGRElasticLayer::BuildFeature(OGRFeature *poFeature, json_object *poSource,
    1448             :                                    CPLString osPath)
    1449             : {
    1450             :     json_object_iter it;
    1451         155 :     it.key = nullptr;
    1452         155 :     it.val = nullptr;
    1453         155 :     it.entry = nullptr;
    1454         310 :     CPLString osCurPath;
    1455         538 :     json_object_object_foreachC(poSource, it)
    1456             :     {
    1457         383 :         if (osPath.empty() && !m_osFID.empty() && EQUAL(m_osFID, it.key))
    1458             :         {
    1459           7 :             json_type eJSONType = json_object_get_type(it.val);
    1460           7 :             if (eJSONType == json_type_int)
    1461             :             {
    1462           7 :                 poFeature->SetFID((GIntBig)json_object_get_int64(it.val));
    1463           7 :                 continue;
    1464             :             }
    1465             :         }
    1466             : 
    1467         376 :         if (!osPath.empty())
    1468         190 :             osCurPath = osPath + "." + it.key;
    1469             :         else
    1470         186 :             osCurPath = it.key;
    1471             :         std::map<CPLString, int>::iterator oIter =
    1472         376 :             m_aosMapToFieldIndex.find(osCurPath);
    1473         376 :         if (oIter != m_aosMapToFieldIndex.end())
    1474             :         {
    1475         163 :             switch (json_object_get_type(it.val))
    1476             :             {
    1477           1 :                 case json_type_null:
    1478           1 :                     poFeature->SetFieldNull(oIter->second);
    1479           1 :                     break;
    1480           7 :                 case json_type_boolean:
    1481           7 :                     poFeature->SetField(oIter->second,
    1482           7 :                                         json_object_get_boolean(it.val));
    1483           7 :                     break;
    1484          35 :                 case json_type_int:
    1485          35 :                     poFeature->SetField(oIter->second,
    1486          35 :                                         (GIntBig)json_object_get_int64(it.val));
    1487          35 :                     break;
    1488          14 :                 case json_type_double:
    1489          14 :                     poFeature->SetField(oIter->second,
    1490          14 :                                         json_object_get_double(it.val));
    1491          14 :                     break;
    1492          28 :                 case json_type_array:
    1493             :                 {
    1494          28 :                     if (m_poFeatureDefn->GetFieldDefn(oIter->second)
    1495          28 :                             ->GetType() == OFTIntegerList)
    1496             :                     {
    1497          14 :                         std::vector<int> anValues;
    1498           7 :                         const auto nLength = json_object_array_length(it.val);
    1499          14 :                         for (auto i = decltype(nLength){0}; i < nLength; i++)
    1500             :                         {
    1501           7 :                             anValues.push_back(json_object_get_int(
    1502           7 :                                 json_object_array_get_idx(it.val, i)));
    1503             :                         }
    1504           7 :                         if (nLength)
    1505           7 :                             poFeature->SetField(oIter->second,
    1506             :                                                 static_cast<int>(nLength),
    1507           7 :                                                 &anValues[0]);
    1508             :                     }
    1509          21 :                     else if (m_poFeatureDefn->GetFieldDefn(oIter->second)
    1510          21 :                                  ->GetType() == OFTInteger64List)
    1511             :                     {
    1512          14 :                         std::vector<GIntBig> anValues;
    1513           7 :                         const auto nLength = json_object_array_length(it.val);
    1514          14 :                         for (auto i = decltype(nLength){0}; i < nLength; i++)
    1515             :                         {
    1516           7 :                             anValues.push_back(json_object_get_int64(
    1517           7 :                                 json_object_array_get_idx(it.val, i)));
    1518             :                         }
    1519           7 :                         if (nLength)
    1520           7 :                             poFeature->SetField(oIter->second,
    1521             :                                                 static_cast<int>(nLength),
    1522           7 :                                                 &anValues[0]);
    1523             :                     }
    1524          14 :                     else if (m_poFeatureDefn->GetFieldDefn(oIter->second)
    1525          14 :                                  ->GetType() == OFTRealList)
    1526             :                     {
    1527          14 :                         std::vector<double> adfValues;
    1528           7 :                         const auto nLength = json_object_array_length(it.val);
    1529          14 :                         for (auto i = decltype(nLength){0}; i < nLength; i++)
    1530             :                         {
    1531           7 :                             adfValues.push_back(json_object_get_double(
    1532           7 :                                 json_object_array_get_idx(it.val, i)));
    1533             :                         }
    1534           7 :                         if (nLength)
    1535           7 :                             poFeature->SetField(oIter->second,
    1536             :                                                 static_cast<int>(nLength),
    1537           7 :                                                 &adfValues[0]);
    1538             :                     }
    1539           7 :                     else if (m_poFeatureDefn->GetFieldDefn(oIter->second)
    1540           7 :                                  ->GetType() == OFTStringList)
    1541             :                     {
    1542          14 :                         std::vector<char *> apszValues;
    1543           7 :                         const auto nLength = json_object_array_length(it.val);
    1544          14 :                         for (auto i = decltype(nLength){0}; i < nLength; i++)
    1545             :                         {
    1546           7 :                             apszValues.push_back(
    1547           7 :                                 CPLStrdup(json_object_get_string(
    1548           7 :                                     json_object_array_get_idx(it.val, i))));
    1549             :                         }
    1550           7 :                         apszValues.push_back(nullptr);
    1551           7 :                         poFeature->SetField(oIter->second, &apszValues[0]);
    1552          14 :                         for (auto i = decltype(nLength){0}; i < nLength; i++)
    1553             :                         {
    1554           7 :                             CPLFree(apszValues[i]);
    1555             :                         }
    1556             :                     }
    1557          28 :                     break;
    1558             :                 }
    1559          78 :                 default:
    1560             :                 {
    1561          78 :                     if (m_poFeatureDefn->GetFieldDefn(oIter->second)
    1562          78 :                             ->GetType() == OFTBinary)
    1563             :                     {
    1564             :                         GByte *pabyBase64 =
    1565           7 :                             (GByte *)CPLStrdup(json_object_get_string(it.val));
    1566           7 :                         int nBytes = CPLBase64DecodeInPlace(pabyBase64);
    1567           7 :                         poFeature->SetField(oIter->second, nBytes, pabyBase64);
    1568           7 :                         CPLFree(pabyBase64);
    1569             :                     }
    1570             :                     else
    1571             :                     {
    1572          71 :                         poFeature->SetField(oIter->second,
    1573             :                                             json_object_get_string(it.val));
    1574             :                     }
    1575          78 :                     break;
    1576             :                 }
    1577             :             }
    1578             :         }
    1579         213 :         else if ((oIter = m_aosMapToGeomFieldIndex.find(osCurPath)) !=
    1580         426 :                  m_aosMapToGeomFieldIndex.end())
    1581             :         {
    1582          64 :             OGRGeometry *poGeom = nullptr;
    1583          64 :             if (m_abIsGeoPoint[oIter->second])
    1584             :             {
    1585          39 :                 json_type eJSONType = json_object_get_type(it.val);
    1586          62 :                 if (eJSONType == json_type_array &&
    1587          23 :                     json_object_array_length(it.val) == 2)
    1588             :                 {
    1589          23 :                     json_object *poX = json_object_array_get_idx(it.val, 0);
    1590          23 :                     json_object *poY = json_object_array_get_idx(it.val, 1);
    1591          23 :                     if (poX != nullptr && poY != nullptr)
    1592             :                     {
    1593          46 :                         poGeom = new OGRPoint(json_object_get_double(poX),
    1594          23 :                                               json_object_get_double(poY));
    1595             :                     }
    1596             :                 }
    1597          16 :                 else if (eJSONType == json_type_object)
    1598             :                 {
    1599             :                     json_object *poX =
    1600           4 :                         CPL_json_object_object_get(it.val, "lon");
    1601             :                     json_object *poY =
    1602           4 :                         CPL_json_object_object_get(it.val, "lat");
    1603           4 :                     if (poX != nullptr && poY != nullptr)
    1604             :                     {
    1605           8 :                         poGeom = new OGRPoint(json_object_get_double(poX),
    1606           4 :                                               json_object_get_double(poY));
    1607             :                     }
    1608             :                 }
    1609          12 :                 else if (eJSONType == json_type_string)
    1610             :                 {
    1611          12 :                     const char *pszLatLon = json_object_get_string(it.val);
    1612          12 :                     char **papszTokens = CSLTokenizeString2(pszLatLon, ",", 0);
    1613          12 :                     if (CSLCount(papszTokens) == 2)
    1614             :                     {
    1615          16 :                         poGeom = new OGRPoint(CPLAtof(papszTokens[1]),
    1616           8 :                                               CPLAtof(papszTokens[0]));
    1617             :                     }
    1618             :                     else
    1619             :                     {
    1620           4 :                         double lat[2] = {0.0, 0.0};
    1621           4 :                         double lon[2] = {0.0, 0.0};
    1622           4 :                         decode_geohash_bbox(pszLatLon, lat, lon);
    1623           4 :                         poGeom = new OGRPoint((lon[0] + lon[1]) / 2,
    1624           4 :                                               (lat[0] + lat[1]) / 2);
    1625             :                     }
    1626             : 
    1627          12 :                     CSLDestroy(papszTokens);
    1628             :                 }
    1629             :             }
    1630          25 :             else if (json_object_get_type(it.val) == json_type_object)
    1631             :             {
    1632             :                 json_object *poType =
    1633          24 :                     CPL_json_object_object_get(it.val, "type");
    1634             :                 json_object *poRadius =
    1635          24 :                     CPL_json_object_object_get(it.val, "radius");
    1636             :                 json_object *poCoordinates =
    1637          24 :                     CPL_json_object_object_get(it.val, "coordinates");
    1638          24 :                 if (poType && poRadius && poCoordinates &&
    1639           3 :                     json_object_get_type(poType) == json_type_string &&
    1640           3 :                     EQUAL(json_object_get_string(poType), "circle") &&
    1641           3 :                     (json_object_get_type(poRadius) == json_type_string ||
    1642           1 :                      json_object_get_type(poRadius) == json_type_double ||
    1643           1 :                      json_object_get_type(poRadius) == json_type_int) &&
    1644          51 :                     json_object_get_type(poCoordinates) == json_type_array &&
    1645           3 :                     json_object_array_length(poCoordinates) == 2)
    1646             :                 {
    1647           3 :                     const char *pszRadius = json_object_get_string(poRadius);
    1648           6 :                     const double dfX = json_object_get_double(
    1649           3 :                         json_object_array_get_idx(poCoordinates, 0));
    1650           6 :                     const double dfY = json_object_get_double(
    1651           3 :                         json_object_array_get_idx(poCoordinates, 1));
    1652           3 :                     const int nRadiusLength = (int)strlen(pszRadius);
    1653           3 :                     double dfRadius = CPLAtof(pszRadius);
    1654           3 :                     double dfUnit = 0.0;
    1655           3 :                     if (nRadiusLength >= 1 &&
    1656           3 :                         pszRadius[nRadiusLength - 1] == 'm')
    1657             :                     {
    1658           2 :                         if (nRadiusLength >= 2 &&
    1659           2 :                             pszRadius[nRadiusLength - 2] == 'k')
    1660           1 :                             dfUnit = 1000;
    1661           1 :                         else if (nRadiusLength >= 2 &&
    1662           1 :                                  pszRadius[nRadiusLength - 2] >= '0' &&
    1663           1 :                                  pszRadius[nRadiusLength - 2] <= '9')
    1664           1 :                             dfUnit = 1;
    1665             :                     }
    1666           1 :                     else if (nRadiusLength >= 1 &&
    1667           1 :                              pszRadius[nRadiusLength - 1] >= '0' &&
    1668           1 :                              pszRadius[nRadiusLength - 1] <= '9')
    1669             :                     {
    1670           1 :                         dfUnit = 1;
    1671             :                     }
    1672             : 
    1673           3 :                     if (dfRadius == 0)
    1674           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1675             :                                  "Unknown unit in %s", pszRadius);
    1676             :                     else
    1677             :                     {
    1678           3 :                         dfRadius *= dfUnit;
    1679           3 :                         OGRLinearRing *poRing = new OGRLinearRing();
    1680         276 :                         for (double dfStep = 0; dfStep <= 360; dfStep += 4)
    1681             :                         {
    1682         273 :                             double dfLat = 0.0;
    1683         273 :                             double dfLon = 0.0;
    1684         273 :                             OGR_GreatCircle_ExtendPosition(
    1685             :                                 dfY, dfX, dfRadius, dfStep, &dfLat, &dfLon);
    1686         273 :                             poRing->addPoint(dfLon, dfLat);
    1687             :                         }
    1688           3 :                         OGRPolygon *poPoly = new OGRPolygon();
    1689           3 :                         poPoly->addRingDirectly(poRing);
    1690           3 :                         poGeom = poPoly;
    1691             :                     }
    1692             :                 }
    1693          21 :                 else if (poType && poCoordinates &&
    1694          21 :                          json_object_get_type(poType) == json_type_string &&
    1695          21 :                          EQUAL(json_object_get_string(poType), "envelope") &&
    1696           1 :                          json_object_get_type(poCoordinates) ==
    1697          42 :                              json_type_array &&
    1698           1 :                          json_object_array_length(poCoordinates) == 2)
    1699             :                 {
    1700             :                     json_object *poCorner1 =
    1701           1 :                         json_object_array_get_idx(poCoordinates, 0);
    1702             :                     json_object *poCorner2 =
    1703           1 :                         json_object_array_get_idx(poCoordinates, 1);
    1704           1 :                     if (poCorner1 && poCorner2 &&
    1705           1 :                         json_object_get_type(poCorner1) == json_type_array &&
    1706           1 :                         json_object_array_length(poCorner1) == 2 &&
    1707           3 :                         json_object_get_type(poCorner2) == json_type_array &&
    1708           1 :                         json_object_array_length(poCorner2) == 2)
    1709             :                     {
    1710           2 :                         const double dfX1 = json_object_get_double(
    1711           1 :                             json_object_array_get_idx(poCorner1, 0));
    1712           2 :                         const double dfY1 = json_object_get_double(
    1713           1 :                             json_object_array_get_idx(poCorner1, 1));
    1714           2 :                         const double dfX2 = json_object_get_double(
    1715           1 :                             json_object_array_get_idx(poCorner2, 0));
    1716           2 :                         const double dfY2 = json_object_get_double(
    1717           1 :                             json_object_array_get_idx(poCorner2, 1));
    1718           1 :                         OGRLinearRing *poRing = new OGRLinearRing();
    1719           1 :                         poRing->addPoint(dfX1, dfY1);
    1720           1 :                         poRing->addPoint(dfX2, dfY1);
    1721           1 :                         poRing->addPoint(dfX2, dfY2);
    1722           1 :                         poRing->addPoint(dfX1, dfY2);
    1723           1 :                         poRing->addPoint(dfX1, dfY1);
    1724           1 :                         OGRPolygon *poPoly = new OGRPolygon();
    1725           1 :                         poPoly->addRingDirectly(poRing);
    1726           1 :                         poGeom = poPoly;
    1727             :                     }
    1728             :                 }
    1729             :                 else
    1730             :                 {
    1731          20 :                     poGeom = OGRGeoJSONReadGeometry(it.val);
    1732             :                 }
    1733             :             }
    1734           1 :             else if (json_object_get_type(it.val) == json_type_string)
    1735             :             {
    1736             :                 // Assume this is WKT
    1737           1 :                 OGRGeometryFactory::createFromWkt(
    1738             :                     json_object_get_string(it.val), nullptr, &poGeom);
    1739             :             }
    1740             : 
    1741          64 :             if (poGeom != nullptr)
    1742             :             {
    1743          64 :                 poGeom->assignSpatialReference(
    1744          64 :                     m_poFeatureDefn->GetGeomFieldDefn(oIter->second)
    1745         128 :                         ->GetSpatialRef());
    1746          64 :                 poFeature->SetGeomFieldDirectly(oIter->second, poGeom);
    1747             :             }
    1748             :         }
    1749         221 :         else if (json_object_get_type(it.val) == json_type_object &&
    1750          73 :                  (m_poDS->m_bFlattenNestedAttributes ||
    1751           2 :                   (osPath.empty() && m_osMappingName == "FeatureCollection" &&
    1752           0 :                    strcmp(it.key, "properties") == 0)))
    1753             :         {
    1754          71 :             BuildFeature(poFeature, it.val, osCurPath);
    1755             :         }
    1756          79 :         else if (json_object_get_type(it.val) == json_type_object &&
    1757           1 :                  !m_poDS->m_bFlattenNestedAttributes)
    1758             :         {
    1759           1 :             if (m_aosMapToGeomFieldIndex.find(osCurPath + ".coordinates") !=
    1760           2 :                 m_aosMapToGeomFieldIndex.end())
    1761             :             {
    1762           1 :                 BuildFeature(poFeature, it.val, osCurPath);
    1763             :             }
    1764             :         }
    1765             :     }
    1766         155 : }
    1767             : 
    1768             : /************************************************************************/
    1769             : /*                            AppendGroup()                             */
    1770             : /************************************************************************/
    1771             : 
    1772           4 : static json_object *AppendGroup(json_object *parent, const CPLString &name)
    1773             : {
    1774           4 :     json_object *obj = json_object_new_object();
    1775           4 :     json_object *properties = json_object_new_object();
    1776           4 :     json_object_object_add(parent, name, obj);
    1777           4 :     json_object_object_add(obj, "properties", properties);
    1778           4 :     return properties;
    1779             : }
    1780             : 
    1781             : /************************************************************************/
    1782             : /*                           AddPropertyMap()                           */
    1783             : /************************************************************************/
    1784             : 
    1785          18 : static json_object *AddPropertyMap(const CPLString &type)
    1786             : {
    1787          18 :     json_object *obj = json_object_new_object();
    1788          18 :     json_object_object_add(obj, "type", json_object_new_string(type.c_str()));
    1789          18 :     return obj;
    1790             : }
    1791             : 
    1792             : /************************************************************************/
    1793             : /*                      GetContainerForMapping()                        */
    1794             : /************************************************************************/
    1795             : 
    1796             : static json_object *
    1797          48 : GetContainerForMapping(json_object *poContainer,
    1798             :                        const std::vector<CPLString> &aosPath,
    1799             :                        std::map<std::vector<CPLString>, json_object *> &oMap)
    1800             : {
    1801          48 :     std::vector<CPLString> aosSubPath;
    1802          80 :     for (int j = 0; j < (int)aosPath.size() - 1; j++)
    1803             :     {
    1804          32 :         aosSubPath.push_back(aosPath[j]);
    1805             :         std::map<std::vector<CPLString>, json_object *>::iterator oIter =
    1806          32 :             oMap.find(aosSubPath);
    1807          32 :         if (oIter == oMap.end())
    1808             :         {
    1809          11 :             json_object *poNewContainer = json_object_new_object();
    1810          11 :             json_object *poProperties = json_object_new_object();
    1811          11 :             json_object_object_add(poContainer, aosPath[j], poNewContainer);
    1812          11 :             json_object_object_add(poNewContainer, "properties", poProperties);
    1813          11 :             oMap[aosSubPath] = poProperties;
    1814          11 :             poContainer = poProperties;
    1815             :         }
    1816             :         else
    1817             :         {
    1818          21 :             poContainer = oIter->second;
    1819             :         }
    1820             :     }
    1821          96 :     return poContainer;
    1822             : }
    1823             : 
    1824             : /************************************************************************/
    1825             : /*                             BuildMap()                               */
    1826             : /************************************************************************/
    1827             : 
    1828          13 : CPLString OGRElasticLayer::BuildMap()
    1829             : {
    1830          13 :     json_object *map = json_object_new_object();
    1831             : 
    1832          26 :     std::map<std::vector<CPLString>, json_object *> oMap;
    1833             : 
    1834             :     json_object *poMapping;
    1835          13 :     json_object *poMappingProperties = json_object_new_object();
    1836          13 :     if (m_poDS->m_nMajorVersion < 7)
    1837             :     {
    1838          11 :         poMapping = json_object_new_object();
    1839          11 :         json_object_object_add(map, m_osMappingName, poMapping);
    1840             :     }
    1841             :     else
    1842             :     {
    1843           2 :         poMapping = map;
    1844             :     }
    1845          13 :     json_object_object_add(poMapping, "properties", poMappingProperties);
    1846             : 
    1847          13 :     if (m_poDS->m_nMajorVersion < 7 && m_osMappingName == "FeatureCollection")
    1848             :     {
    1849           9 :         json_object_object_add(
    1850             :             poMappingProperties, "type",
    1851           9 :             AddPropertyMap(m_poDS->m_nMajorVersion >= 5 ? "text" : "string"));
    1852             : 
    1853          18 :         std::vector<CPLString> aosPath;
    1854           9 :         aosPath.push_back("properties");
    1855           9 :         aosPath.push_back("dummy");
    1856           9 :         GetContainerForMapping(poMappingProperties, aosPath, oMap);
    1857             :     }
    1858             : 
    1859             :     /* skip _id field */
    1860          34 :     for (int i = 1; i < m_poFeatureDefn->GetFieldCount(); i++)
    1861             :     {
    1862          21 :         OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    1863             : 
    1864          21 :         json_object *poContainer = GetContainerForMapping(
    1865          21 :             poMappingProperties, m_aaosFieldPaths[i], oMap);
    1866          21 :         const char *pszLastComponent = m_aaosFieldPaths[i].back();
    1867             : 
    1868          21 :         const char *pszType = "string";
    1869          21 :         const char *pszFormat = nullptr;
    1870             : 
    1871          21 :         switch (poFieldDefn->GetType())
    1872             :         {
    1873           4 :             case OFTInteger:
    1874             :             case OFTIntegerList:
    1875             :             {
    1876           4 :                 if (poFieldDefn->GetSubType() == OFSTBoolean)
    1877           1 :                     pszType = "boolean";
    1878             :                 else
    1879           3 :                     pszType = "integer";
    1880           4 :                 break;
    1881             :             }
    1882           2 :             case OFTInteger64:
    1883             :             case OFTInteger64List:
    1884           2 :                 pszType = "long";
    1885           2 :                 break;
    1886           3 :             case OFTReal:
    1887             :             case OFTRealList:
    1888           3 :                 pszType = "double";
    1889           3 :                 break;
    1890           2 :             case OFTDateTime:
    1891             :             case OFTDate:
    1892           2 :                 pszType = "date";
    1893           2 :                 pszFormat = "yyyy/MM/dd HH:mm:ss.SSSZZ||yyyy/MM/dd "
    1894             :                             "HH:mm:ss.SSS||yyyy/MM/dd";
    1895           2 :                 break;
    1896           1 :             case OFTTime:
    1897           1 :                 pszType = "date";
    1898           1 :                 pszFormat = "HH:mm:ss.SSS";
    1899           1 :                 break;
    1900           1 :             case OFTBinary:
    1901           1 :                 pszType = "binary";
    1902           1 :                 break;
    1903           8 :             default:
    1904           8 :                 break;
    1905             :         }
    1906             : 
    1907          21 :         bool bAnalyzed = EQUAL(pszType, "string");
    1908          21 :         json_object *poPropertyMap = json_object_new_object();
    1909          21 :         if (m_poDS->m_nMajorVersion >= 5 && EQUAL(pszType, "string"))
    1910             :         {
    1911           0 :             if (CSLFindString(m_papszNotAnalyzedFields,
    1912           0 :                               poFieldDefn->GetNameRef()) >= 0 ||
    1913           0 :                 (CSLCount(m_papszNotAnalyzedFields) == 1 &&
    1914           0 :                  EQUAL(m_papszNotAnalyzedFields[0], "{ALL}")))
    1915             :             {
    1916           0 :                 bAnalyzed = false;
    1917           0 :                 pszType = "keyword";
    1918             :             }
    1919             :             else
    1920           0 :                 pszType = "text";
    1921             :         }
    1922          21 :         json_object_object_add(poPropertyMap, "type",
    1923             :                                json_object_new_string(pszType));
    1924          21 :         if (pszFormat)
    1925           3 :             json_object_object_add(poPropertyMap, "format",
    1926             :                                    json_object_new_string(pszFormat));
    1927          41 :         if (m_bStoreFields ||
    1928          20 :             CSLFindString(m_papszStoredFields, poFieldDefn->GetNameRef()) >= 0)
    1929           2 :             json_object_object_add(poPropertyMap, "store",
    1930             :                                    json_object_new_string("yes"));
    1931          42 :         if (m_poDS->m_nMajorVersion < 5 &&
    1932          21 :             (CSLFindString(m_papszNotAnalyzedFields,
    1933          20 :                            poFieldDefn->GetNameRef()) >= 0 ||
    1934          20 :              (CSLCount(m_papszNotAnalyzedFields) == 1 &&
    1935          13 :               EQUAL(m_papszNotAnalyzedFields[0], "{ALL}"))))
    1936             :         {
    1937           1 :             bAnalyzed = false;
    1938           1 :             json_object_object_add(poPropertyMap, "index",
    1939             :                                    json_object_new_string("not_analyzed"));
    1940             :         }
    1941          20 :         else if (CSLFindString(m_papszNotIndexedFields,
    1942          20 :                                poFieldDefn->GetNameRef()) >= 0)
    1943           1 :             json_object_object_add(poPropertyMap, "index",
    1944             :                                    json_object_new_string("no"));
    1945             : 
    1946          28 :         if (bAnalyzed && (CSLFindString(m_papszFieldsWithRawValue,
    1947           7 :                                         poFieldDefn->GetNameRef()) >= 0 ||
    1948           7 :                           (CSLCount(m_papszFieldsWithRawValue) == 1 &&
    1949           0 :                            EQUAL(m_papszFieldsWithRawValue[0], "{ALL}"))))
    1950             :         {
    1951           0 :             json_object *poFields = json_object_new_object();
    1952           0 :             json_object *poRaw = json_object_new_object();
    1953           0 :             json_object_object_add(poFields, "raw", poRaw);
    1954           0 :             if (m_poDS->m_nMajorVersion >= 5)
    1955             :             {
    1956           0 :                 json_object_object_add(poRaw, "type",
    1957             :                                        json_object_new_string("keyword"));
    1958             :             }
    1959             :             else
    1960             :             {
    1961           0 :                 json_object_object_add(poRaw, "type",
    1962             :                                        json_object_new_string("string"));
    1963           0 :                 json_object_object_add(poRaw, "index",
    1964             :                                        json_object_new_string("not_analyzed"));
    1965             :             }
    1966           0 :             json_object_object_add(poPropertyMap, "fields", poFields);
    1967             :         }
    1968             : 
    1969          21 :         json_object_object_add(poContainer, pszLastComponent, poPropertyMap);
    1970             :     }
    1971             : 
    1972          31 :     for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
    1973             :     {
    1974          36 :         std::vector<CPLString> aosPath = m_aaosGeomFieldPaths[i];
    1975          18 :         bool bAddGeoJSONType = false;
    1976          22 :         if (m_abIsGeoPoint[i] && aosPath.size() >= 2 &&
    1977           4 :             aosPath.back() == "coordinates")
    1978             :         {
    1979           4 :             bAddGeoJSONType = true;
    1980           4 :             aosPath.resize((int)aosPath.size() - 1);
    1981             :         }
    1982             : 
    1983             :         json_object *poContainer =
    1984          18 :             GetContainerForMapping(poMappingProperties, aosPath, oMap);
    1985          18 :         const char *pszLastComponent = aosPath.back();
    1986             : 
    1987          18 :         if (m_abIsGeoPoint[i])
    1988             :         {
    1989           5 :             json_object *geo_point = AddPropertyMap("geo_point");
    1990           5 :             if (bAddGeoJSONType)
    1991             :             {
    1992             :                 json_object *geometry =
    1993           4 :                     AppendGroup(poContainer, pszLastComponent);
    1994           4 :                 json_object_object_add(
    1995             :                     geometry, "type",
    1996           4 :                     AddPropertyMap(m_poDS->m_nMajorVersion >= 5 ? "text"
    1997             :                                                                 : "string"));
    1998           4 :                 json_object_object_add(geometry, "coordinates", geo_point);
    1999             :             }
    2000             :             else
    2001             :             {
    2002           1 :                 json_object_object_add(poContainer, pszLastComponent,
    2003             :                                        geo_point);
    2004             :             }
    2005           5 :             if (!m_osPrecision.empty())
    2006             :             {
    2007           1 :                 json_object *field_data = json_object_new_object();
    2008           1 :                 json_object_object_add(geo_point, "fielddata", field_data);
    2009           1 :                 json_object_object_add(field_data, "format",
    2010             :                                        json_object_new_string("compressed"));
    2011           1 :                 json_object_object_add(
    2012             :                     field_data, "precision",
    2013             :                     json_object_new_string(m_osPrecision.c_str()));
    2014             :             }
    2015             :         }
    2016             :         else
    2017             :         {
    2018          13 :             json_object *geometry = json_object_new_object();
    2019          13 :             json_object_object_add(poContainer, pszLastComponent, geometry);
    2020          13 :             json_object_object_add(geometry, "type",
    2021             :                                    json_object_new_string("geo_shape"));
    2022          13 :             if (!m_osPrecision.empty())
    2023           1 :                 json_object_object_add(
    2024             :                     geometry, "precision",
    2025             :                     json_object_new_string(m_osPrecision.c_str()));
    2026             :         }
    2027             :     }
    2028             : 
    2029          13 :     json_object *poMeta = nullptr;
    2030          13 :     json_object *poGeomFields = nullptr;
    2031          13 :     json_object *poFields = nullptr;
    2032          13 :     if (!m_osFID.empty())
    2033             :     {
    2034           5 :         poMeta = json_object_new_object();
    2035           5 :         json_object_object_add(poMeta, "fid",
    2036             :                                json_object_new_string(m_osFID.c_str()));
    2037             :     }
    2038          31 :     for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
    2039             :     {
    2040             :         OGRGeomFieldDefn *poGeomFieldDefn =
    2041          18 :             m_poFeatureDefn->GetGeomFieldDefn(i);
    2042          18 :         if (!m_abIsGeoPoint[i] && poGeomFieldDefn->GetType() != wkbUnknown)
    2043             :         {
    2044           1 :             if (poMeta == nullptr)
    2045           1 :                 poMeta = json_object_new_object();
    2046           1 :             if (poGeomFields == nullptr)
    2047             :             {
    2048           1 :                 poGeomFields = json_object_new_object();
    2049           1 :                 json_object_object_add(poMeta, "geomfields", poGeomFields);
    2050             :             }
    2051           1 :             json_object_object_add(poGeomFields, poGeomFieldDefn->GetNameRef(),
    2052             :                                    json_object_new_string(OGRToOGCGeomType(
    2053             :                                        poGeomFieldDefn->GetType())));
    2054             :         }
    2055             :     }
    2056          47 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    2057             :     {
    2058          34 :         OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    2059          34 :         OGRFieldType eType = poFieldDefn->GetType();
    2060          34 :         if (eType == OFTIntegerList || eType == OFTInteger64List ||
    2061          31 :             eType == OFTRealList || eType == OFTStringList)
    2062             :         {
    2063           4 :             if (poMeta == nullptr)
    2064           1 :                 poMeta = json_object_new_object();
    2065           4 :             if (poFields == nullptr)
    2066             :             {
    2067           1 :                 poFields = json_object_new_object();
    2068           1 :                 json_object_object_add(poMeta, "fields", poFields);
    2069             :             }
    2070           4 :             json_object_object_add(
    2071             :                 poFields, poFieldDefn->GetNameRef(),
    2072             :                 json_object_new_string(OGR_GetFieldTypeName(eType)));
    2073             :         }
    2074             :     }
    2075          13 :     if (poMeta)
    2076           7 :         json_object_object_add(poMapping, "_meta", poMeta);
    2077             : 
    2078          13 :     CPLString jsonMap(json_object_to_json_string(map));
    2079          13 :     json_object_put(map);
    2080             : 
    2081             :     // Got personally caught by that...
    2082          13 :     if (CSLCount(m_papszStoredFields) == 1 &&
    2083           1 :         (EQUAL(m_papszStoredFields[0], "YES") ||
    2084          14 :          EQUAL(m_papszStoredFields[0], "TRUE")) &&
    2085           0 :         m_poFeatureDefn->GetFieldIndex(m_papszStoredFields[0]) < 0)
    2086             :     {
    2087           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    2088             :                  "STORED_FIELDS=%s was specified. Perhaps you meant "
    2089             :                  "STORE_FIELDS=%s instead?",
    2090           0 :                  m_papszStoredFields[0], m_papszStoredFields[0]);
    2091             :     }
    2092             : 
    2093          26 :     return jsonMap;
    2094             : }
    2095             : 
    2096             : /************************************************************************/
    2097             : /*                       BuildGeoJSONGeometry()                         */
    2098             : /************************************************************************/
    2099             : 
    2100          19 : static void BuildGeoJSONGeometry(json_object *geometry,
    2101             :                                  const OGRGeometry *poGeom)
    2102             : {
    2103          19 :     const int nPrecision = 10;
    2104          19 :     double dfEps = pow(10.0, -(double)nPrecision);
    2105          19 :     const char *pszGeomType = "";
    2106          19 :     switch (wkbFlatten(poGeom->getGeometryType()))
    2107             :     {
    2108           6 :         case wkbPoint:
    2109           6 :             pszGeomType = "point";
    2110           6 :             break;
    2111           3 :         case wkbLineString:
    2112           3 :             pszGeomType = "linestring";
    2113           3 :             break;
    2114           2 :         case wkbPolygon:
    2115           2 :             pszGeomType = "polygon";
    2116           2 :             break;
    2117           2 :         case wkbMultiPoint:
    2118           2 :             pszGeomType = "multipoint";
    2119           2 :             break;
    2120           2 :         case wkbMultiLineString:
    2121           2 :             pszGeomType = "multilinestring";
    2122           2 :             break;
    2123           2 :         case wkbMultiPolygon:
    2124           2 :             pszGeomType = "multipolygon";
    2125           2 :             break;
    2126           2 :         case wkbGeometryCollection:
    2127           2 :             pszGeomType = "geometrycollection";
    2128           2 :             break;
    2129           0 :         default:
    2130           0 :             break;
    2131             :     }
    2132          19 :     json_object_object_add(geometry, "type",
    2133             :                            json_object_new_string(pszGeomType));
    2134             : 
    2135          19 :     switch (wkbFlatten(poGeom->getGeometryType()))
    2136             :     {
    2137           6 :         case wkbPoint:
    2138             :         {
    2139           6 :             const OGRPoint *poPoint = poGeom->toPoint();
    2140           6 :             json_object *coordinates = json_object_new_array();
    2141           6 :             json_object_object_add(geometry, "coordinates", coordinates);
    2142           6 :             json_object_array_add(coordinates,
    2143             :                                   json_object_new_double_with_precision(
    2144             :                                       poPoint->getX(), nPrecision));
    2145           6 :             json_object_array_add(coordinates,
    2146             :                                   json_object_new_double_with_precision(
    2147             :                                       poPoint->getY(), nPrecision));
    2148           6 :             break;
    2149             :         }
    2150             : 
    2151           3 :         case wkbLineString:
    2152             :         {
    2153           3 :             const OGRLineString *poLS = poGeom->toLineString();
    2154           3 :             json_object *coordinates = json_object_new_array();
    2155           3 :             json_object_object_add(geometry, "coordinates", coordinates);
    2156           9 :             for (int i = 0; i < poLS->getNumPoints(); i++)
    2157             :             {
    2158           6 :                 json_object *point = json_object_new_array();
    2159           6 :                 json_object_array_add(coordinates, point);
    2160           6 :                 json_object_array_add(
    2161             :                     point, json_object_new_double_with_precision(poLS->getX(i),
    2162             :                                                                  nPrecision));
    2163           6 :                 json_object_array_add(
    2164             :                     point, json_object_new_double_with_precision(poLS->getY(i),
    2165             :                                                                  nPrecision));
    2166             :             }
    2167           3 :             break;
    2168             :         }
    2169             : 
    2170           2 :         case wkbPolygon:
    2171             :         {
    2172           2 :             const OGRPolygon *poPoly = poGeom->toPolygon();
    2173           2 :             json_object *coordinates = json_object_new_array();
    2174           2 :             json_object_object_add(geometry, "coordinates", coordinates);
    2175           6 :             for (auto &&poLS : *poPoly)
    2176             :             {
    2177           4 :                 json_object *ring = json_object_new_array();
    2178           4 :                 json_object_array_add(coordinates, ring);
    2179          20 :                 for (int j = 0; j < poLS->getNumPoints(); j++)
    2180             :                 {
    2181          28 :                     if (j > 0 &&
    2182          20 :                         fabs(poLS->getX(j) - poLS->getX(j - 1)) < dfEps &&
    2183           4 :                         fabs(poLS->getY(j) - poLS->getY(j - 1)) < dfEps)
    2184           0 :                         continue;
    2185          16 :                     json_object *point = json_object_new_array();
    2186          16 :                     json_object_array_add(ring, point);
    2187          16 :                     json_object_array_add(point,
    2188             :                                           json_object_new_double_with_precision(
    2189          16 :                                               poLS->getX(j), nPrecision));
    2190          16 :                     json_object_array_add(point,
    2191             :                                           json_object_new_double_with_precision(
    2192          16 :                                               poLS->getY(j), nPrecision));
    2193             :                 }
    2194             :             }
    2195           2 :             break;
    2196             :         }
    2197             : 
    2198           2 :         case wkbMultiPoint:
    2199             :         {
    2200           2 :             const OGRMultiPoint *poMP = poGeom->toMultiPoint();
    2201           2 :             json_object *coordinates = json_object_new_array();
    2202           2 :             json_object_object_add(geometry, "coordinates", coordinates);
    2203           6 :             for (auto &&poPoint : *poMP)
    2204             :             {
    2205           4 :                 json_object *point = json_object_new_array();
    2206           4 :                 json_object_array_add(coordinates, point);
    2207           4 :                 json_object_array_add(point,
    2208             :                                       json_object_new_double_with_precision(
    2209             :                                           poPoint->getX(), nPrecision));
    2210           4 :                 json_object_array_add(point,
    2211             :                                       json_object_new_double_with_precision(
    2212             :                                           poPoint->getY(), nPrecision));
    2213             :             }
    2214           2 :             break;
    2215             :         }
    2216             : 
    2217           2 :         case wkbMultiLineString:
    2218             :         {
    2219           2 :             const OGRMultiLineString *poMLS = poGeom->toMultiLineString();
    2220           2 :             json_object *coordinates = json_object_new_array();
    2221           2 :             json_object_object_add(geometry, "coordinates", coordinates);
    2222           6 :             for (auto &&poLS : *poMLS)
    2223             :             {
    2224           4 :                 json_object *ls = json_object_new_array();
    2225           4 :                 json_object_array_add(coordinates, ls);
    2226          12 :                 for (auto &&oPoint : *poLS)
    2227             :                 {
    2228           8 :                     json_object *point = json_object_new_array();
    2229           8 :                     json_object_array_add(ls, point);
    2230           8 :                     json_object_array_add(point,
    2231             :                                           json_object_new_double_with_precision(
    2232             :                                               oPoint.getX(), nPrecision));
    2233           8 :                     json_object_array_add(point,
    2234             :                                           json_object_new_double_with_precision(
    2235             :                                               oPoint.getY(), nPrecision));
    2236             :                 }
    2237             :             }
    2238           2 :             break;
    2239             :         }
    2240             : 
    2241           2 :         case wkbMultiPolygon:
    2242             :         {
    2243           2 :             const OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
    2244           2 :             json_object *coordinates = json_object_new_array();
    2245           2 :             json_object_object_add(geometry, "coordinates", coordinates);
    2246           6 :             for (auto &&poPoly : *poMP)
    2247             :             {
    2248           4 :                 json_object *poly = json_object_new_array();
    2249           4 :                 json_object_array_add(coordinates, poly);
    2250          10 :                 for (auto &&poLS : *poPoly)
    2251             :                 {
    2252           6 :                     json_object *ring = json_object_new_array();
    2253           6 :                     json_object_array_add(poly, ring);
    2254          30 :                     for (int k = 0; k < poLS->getNumPoints(); k++)
    2255             :                     {
    2256          42 :                         if (k > 0 &&
    2257          30 :                             fabs(poLS->getX(k) - poLS->getX(k - 1)) < dfEps &&
    2258           6 :                             fabs(poLS->getY(k) - poLS->getY(k - 1)) < dfEps)
    2259           0 :                             continue;
    2260          24 :                         json_object *point = json_object_new_array();
    2261          24 :                         json_object_array_add(ring, point);
    2262          24 :                         json_object_array_add(
    2263             :                             point, json_object_new_double_with_precision(
    2264          24 :                                        poLS->getX(k), nPrecision));
    2265          24 :                         json_object_array_add(
    2266             :                             point, json_object_new_double_with_precision(
    2267          24 :                                        poLS->getY(k), nPrecision));
    2268             :                     }
    2269             :                 }
    2270             :             }
    2271           2 :             break;
    2272             :         }
    2273             : 
    2274           2 :         case wkbGeometryCollection:
    2275             :         {
    2276           2 :             const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    2277           2 :             json_object *geometries = json_object_new_array();
    2278           2 :             json_object_object_add(geometry, "geometries", geometries);
    2279          14 :             for (auto &&poSubGeom : *poGC)
    2280             :             {
    2281          12 :                 json_object *subgeom = json_object_new_object();
    2282          12 :                 json_object_array_add(geometries, subgeom);
    2283          12 :                 BuildGeoJSONGeometry(subgeom, poSubGeom);
    2284             :             }
    2285           2 :             break;
    2286             :         }
    2287             : 
    2288           0 :         default:
    2289           0 :             break;
    2290             :     }
    2291          19 : }
    2292             : 
    2293             : /************************************************************************/
    2294             : /*                       WriteMapIfNecessary()                          */
    2295             : /************************************************************************/
    2296             : 
    2297          86 : OGRErr OGRElasticLayer::WriteMapIfNecessary()
    2298             : {
    2299          86 :     if (m_bManualMapping)
    2300          11 :         return OGRERR_NONE;
    2301             : 
    2302             :     // Check to see if the user has elected to only write out the mapping file
    2303             :     // This method will only write out one layer from the vector file in cases
    2304             :     // where there are multiple layers
    2305          75 :     if (!m_osWriteMapFilename.empty())
    2306             :     {
    2307           4 :         if (m_bSerializeMapping)
    2308             :         {
    2309           2 :             m_bSerializeMapping = false;
    2310           4 :             CPLString map = BuildMap();
    2311             : 
    2312             :             // Write the map to a file
    2313           2 :             VSILFILE *f = VSIFOpenL(m_osWriteMapFilename, "wb");
    2314           2 :             if (f)
    2315             :             {
    2316           2 :                 VSIFWriteL(map.c_str(), 1, map.length(), f);
    2317           2 :                 VSIFCloseL(f);
    2318             :             }
    2319             :         }
    2320           4 :         return OGRERR_NONE;
    2321             :     }
    2322             : 
    2323             :     // Check to see if we have any fields to upload to this index
    2324          71 :     if (m_osWriteMapFilename.empty() && m_bSerializeMapping)
    2325             :     {
    2326          11 :         m_bSerializeMapping = false;
    2327          11 :         CPLString osURL = BuildMappingURL(true);
    2328          11 :         if (!m_poDS->UploadFile(osURL.c_str(), BuildMap()))
    2329             :         {
    2330           1 :             return OGRERR_FAILURE;
    2331             :         }
    2332             :     }
    2333             : 
    2334          70 :     return OGRERR_NONE;
    2335             : }
    2336             : 
    2337             : /************************************************************************/
    2338             : /*                      GetContainerForFeature()                        */
    2339             : /************************************************************************/
    2340             : 
    2341             : static json_object *
    2342          66 : GetContainerForFeature(json_object *poContainer,
    2343             :                        const std::vector<CPLString> &aosPath,
    2344             :                        std::map<std::vector<CPLString>, json_object *> &oMap)
    2345             : {
    2346          66 :     std::vector<CPLString> aosSubPath;
    2347         121 :     for (int j = 0; j < (int)aosPath.size() - 1; j++)
    2348             :     {
    2349          55 :         aosSubPath.push_back(aosPath[j]);
    2350             :         std::map<std::vector<CPLString>, json_object *>::iterator oIter =
    2351          55 :             oMap.find(aosSubPath);
    2352          55 :         if (oIter == oMap.end())
    2353             :         {
    2354          22 :             json_object *poNewContainer = json_object_new_object();
    2355          22 :             json_object_object_add(poContainer, aosPath[j], poNewContainer);
    2356          22 :             oMap[aosSubPath] = poNewContainer;
    2357          22 :             poContainer = poNewContainer;
    2358             :         }
    2359             :         else
    2360             :         {
    2361          33 :             poContainer = oIter->second;
    2362             :         }
    2363             :     }
    2364         132 :     return poContainer;
    2365             : }
    2366             : 
    2367             : /************************************************************************/
    2368             : /*                        BuildMappingURL()                             */
    2369             : /************************************************************************/
    2370          90 : CPLString OGRElasticLayer::BuildMappingURL(bool bMappingApi)
    2371             : {
    2372             :     CPLString osURL =
    2373          90 :         CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str());
    2374          90 :     if (bMappingApi)
    2375          11 :         osURL += "/_mapping";
    2376          90 :     if (m_poDS->m_nMajorVersion < 7)
    2377          85 :         osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    2378          90 :     return osURL;
    2379             : }
    2380             : 
    2381             : /************************************************************************/
    2382             : /*                        BuildJSonFromFeature()                        */
    2383             : /************************************************************************/
    2384             : 
    2385          24 : CPLString OGRElasticLayer::BuildJSonFromFeature(OGRFeature *poFeature)
    2386             : {
    2387             : 
    2388          24 :     CPLString fields;
    2389          24 :     int nJSonFieldIndex = m_poFeatureDefn->GetFieldIndex("_json");
    2390          25 :     if (nJSonFieldIndex >= 0 &&
    2391           1 :         poFeature->IsFieldSetAndNotNull(nJSonFieldIndex))
    2392             :     {
    2393           1 :         fields = poFeature->GetFieldAsString(nJSonFieldIndex);
    2394             :     }
    2395             :     else
    2396             :     {
    2397          23 :         json_object *fieldObject = json_object_new_object();
    2398             : 
    2399          23 :         if (poFeature->GetFID() >= 0 && !m_osFID.empty())
    2400             :         {
    2401           9 :             json_object_object_add(fieldObject, m_osFID.c_str(),
    2402           9 :                                    json_object_new_int64(poFeature->GetFID()));
    2403             :         }
    2404             : 
    2405          46 :         std::map<std::vector<CPLString>, json_object *> oMap;
    2406             : 
    2407          47 :         for (int i = 0; i < poFeature->GetGeomFieldCount(); i++)
    2408             :         {
    2409          24 :             OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
    2410          24 :             if (poGeom != nullptr && !poGeom->IsEmpty())
    2411             :             {
    2412          13 :                 OGREnvelope env;
    2413          13 :                 poGeom->getEnvelope(&env);
    2414             : 
    2415          13 :                 if (m_apoCT[i] != nullptr)
    2416           1 :                     poGeom->transform(m_apoCT[i]);
    2417          12 :                 else if (env.MinX < -180 || env.MinY < -90 || env.MaxX > 180 ||
    2418          11 :                          env.MaxY > 90)
    2419             :                 {
    2420             :                     static bool bHasWarned = false;
    2421           1 :                     if (!bHasWarned)
    2422             :                     {
    2423           1 :                         bHasWarned = true;
    2424           1 :                         CPLError(
    2425             :                             CE_Warning, CPLE_AppDefined,
    2426             :                             "At least one geometry has a bounding box outside "
    2427             :                             "of [-180,180] longitude range and/or [-90,90] "
    2428             :                             "latitude range. Undefined behavior");
    2429             :                     }
    2430             :                 }
    2431             : 
    2432          26 :                 std::vector<CPLString> aosPath = m_aaosGeomFieldPaths[i];
    2433          13 :                 bool bAddGeoJSONType = false;
    2434          17 :                 if (m_abIsGeoPoint[i] && aosPath.size() >= 2 &&
    2435           4 :                     aosPath.back() == "coordinates")
    2436             :                 {
    2437           4 :                     bAddGeoJSONType = true;
    2438           4 :                     aosPath.resize((int)aosPath.size() - 1);
    2439             :                 }
    2440             : 
    2441             :                 json_object *poContainer =
    2442          13 :                     GetContainerForFeature(fieldObject, aosPath, oMap);
    2443          13 :                 const char *pszLastComponent = aosPath.back();
    2444             : 
    2445          13 :                 if (m_abIsGeoPoint[i])
    2446             :                 {
    2447           5 :                     json_object *coordinates = json_object_new_array();
    2448           5 :                     const int nPrecision = 10;
    2449           5 :                     json_object_array_add(
    2450             :                         coordinates,
    2451             :                         json_object_new_double_with_precision(
    2452           5 :                             (env.MaxX + env.MinX) * 0.5, nPrecision));
    2453           5 :                     json_object_array_add(
    2454             :                         coordinates,
    2455             :                         json_object_new_double_with_precision(
    2456           5 :                             (env.MaxY + env.MinY) * 0.5, nPrecision));
    2457             : 
    2458           5 :                     if (bAddGeoJSONType)
    2459             :                     {
    2460           4 :                         json_object *geometry = json_object_new_object();
    2461           4 :                         json_object_object_add(poContainer, pszLastComponent,
    2462             :                                                geometry);
    2463           4 :                         json_object_object_add(geometry, "type",
    2464             :                                                json_object_new_string("Point"));
    2465           4 :                         json_object_object_add(geometry, "coordinates",
    2466             :                                                coordinates);
    2467             :                     }
    2468             :                     else
    2469             :                     {
    2470           1 :                         json_object_object_add(poContainer, pszLastComponent,
    2471             :                                                coordinates);
    2472             :                     }
    2473             :                 }
    2474             :                 else
    2475             :                 {
    2476           8 :                     if (m_bGeoShapeAsGeoJSON)
    2477             :                     {
    2478           7 :                         json_object *geometry = json_object_new_object();
    2479           7 :                         json_object_object_add(poContainer, pszLastComponent,
    2480             :                                                geometry);
    2481           7 :                         BuildGeoJSONGeometry(geometry, poGeom);
    2482             :                     }
    2483             :                     else
    2484             :                     {
    2485           1 :                         char *pszWKT = nullptr;
    2486           1 :                         poGeom->exportToWkt(&pszWKT);
    2487           1 :                         json_object_object_add(poContainer, pszLastComponent,
    2488             :                                                json_object_new_string(pszWKT));
    2489           1 :                         CPLFree(pszWKT);
    2490             :                     }
    2491             :                 }
    2492             :             }
    2493             :         }
    2494             : 
    2495          23 :         if (m_osMappingName == "FeatureCollection")
    2496             :         {
    2497          37 :             if (poFeature->GetGeomFieldCount() == 1 &&
    2498          17 :                 poFeature->GetGeomFieldRef(0))
    2499             :             {
    2500           7 :                 json_object_object_add(fieldObject, "type",
    2501             :                                        json_object_new_string("Feature"));
    2502             :             }
    2503             : 
    2504          40 :             std::vector<CPLString> aosPath;
    2505          20 :             aosPath.push_back("properties");
    2506          20 :             aosPath.push_back("dummy");
    2507          20 :             GetContainerForFeature(fieldObject, aosPath, oMap);
    2508             :         }
    2509             : 
    2510             :         // For every field (except _id)
    2511          23 :         int fieldCount = m_poFeatureDefn->GetFieldCount();
    2512         115 :         for (int i = 1; i < fieldCount; i++)
    2513             :         {
    2514          92 :             if (!poFeature->IsFieldSet(i))
    2515             :             {
    2516          59 :                 continue;
    2517             :             }
    2518             : 
    2519             :             json_object *poContainer =
    2520          33 :                 GetContainerForFeature(fieldObject, m_aaosFieldPaths[i], oMap);
    2521          33 :             const char *pszLastComponent = m_aaosFieldPaths[i].back();
    2522             : 
    2523          33 :             if (poFeature->IsFieldNull(i))
    2524             :             {
    2525           1 :                 json_object_object_add(poContainer, pszLastComponent, nullptr);
    2526           1 :                 continue;
    2527             :             }
    2528             : 
    2529          32 :             switch (m_poFeatureDefn->GetFieldDefn(i)->GetType())
    2530             :             {
    2531           5 :                 case OFTInteger:
    2532           5 :                     if (m_poFeatureDefn->GetFieldDefn(i)->GetSubType() ==
    2533             :                         OFSTBoolean)
    2534           2 :                         json_object_object_add(
    2535             :                             poContainer, pszLastComponent,
    2536             :                             json_object_new_boolean(
    2537             :                                 poFeature->GetFieldAsInteger(i)));
    2538             :                     else
    2539           3 :                         json_object_object_add(
    2540             :                             poContainer, pszLastComponent,
    2541             :                             json_object_new_int(
    2542             :                                 poFeature->GetFieldAsInteger(i)));
    2543           5 :                     break;
    2544           2 :                 case OFTInteger64:
    2545           2 :                     json_object_object_add(
    2546             :                         poContainer, pszLastComponent,
    2547             :                         json_object_new_int64(
    2548           2 :                             poFeature->GetFieldAsInteger64(i)));
    2549           2 :                     break;
    2550           2 :                 case OFTReal:
    2551           2 :                     json_object_object_add(
    2552             :                         poContainer, pszLastComponent,
    2553             :                         json_object_new_double_with_significant_figures(
    2554             :                             poFeature->GetFieldAsDouble(i), -1));
    2555           2 :                     break;
    2556           2 :                 case OFTIntegerList:
    2557             :                 {
    2558           2 :                     int nCount = 0;
    2559             :                     const int *panValues =
    2560           2 :                         poFeature->GetFieldAsIntegerList(i, &nCount);
    2561           2 :                     json_object *poArray = json_object_new_array();
    2562           6 :                     for (int j = 0; j < nCount; j++)
    2563           4 :                         json_object_array_add(
    2564           4 :                             poArray, json_object_new_int(panValues[j]));
    2565           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2566             :                                            poArray);
    2567           2 :                     break;
    2568             :                 }
    2569           2 :                 case OFTInteger64List:
    2570             :                 {
    2571           2 :                     int nCount = 0;
    2572             :                     const GIntBig *panValues =
    2573           2 :                         poFeature->GetFieldAsInteger64List(i, &nCount);
    2574           2 :                     json_object *poArray = json_object_new_array();
    2575           6 :                     for (int j = 0; j < nCount; j++)
    2576           4 :                         json_object_array_add(
    2577           4 :                             poArray, json_object_new_int64(panValues[j]));
    2578           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2579             :                                            poArray);
    2580           2 :                     break;
    2581             :                 }
    2582           2 :                 case OFTRealList:
    2583             :                 {
    2584           2 :                     int nCount = 0;
    2585             :                     const double *padfValues =
    2586           2 :                         poFeature->GetFieldAsDoubleList(i, &nCount);
    2587           2 :                     json_object *poArray = json_object_new_array();
    2588           6 :                     for (int j = 0; j < nCount; j++)
    2589           4 :                         json_object_array_add(
    2590             :                             poArray,
    2591             :                             json_object_new_double_with_significant_figures(
    2592           4 :                                 padfValues[j], -1));
    2593           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2594             :                                            poArray);
    2595           2 :                     break;
    2596             :                 }
    2597           2 :                 case OFTStringList:
    2598             :                 {
    2599           2 :                     char **papszValues = poFeature->GetFieldAsStringList(i);
    2600           2 :                     json_object *poArray = json_object_new_array();
    2601           6 :                     for (int j = 0; papszValues[j] != nullptr; j++)
    2602           4 :                         json_object_array_add(
    2603           4 :                             poArray, json_object_new_string(papszValues[j]));
    2604           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2605             :                                            poArray);
    2606           2 :                     break;
    2607             :                 }
    2608           2 :                 case OFTBinary:
    2609             :                 {
    2610           2 :                     int nCount = 0;
    2611           2 :                     GByte *pabyVal = poFeature->GetFieldAsBinary(i, &nCount);
    2612           2 :                     char *pszVal = CPLBase64Encode(nCount, pabyVal);
    2613           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2614             :                                            json_object_new_string(pszVal));
    2615           2 :                     CPLFree(pszVal);
    2616           2 :                     break;
    2617             :                 }
    2618           3 :                 case OFTDateTime:
    2619             :                 {
    2620           3 :                     int nYear = 0;
    2621           3 :                     int nMonth = 0;
    2622           3 :                     int nDay = 0;
    2623           3 :                     int nHour = 0;
    2624           3 :                     int nMin = 0;
    2625           3 :                     int nTZ = 0;
    2626           3 :                     float fSec = 0.0f;
    2627           3 :                     poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay,
    2628             :                                                   &nHour, &nMin, &fSec, &nTZ);
    2629           3 :                     if (nTZ == 0)
    2630             :                     {
    2631           2 :                         json_object_object_add(
    2632             :                             poContainer, pszLastComponent,
    2633             :                             json_object_new_string(CPLSPrintf(
    2634             :                                 "%04d/%02d/%02d %02d:%02d:%06.3f", nYear,
    2635             :                                 nMonth, nDay, nHour, nMin, fSec)));
    2636             :                     }
    2637             :                     else
    2638             :                     {
    2639           1 :                         const int TZOffset = std::abs(nTZ - 100) * 15;
    2640           1 :                         const int TZHour = TZOffset / 60;
    2641           1 :                         const int TZMinute = TZOffset - TZHour * 60;
    2642           1 :                         json_object_object_add(
    2643             :                             poContainer, pszLastComponent,
    2644             :                             json_object_new_string(CPLSPrintf(
    2645             :                                 "%04d/%02d/%02d %02d:%02d:%06.3f%c%02d:%02d",
    2646             :                                 nYear, nMonth, nDay, nHour, nMin, fSec,
    2647           1 :                                 (nTZ >= 100) ? '+' : '-', TZHour, TZMinute)));
    2648             :                     }
    2649           3 :                     break;
    2650             :                 }
    2651          10 :                 default:
    2652             :                 {
    2653          10 :                     const char *pszVal = poFeature->GetFieldAsString(i);
    2654          10 :                     json_object_object_add(poContainer, pszLastComponent,
    2655             :                                            json_object_new_string(pszVal));
    2656             :                 }
    2657             :             }
    2658             :         }
    2659             : 
    2660             :         // Build the field string
    2661          23 :         fields = json_object_to_json_string(fieldObject);
    2662          23 :         json_object_put(fieldObject);
    2663             :     }
    2664             : 
    2665          24 :     return fields;
    2666             : }
    2667             : 
    2668             : /************************************************************************/
    2669             : /*                          ICreateFeature()                            */
    2670             : /************************************************************************/
    2671             : 
    2672          20 : OGRErr OGRElasticLayer::ICreateFeature(OGRFeature *poFeature)
    2673             : {
    2674          20 :     if (m_poDS->GetAccess() != GA_Update)
    2675             :     {
    2676           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2677             :                  "Dataset opened in read-only mode");
    2678           1 :         return OGRERR_FAILURE;
    2679             :     }
    2680             : 
    2681          19 :     FinalizeFeatureDefn();
    2682             : 
    2683          19 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    2684           1 :         return OGRERR_FAILURE;
    2685             : 
    2686          18 :     if (!m_osWriteMapFilename.empty())
    2687           2 :         return OGRERR_NONE;
    2688             : 
    2689          16 :     if (poFeature->GetFID() < 0)
    2690             :     {
    2691          14 :         if (m_nNextFID < 0)
    2692           1 :             m_nNextFID = GetFeatureCount(FALSE);
    2693          14 :         poFeature->SetFID(++m_nNextFID);
    2694             :     }
    2695             : 
    2696          32 :     CPLString osFields(BuildJSonFromFeature(poFeature));
    2697             : 
    2698          16 :     const char *pszId = nullptr;
    2699          16 :     if (poFeature->IsFieldSetAndNotNull(0) && !m_bIgnoreSourceID)
    2700           1 :         pszId = poFeature->GetFieldAsString(0);
    2701             : 
    2702             :     // Check to see if we're using bulk uploading
    2703          16 :     if (m_nBulkUpload > 0)
    2704             :     {
    2705             :         m_osBulkContent +=
    2706           7 :             CPLSPrintf("{\"index\" :{\"_index\":\"%s\"", m_osIndexName.c_str());
    2707           7 :         if (m_poDS->m_nMajorVersion < 7)
    2708             :             m_osBulkContent +=
    2709           6 :                 CPLSPrintf(", \"_type\":\"%s\"", m_osMappingName.c_str());
    2710           7 :         if (pszId)
    2711           0 :             m_osBulkContent += CPLSPrintf(",\"_id\":\"%s\"", pszId);
    2712           7 :         m_osBulkContent += "}}\n" + osFields + "\n\n";
    2713             : 
    2714             :         // Only push the data if we are over our bulk upload limit
    2715           7 :         if ((int)m_osBulkContent.length() > m_nBulkUpload)
    2716             :         {
    2717           0 :             if (!PushIndex())
    2718             :             {
    2719           0 :                 return OGRERR_FAILURE;
    2720             :             }
    2721             :         }
    2722             :     }
    2723             :     else
    2724             :     {
    2725             :         // Fall back to using single item upload for every feature.
    2726           9 :         CPLString osURL(BuildMappingURL(false));
    2727           9 :         if (pszId)
    2728           1 :             osURL += CPLSPrintf("/%s", pszId);
    2729           9 :         json_object *poRes = m_poDS->RunRequest(osURL, osFields);
    2730           9 :         if (poRes == nullptr)
    2731             :         {
    2732           1 :             return OGRERR_FAILURE;
    2733             :         }
    2734           8 :         if (pszId == nullptr)
    2735             :         {
    2736           7 :             json_object *poId = CPL_json_object_object_get(poRes, "_id");
    2737           8 :             if (poId != nullptr &&
    2738           1 :                 json_object_get_type(poId) == json_type_string)
    2739             :             {
    2740           1 :                 pszId = json_object_get_string(poId);
    2741           1 :                 poFeature->SetField(0, pszId);
    2742             :             }
    2743             :         }
    2744           8 :         json_object_put(poRes);
    2745             :     }
    2746             : 
    2747          15 :     return OGRERR_NONE;
    2748             : }
    2749             : 
    2750             : /************************************************************************/
    2751             : /*                           ISetFeature()                              */
    2752             : /************************************************************************/
    2753             : 
    2754           6 : OGRErr OGRElasticLayer::ISetFeature(OGRFeature *poFeature)
    2755             : {
    2756           6 :     if (m_poDS->GetAccess() != GA_Update)
    2757             :     {
    2758           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2759             :                  "Dataset opened in read-only mode");
    2760           1 :         return OGRERR_FAILURE;
    2761             :     }
    2762             : 
    2763           5 :     FinalizeFeatureDefn();
    2764             : 
    2765           5 :     if (!poFeature->IsFieldSetAndNotNull(0))
    2766             :     {
    2767           1 :         CPLError(CE_Failure, CPLE_AppDefined, "_id field not set");
    2768           1 :         return OGRERR_FAILURE;
    2769             :     }
    2770           4 :     if (poFeature->GetFID() < 0 && !m_osFID.empty())
    2771             :     {
    2772           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid FID");
    2773           0 :         return OGRERR_FAILURE;
    2774             :     }
    2775             : 
    2776           4 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    2777           0 :         return OGRERR_FAILURE;
    2778           4 :     PushIndex();
    2779             : 
    2780           8 :     CPLString osFields(BuildJSonFromFeature(poFeature));
    2781             : 
    2782             :     // TODO? we should theoretically detect if the provided _id doesn't exist
    2783             :     CPLString osURL(
    2784           8 :         CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str()));
    2785           4 :     if (m_poDS->m_nMajorVersion < 7)
    2786           4 :         osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    2787           4 :     osURL += CPLSPrintf("/%s", poFeature->GetFieldAsString(0));
    2788           4 :     json_object *poRes = m_poDS->RunRequest(osURL, osFields);
    2789           4 :     if (poRes == nullptr)
    2790             :     {
    2791           1 :         return OGRERR_FAILURE;
    2792             :     }
    2793             :     // CPLDebug("ES", "SetFeature(): %s", json_object_to_json_string(poRes));
    2794           3 :     json_object_put(poRes);
    2795             : 
    2796           3 :     return OGRERR_NONE;
    2797             : }
    2798             : 
    2799             : /************************************************************************/
    2800             : /*                          IUpsertFeature()                            */
    2801             : /************************************************************************/
    2802             : 
    2803           5 : OGRErr OGRElasticLayer::IUpsertFeature(OGRFeature *poFeature)
    2804             : {
    2805           5 :     if (m_poDS->GetAccess() != GA_Update)
    2806             :     {
    2807           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2808             :                  "Dataset opened in read-only mode");
    2809           1 :         return OGRERR_FAILURE;
    2810             :     }
    2811             : 
    2812           4 :     FinalizeFeatureDefn();
    2813             : 
    2814           4 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    2815           0 :         return OGRERR_FAILURE;
    2816             : 
    2817           4 :     if (!m_osWriteMapFilename.empty())
    2818           0 :         return OGRERR_NONE;
    2819             : 
    2820           4 :     if (poFeature->GetFID() < 0)
    2821             :     {
    2822           1 :         if (m_nNextFID < 0)
    2823           0 :             m_nNextFID = GetFeatureCount(FALSE);
    2824           1 :         poFeature->SetFID(++m_nNextFID);
    2825             :     }
    2826             : 
    2827           8 :     CPLString osFields(BuildJSonFromFeature(poFeature));
    2828             : 
    2829           4 :     const char *pszId = nullptr;
    2830           4 :     if (poFeature->IsFieldSetAndNotNull(0))
    2831             :     {
    2832           4 :         pszId = poFeature->GetFieldAsString(0);
    2833             :     }
    2834             :     else
    2835             :     {
    2836           0 :         return OGRERR_FAILURE;
    2837             :     }
    2838             : 
    2839             :     // Check to see if we're using bulk uploading
    2840           4 :     if (m_nBulkUpload > 0)
    2841             :     {
    2842             :         m_osBulkContent +=
    2843             :             CPLSPrintf("{\"update\":{\"_index\":\"%s\",\"_id\":\"%s\"",
    2844           2 :                        m_osIndexName.c_str(), pszId);
    2845           2 :         if (m_poDS->m_nMajorVersion < 7)
    2846             :         {
    2847             :             m_osBulkContent +=
    2848           2 :                 CPLSPrintf(", \"_type\":\"%s\"", m_osMappingName.c_str());
    2849             :         }
    2850             :         m_osBulkContent +=
    2851           2 :             "}}\n{\"doc\":" + osFields + ",\"doc_as_upsert\":true}\n\n";
    2852             : 
    2853             :         // Only push the data if we are over our bulk upload limit
    2854           2 :         if (m_osBulkContent.length() > static_cast<size_t>(m_nBulkUpload))
    2855             :         {
    2856           0 :             if (!PushIndex())
    2857             :             {
    2858           0 :                 return OGRERR_FAILURE;
    2859             :             }
    2860             :         }
    2861             :     }
    2862             :     else
    2863             :     {
    2864             :         // Fall back to using single item upload for every feature.
    2865           2 :         CPLString osURL(BuildMappingURL(false));
    2866           2 :         if (m_poDS->m_nMajorVersion < 7)
    2867             :         {
    2868           2 :             osURL += CPLSPrintf("/%s/_update", pszId);
    2869             :         }
    2870             :         else
    2871             :         {
    2872           0 :             osURL += CPLSPrintf("/_update/%s", pszId);
    2873             :         }
    2874             : 
    2875             :         const CPLString osUpdate =
    2876           2 :             CPLSPrintf("{\"doc\":%s,\"doc_as_upsert\":true}", osFields.c_str());
    2877           2 :         const CPLString osMethod = "POST";
    2878           2 :         if (!m_poDS->UploadFile(osURL, osUpdate, osMethod))
    2879             :         {
    2880           0 :             return OGRERR_FAILURE;
    2881             :         }
    2882             :     }
    2883             : 
    2884           4 :     return OGRERR_NONE;
    2885             : }
    2886             : 
    2887             : /************************************************************************/
    2888             : /*                             PushIndex()                              */
    2889             : /************************************************************************/
    2890             : 
    2891          63 : bool OGRElasticLayer::PushIndex()
    2892             : {
    2893          63 :     if (m_osBulkContent.empty())
    2894             :     {
    2895          56 :         return true;
    2896             :     }
    2897             : 
    2898          14 :     const bool bRet = m_poDS->UploadFile(
    2899           7 :         CPLSPrintf("%s/_bulk", m_poDS->GetURL()), m_osBulkContent);
    2900           7 :     m_osBulkContent.clear();
    2901             : 
    2902           7 :     return bRet;
    2903             : }
    2904             : 
    2905             : /************************************************************************/
    2906             : /*                            CreateField()                             */
    2907             : /************************************************************************/
    2908             : 
    2909          17 : OGRErr OGRElasticLayer::CreateField(const OGRFieldDefn *poFieldDefn,
    2910             :                                     int /*bApproxOK*/)
    2911             : {
    2912          17 :     if (m_poDS->GetAccess() != GA_Update)
    2913             :     {
    2914           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2915             :                  "Dataset opened in read-only mode");
    2916           1 :         return OGRERR_FAILURE;
    2917             :     }
    2918             : 
    2919          16 :     FinalizeFeatureDefn();
    2920          16 :     ResetReading();
    2921             : 
    2922          16 :     if (m_poFeatureDefn->GetFieldIndex(poFieldDefn->GetNameRef()) >= 0)
    2923             :     {
    2924           0 :         if (!EQUAL(poFieldDefn->GetNameRef(), "_id") &&
    2925           0 :             !EQUAL(poFieldDefn->GetNameRef(), "_json"))
    2926             :         {
    2927           0 :             CPLError(
    2928             :                 CE_Failure, CPLE_AppDefined,
    2929             :                 "CreateField() called with an already existing field name: %s",
    2930             :                 poFieldDefn->GetNameRef());
    2931             :         }
    2932           0 :         return OGRERR_FAILURE;
    2933             :     }
    2934             : 
    2935          16 :     std::vector<CPLString> aosPath;
    2936          16 :     if (m_osMappingName == "FeatureCollection")
    2937          14 :         aosPath.push_back("properties");
    2938             : 
    2939          16 :     if (m_bDotAsNestedField)
    2940             :     {
    2941             :         char **papszTokens =
    2942          16 :             CSLTokenizeString2(poFieldDefn->GetNameRef(), ".", 0);
    2943          33 :         for (int i = 0; papszTokens[i]; i++)
    2944          17 :             aosPath.push_back(papszTokens[i]);
    2945          16 :         CSLDestroy(papszTokens);
    2946             :     }
    2947             :     else
    2948           0 :         aosPath.push_back(poFieldDefn->GetNameRef());
    2949             : 
    2950          16 :     AddFieldDefn(poFieldDefn->GetNameRef(), poFieldDefn->GetType(), aosPath,
    2951             :                  poFieldDefn->GetSubType());
    2952             : 
    2953          16 :     m_bSerializeMapping = true;
    2954             : 
    2955          16 :     return OGRERR_NONE;
    2956             : }
    2957             : 
    2958             : /************************************************************************/
    2959             : /*                           CreateGeomField()                          */
    2960             : /************************************************************************/
    2961             : 
    2962          20 : OGRErr OGRElasticLayer::CreateGeomField(const OGRGeomFieldDefn *poFieldIn,
    2963             :                                         int /*bApproxOK*/)
    2964             : 
    2965             : {
    2966          20 :     if (m_poDS->GetAccess() != GA_Update)
    2967             :     {
    2968           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2969             :                  "Dataset opened in read-only mode");
    2970           1 :         return OGRERR_FAILURE;
    2971             :     }
    2972             : 
    2973          19 :     FinalizeFeatureDefn();
    2974          19 :     ResetReading();
    2975             : 
    2976          19 :     if (m_poFeatureDefn->GetGeomFieldIndex(poFieldIn->GetNameRef()) >= 0)
    2977             :     {
    2978           0 :         CPLError(
    2979             :             CE_Failure, CPLE_AppDefined,
    2980             :             "CreateGeomField() called with an already existing field name: %s",
    2981             :             poFieldIn->GetNameRef());
    2982           0 :         return OGRERR_FAILURE;
    2983             :     }
    2984             : 
    2985          38 :     OGRGeomFieldDefn oFieldDefn(poFieldIn);
    2986          19 :     auto poSRSOri = poFieldIn->GetSpatialRef();
    2987          19 :     if (poSRSOri)
    2988             :     {
    2989          17 :         auto poSRS = poSRSOri->Clone();
    2990          17 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2991          17 :         oFieldDefn.SetSpatialRef(poSRS);
    2992          17 :         poSRS->Release();
    2993             :     }
    2994          19 :     if (EQUAL(oFieldDefn.GetNameRef(), ""))
    2995           0 :         oFieldDefn.SetName("geometry");
    2996             : 
    2997          19 :     std::vector<CPLString> aosPath;
    2998          19 :     if (m_bDotAsNestedField)
    2999             :     {
    3000             :         char **papszTokens =
    3001          19 :             CSLTokenizeString2(oFieldDefn.GetNameRef(), ".", 0);
    3002          39 :         for (int i = 0; papszTokens[i]; i++)
    3003          20 :             aosPath.push_back(papszTokens[i]);
    3004          19 :         CSLDestroy(papszTokens);
    3005             :     }
    3006             :     else
    3007           0 :         aosPath.push_back(oFieldDefn.GetNameRef());
    3008             : 
    3009          37 :     if (m_eGeomTypeMapping == ES_GEOMTYPE_GEO_SHAPE ||
    3010          35 :         (m_eGeomTypeMapping == ES_GEOMTYPE_AUTO &&
    3011          17 :          poFieldIn->GetType() != wkbPoint))
    3012             :     {
    3013          16 :         m_abIsGeoPoint.push_back(FALSE);
    3014             :     }
    3015             :     else
    3016             :     {
    3017           3 :         m_abIsGeoPoint.push_back(TRUE);
    3018           3 :         aosPath.push_back("coordinates");
    3019             :     }
    3020             : 
    3021          19 :     m_aaosGeomFieldPaths.push_back(aosPath);
    3022             : 
    3023          19 :     m_aosMapToGeomFieldIndex[BuildPathFromArray(aosPath)] =
    3024          19 :         m_poFeatureDefn->GetGeomFieldCount();
    3025             : 
    3026          19 :     m_poFeatureDefn->AddGeomFieldDefn(&oFieldDefn);
    3027             : 
    3028          19 :     OGRCoordinateTransformation *poCT = nullptr;
    3029          19 :     if (oFieldDefn.GetSpatialRef() != nullptr)
    3030             :     {
    3031          34 :         OGRSpatialReference oSRS_WGS84;
    3032          17 :         oSRS_WGS84.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
    3033          17 :         oSRS_WGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    3034          17 :         if (!oSRS_WGS84.IsSame(oFieldDefn.GetSpatialRef()))
    3035             :         {
    3036           1 :             poCT = OGRCreateCoordinateTransformation(oFieldDefn.GetSpatialRef(),
    3037             :                                                      &oSRS_WGS84);
    3038           1 :             if (poCT == nullptr)
    3039             :             {
    3040           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3041             :                          "On-the-fly reprojection to WGS84 long/lat would be "
    3042             :                          "needed, but instantiation of transformer failed");
    3043             :             }
    3044             :         }
    3045             :     }
    3046             :     else
    3047             :     {
    3048           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    3049             :                  "No SRS given for geometry column %s. SRS is assumed to "
    3050             :                  "be EPSG:4326 (WGS84 long/lat)",
    3051             :                  oFieldDefn.GetNameRef());
    3052             :     }
    3053             : 
    3054          19 :     m_apoCT.push_back(poCT);
    3055             : 
    3056          19 :     m_bSerializeMapping = true;
    3057             : 
    3058          19 :     return OGRERR_NONE;
    3059             : }
    3060             : 
    3061             : /************************************************************************/
    3062             : /*                           TestCapability()                           */
    3063             : /************************************************************************/
    3064             : 
    3065          67 : int OGRElasticLayer::TestCapability(const char *pszCap)
    3066             : {
    3067          67 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    3068           1 :         return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr;
    3069             : 
    3070          66 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    3071          27 :         return TRUE;
    3072             : 
    3073          39 :     else if (EQUAL(pszCap, OLCSequentialWrite) ||
    3074          38 :              EQUAL(pszCap, OLCUpsertFeature) || EQUAL(pszCap, OLCRandomWrite))
    3075           1 :         return m_poDS->GetAccess() == GA_Update;
    3076          38 :     else if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCCreateGeomField))
    3077           2 :         return m_poDS->GetAccess() == GA_Update;
    3078             :     else
    3079          36 :         return FALSE;
    3080             : }
    3081             : 
    3082             : /************************************************************************/
    3083             : /*                   AddTimeoutTerminateAfterToURL()                    */
    3084             : /************************************************************************/
    3085             : 
    3086          16 : void OGRElasticLayer::AddTimeoutTerminateAfterToURL(CPLString &osURL)
    3087             : {
    3088          16 :     if (!m_osSingleQueryTimeout.empty())
    3089           5 :         osURL += "&timeout=" + m_osSingleQueryTimeout;
    3090          16 :     if (!m_osSingleQueryTerminateAfter.empty())
    3091           5 :         osURL += "&terminate_after=" + m_osSingleQueryTerminateAfter;
    3092          16 : }
    3093             : 
    3094             : /************************************************************************/
    3095             : /*                          GetFeatureCount()                           */
    3096             : /************************************************************************/
    3097             : 
    3098          12 : GIntBig OGRElasticLayer::GetFeatureCount(int bForce)
    3099             : {
    3100          12 :     if (m_bFilterMustBeClientSideEvaluated)
    3101             :     {
    3102           0 :         m_bUseSingleQueryParams = true;
    3103           0 :         const auto nRet = OGRLayer::GetFeatureCount(bForce);
    3104           0 :         m_bUseSingleQueryParams = false;
    3105           0 :         return nRet;
    3106             :     }
    3107             : 
    3108          12 :     json_object *poResponse = nullptr;
    3109          24 :     CPLString osURL(CPLSPrintf("%s", m_poDS->GetURL()));
    3110          24 :     CPLString osFilter = "";
    3111          12 :     if (!m_osESSearch.empty())
    3112             :     {
    3113           1 :         if (m_osESSearch[0] != '{')
    3114           0 :             return OGRLayer::GetFeatureCount(bForce);
    3115           1 :         osURL += "/_search?pretty";
    3116           1 :         osFilter = "{ \"size\": 0 ";
    3117           1 :         if (m_osESSearch == "{}")
    3118           0 :             osFilter += '}';
    3119             :         else
    3120           1 :             osFilter += ", " + m_osESSearch.substr(1);
    3121             :     }
    3122          11 :     else if ((m_poSpatialFilter && m_osJSONFilter.empty()) || m_poJSONFilter)
    3123             :     {
    3124           3 :         osFilter = BuildQuery(true);
    3125           3 :         osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
    3126           3 :         if (m_poDS->m_nMajorVersion < 7)
    3127           2 :             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3128           3 :         if (m_poDS->m_nMajorVersion >= 5 && m_osSingleQueryTimeout.empty())
    3129             :         {
    3130           1 :             osURL += "/_count?pretty";
    3131             :         }
    3132             :         else
    3133             :         {
    3134           2 :             osURL += "/_search?pretty";
    3135             :         }
    3136             :     }
    3137           8 :     else if (!m_osJSONFilter.empty())
    3138             :     {
    3139           2 :         osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
    3140           2 :         if (m_poDS->m_nMajorVersion < 7)
    3141           1 :             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3142           2 :         osURL += "/_search?pretty";
    3143           2 :         osFilter = ("{ \"size\": 0, " + m_osJSONFilter.substr(1));
    3144             :     }
    3145             :     else
    3146             :     {
    3147           6 :         osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
    3148           6 :         if (m_poDS->m_nMajorVersion < 7)
    3149           5 :             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3150           6 :         if (m_osSingleQueryTimeout.empty())
    3151             :         {
    3152           5 :             osURL += "/_count?pretty";
    3153             :         }
    3154             :         else
    3155             :         {
    3156           1 :             osFilter = "{ \"size\": 0 }";
    3157           1 :             osURL += CPLSPrintf("/_search?pretty");
    3158             :         }
    3159             :     }
    3160          12 :     AddTimeoutTerminateAfterToURL(osURL);
    3161             : 
    3162          12 :     poResponse = m_poDS->RunRequest(osURL.c_str(), osFilter.c_str());
    3163             : 
    3164          12 :     json_object *poCount = json_ex_get_object_by_path(poResponse, "hits.count");
    3165          12 :     if (poCount == nullptr)
    3166             :     {
    3167             :         // For _search request
    3168          10 :         poCount = json_ex_get_object_by_path(poResponse, "hits.total");
    3169          10 :         if (poCount && json_object_get_type(poCount) == json_type_object)
    3170             :         {
    3171             :             // Since ES 7.0
    3172           4 :             poCount = json_ex_get_object_by_path(poCount, "value");
    3173             :         }
    3174             :     }
    3175          12 :     if (poCount == nullptr)
    3176             :     {
    3177             :         // For _count request
    3178           4 :         poCount = json_ex_get_object_by_path(poResponse, "count");
    3179             :     }
    3180          12 :     if (poCount == nullptr || json_object_get_type(poCount) != json_type_int)
    3181             :     {
    3182           3 :         json_object_put(poResponse);
    3183           3 :         CPLDebug("ES", "Cannot find hits in GetFeatureCount() response. "
    3184             :                        "Falling back to slow implementation");
    3185           3 :         m_bUseSingleQueryParams = true;
    3186           3 :         const auto nRet = OGRLayer::GetFeatureCount(bForce);
    3187           3 :         m_bUseSingleQueryParams = false;
    3188           3 :         return nRet;
    3189             :     }
    3190             : 
    3191           9 :     GIntBig nCount = json_object_get_int64(poCount);
    3192           9 :     json_object_put(poResponse);
    3193           9 :     return nCount;
    3194             : }
    3195             : 
    3196             : /************************************************************************/
    3197             : /*                            GetValue()                                */
    3198             : /************************************************************************/
    3199             : 
    3200          28 : json_object *OGRElasticLayer::GetValue(int nFieldIdx, swq_expr_node *poValNode)
    3201             : {
    3202          28 :     json_object *poVal = nullptr;
    3203          28 :     if (poValNode->field_type == SWQ_FLOAT)
    3204           1 :         poVal = json_object_new_double(poValNode->float_value);
    3205          27 :     else if (poValNode->field_type == SWQ_INTEGER ||
    3206          26 :              poValNode->field_type == SWQ_INTEGER64)
    3207           2 :         poVal = json_object_new_int64(poValNode->int_value);
    3208          25 :     else if (poValNode->field_type == SWQ_STRING)
    3209          23 :         poVal = json_object_new_string(poValNode->string_value);
    3210           2 :     else if (poValNode->field_type == SWQ_TIMESTAMP)
    3211             :     {
    3212           2 :         int nYear = 0;
    3213           2 :         int nMonth = 0;
    3214           2 :         int nDay = 0;
    3215           2 :         int nHour = 0;
    3216           2 :         int nMinute = 0;
    3217           2 :         float fSecond = 0;
    3218           4 :         if (sscanf(poValNode->string_value, "%04d/%02d/%02d %02d:%02d:%f",
    3219           2 :                    &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) >= 3 ||
    3220           0 :             sscanf(poValNode->string_value, "%04d-%02d-%02dT%02d:%02d:%f",
    3221             :                    &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) >= 3)
    3222             :         {
    3223             :             OGRFieldType eType(
    3224           2 :                 m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType());
    3225           2 :             if (eType == OFTDateTime)
    3226           2 :                 poVal = json_object_new_string(
    3227             :                     CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02.03f", nYear,
    3228             :                                nMonth, nDay, nHour, nMinute, fSecond));
    3229           0 :             else if (eType == OFTDate)
    3230           0 :                 poVal = json_object_new_string(
    3231             :                     CPLSPrintf("%04d/%02d/%02d", nYear, nMonth, nDay));
    3232             :             else
    3233           0 :                 poVal = json_object_new_string(
    3234             :                     CPLSPrintf("%02d:%02d:%02.03f", nHour, nMinute, fSecond));
    3235             :         }
    3236             :         else
    3237             :         {
    3238           0 :             return nullptr;
    3239             :         }
    3240             :     }
    3241             :     else
    3242             :     {
    3243           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unhandled type: %d",
    3244           0 :                  poValNode->field_type);
    3245             :     }
    3246          28 :     return poVal;
    3247             : }
    3248             : 
    3249             : /************************************************************************/
    3250             : /*                      OGRESGetFieldIndexFromSQL()                     */
    3251             : /************************************************************************/
    3252             : 
    3253          31 : static int OGRESGetFieldIndexFromSQL(const swq_expr_node *poNode)
    3254             : {
    3255          31 :     if (poNode->eNodeType == SNT_COLUMN)
    3256          27 :         return poNode->field_index;
    3257             : 
    3258           4 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_CAST &&
    3259           1 :         poNode->nSubExprCount >= 1 &&
    3260           1 :         poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN)
    3261           1 :         return poNode->papoSubExpr[0]->field_index;
    3262             : 
    3263           3 :     return -1;
    3264             : }
    3265             : 
    3266             : /************************************************************************/
    3267             : /*                        TranslateSQLToFilter()                        */
    3268             : /************************************************************************/
    3269             : 
    3270          40 : json_object *OGRElasticLayer::TranslateSQLToFilter(swq_expr_node *poNode)
    3271             : {
    3272          40 :     if (poNode->eNodeType == SNT_OPERATION)
    3273             :     {
    3274          40 :         int nFieldIdx = 0;
    3275          40 :         CPL_IGNORE_RET_VAL(nFieldIdx);  // to make cppcheck happy
    3276          40 :         if (poNode->nOperation == SWQ_AND && poNode->nSubExprCount == 2)
    3277             :         {
    3278             :             // For AND, we can deal with a failure in one of the branch
    3279             :             // since client-side will do that extra filtering
    3280             :             json_object *poFilter1 =
    3281           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[0]);
    3282             :             json_object *poFilter2 =
    3283           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[1]);
    3284           3 :             if (poFilter1 && poFilter2)
    3285             :             {
    3286           1 :                 json_object *poRet = json_object_new_object();
    3287           1 :                 json_object *poBool = json_object_new_object();
    3288           1 :                 json_object_object_add(poRet, "bool", poBool);
    3289           1 :                 json_object *poMust = json_object_new_array();
    3290           1 :                 json_object_object_add(poBool, "must", poMust);
    3291           1 :                 json_object_array_add(poMust, poFilter1);
    3292           1 :                 json_object_array_add(poMust, poFilter2);
    3293          35 :                 return poRet;
    3294             :             }
    3295           2 :             else if (poFilter1)
    3296           1 :                 return poFilter1;
    3297             :             else
    3298           1 :                 return poFilter2;
    3299             :         }
    3300          37 :         else if (poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
    3301             :         {
    3302             :             json_object *poFilter1 =
    3303           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[0]);
    3304             :             json_object *poFilter2 =
    3305           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[1]);
    3306           3 :             if (poFilter1 && poFilter2)
    3307             :             {
    3308           2 :                 json_object *poRet = json_object_new_object();
    3309           2 :                 json_object *poBool = json_object_new_object();
    3310           2 :                 json_object_object_add(poRet, "bool", poBool);
    3311           2 :                 json_object *poShould = json_object_new_array();
    3312           2 :                 json_object_object_add(poBool, "should", poShould);
    3313           2 :                 json_object_array_add(poShould, poFilter1);
    3314           2 :                 json_object_array_add(poShould, poFilter2);
    3315           2 :                 return poRet;
    3316             :             }
    3317             :             else
    3318             :             {
    3319           1 :                 json_object_put(poFilter1);
    3320           1 :                 json_object_put(poFilter2);
    3321           1 :                 return nullptr;
    3322             :             }
    3323             :         }
    3324          34 :         else if (poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
    3325             :         {
    3326           6 :             if (poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
    3327           2 :                 poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
    3328           1 :                 poNode->papoSubExpr[0]->nSubExprCount == 1 &&
    3329           5 :                 poNode->papoSubExpr[0]->papoSubExpr[0]->field_index != 0 &&
    3330           1 :                 poNode->papoSubExpr[0]->papoSubExpr[0]->field_index <
    3331           1 :                     m_poFeatureDefn->GetFieldCount())
    3332             :             {
    3333           1 :                 json_object *poRet = json_object_new_object();
    3334           1 :                 json_object *poExists = json_object_new_object();
    3335             :                 CPLString osFieldName(BuildPathFromArray(
    3336             :                     m_aaosFieldPaths
    3337           1 :                         [poNode->papoSubExpr[0]->papoSubExpr[0]->field_index]));
    3338           1 :                 json_object_object_add(poExists, "field",
    3339             :                                        json_object_new_string(osFieldName));
    3340           1 :                 json_object_object_add(poRet, "exists", poExists);
    3341           1 :                 return poRet;
    3342             :             }
    3343             :             else
    3344             :             {
    3345             :                 json_object *poFilter =
    3346           1 :                     TranslateSQLToFilter(poNode->papoSubExpr[0]);
    3347           1 :                 if (poFilter)
    3348             :                 {
    3349           1 :                     json_object *poRet = json_object_new_object();
    3350           1 :                     json_object *poBool = json_object_new_object();
    3351           1 :                     json_object_object_add(poRet, "bool", poBool);
    3352           1 :                     json_object_object_add(poBool, "must_not", poFilter);
    3353           1 :                     return poRet;
    3354             :                 }
    3355             :                 else
    3356             :                 {
    3357           0 :                     return nullptr;
    3358             :                 }
    3359             :             }
    3360             :         }
    3361          65 :         else if (poNode->nOperation == SWQ_ISNULL &&
    3362           1 :                  poNode->nSubExprCount == 1 &&
    3363           1 :                  (nFieldIdx =
    3364          34 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3365           1 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3366             :         {
    3367           1 :             json_object *poRet = json_object_new_object();
    3368           1 :             json_object *poExists = json_object_new_object();
    3369             :             CPLString osFieldName(
    3370           1 :                 BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3371           1 :             json_object_object_add(poExists, "field",
    3372             :                                    json_object_new_string(osFieldName));
    3373           1 :             json_object *poBool = json_object_new_object();
    3374           1 :             json_object_object_add(poRet, "bool", poBool);
    3375           1 :             json_object *poMustNot = json_object_new_object();
    3376           1 :             json_object_object_add(poMustNot, "exists", poExists);
    3377           1 :             json_object_object_add(poBool, "must_not", poMustNot);
    3378           1 :             return poRet;
    3379             :         }
    3380          31 :         else if (poNode->nOperation == SWQ_NE)
    3381             :         {
    3382           1 :             poNode->nOperation = SWQ_EQ;
    3383           1 :             json_object *poFilter = TranslateSQLToFilter(poNode);
    3384           1 :             poNode->nOperation = SWQ_NE;
    3385           1 :             if (poFilter)
    3386             :             {
    3387           1 :                 json_object *poRet = json_object_new_object();
    3388           1 :                 json_object *poBool = json_object_new_object();
    3389           1 :                 json_object_object_add(poRet, "bool", poBool);
    3390           1 :                 json_object_object_add(poBool, "must_not", poFilter);
    3391           1 :                 return poRet;
    3392             :             }
    3393             :             else
    3394             :             {
    3395           0 :                 return nullptr;
    3396             :             }
    3397             :         }
    3398          16 :         else if (poNode->nOperation == SWQ_EQ && poNode->nSubExprCount == 2 &&
    3399          16 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
    3400          16 :                  (nFieldIdx =
    3401          62 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) >= 0 &&
    3402          13 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3403             :         {
    3404          13 :             json_object *poVal = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
    3405          13 :             if (poVal == nullptr)
    3406             :             {
    3407           0 :                 return nullptr;
    3408             :             }
    3409          13 :             json_object *poRet = json_object_new_object();
    3410          13 :             if (nFieldIdx == 0)
    3411             :             {
    3412           1 :                 json_object *poIds = json_object_new_object();
    3413           1 :                 json_object *poValues = json_object_new_array();
    3414           1 :                 json_object_object_add(poIds, "values", poValues);
    3415           1 :                 json_object_array_add(poValues, poVal);
    3416           1 :                 json_object_object_add(poRet, "ids", poIds);
    3417             :             }
    3418             :             else
    3419             :             {
    3420          12 :                 json_object *poTerm = json_object_new_object();
    3421             :                 CPLString osPath(
    3422          24 :                     BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3423          12 :                 bool bNotAnalyzed = true;
    3424          12 :                 if (poNode->papoSubExpr[1]->field_type == SWQ_STRING)
    3425             :                 {
    3426             :                     const char *pszFieldName =
    3427          12 :                         m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
    3428          12 :                     bNotAnalyzed = CSLFindString(m_papszNotAnalyzedFields,
    3429             :                                                  pszFieldName) >= 0;
    3430          12 :                     if (!bNotAnalyzed)
    3431             :                     {
    3432           3 :                         if (CSLFindString(m_papszFieldsWithRawValue,
    3433           3 :                                           pszFieldName) >= 0)
    3434             :                         {
    3435           1 :                             osPath += ".raw";
    3436           1 :                             bNotAnalyzed = true;
    3437             :                         }
    3438           2 :                         else if (!m_bFilterMustBeClientSideEvaluated)
    3439             :                         {
    3440           2 :                             m_bFilterMustBeClientSideEvaluated = true;
    3441           2 :                             CPLDebug("ES",
    3442             :                                      "Part or full filter will have to be "
    3443             :                                      "evaluated on "
    3444             :                                      "client side (equality test on a analyzed "
    3445             :                                      "field).");
    3446             :                         }
    3447             :                     }
    3448             :                 }
    3449          12 :                 json_object_object_add(poRet, bNotAnalyzed ? "term" : "match",
    3450             :                                        poTerm);
    3451          12 :                 json_object_object_add(poTerm, osPath, poVal);
    3452             : 
    3453          12 :                 if (!bNotAnalyzed && m_poDS->m_nMajorVersion < 2)
    3454             :                 {
    3455           0 :                     json_object *poNewRet = json_object_new_object();
    3456           0 :                     json_object_object_add(poNewRet, "query", poRet);
    3457           0 :                     poRet = poNewRet;
    3458             :                 }
    3459             :             }
    3460          13 :             return poRet;
    3461             :         }
    3462          50 :         else if ((poNode->nOperation == SWQ_LT ||
    3463          16 :                   poNode->nOperation == SWQ_LE ||
    3464          15 :                   poNode->nOperation == SWQ_GT ||
    3465          14 :                   poNode->nOperation == SWQ_GE) &&
    3466           5 :                  poNode->nSubExprCount == 2 &&
    3467           5 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
    3468           5 :                  (nFieldIdx =
    3469          39 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3470           5 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3471             :         {
    3472           5 :             json_object *poVal = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
    3473           5 :             if (poVal == nullptr)
    3474             :             {
    3475           0 :                 return nullptr;
    3476             :             }
    3477           5 :             json_object *poRet = json_object_new_object();
    3478           5 :             json_object *poRange = json_object_new_object();
    3479           5 :             json_object_object_add(poRet, "range", poRange);
    3480           5 :             json_object *poFieldConstraint = json_object_new_object();
    3481             :             CPLString osFieldName(
    3482           5 :                 BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3483           5 :             json_object_object_add(poRange, osFieldName, poFieldConstraint);
    3484           9 :             const char *pszOp = (poNode->nOperation == SWQ_LT)   ? "lt"
    3485           7 :                                 : (poNode->nOperation == SWQ_LE) ? "lte"
    3486           3 :                                 : (poNode->nOperation == SWQ_GT)
    3487           3 :                                     ? "gt"
    3488             :                                     :
    3489             :                                     /*(poNode->nOperation == SWQ_GE) ?*/ "gte";
    3490           5 :             json_object_object_add(poFieldConstraint, pszOp, poVal);
    3491           5 :             return poRet;
    3492             :         }
    3493          25 :         else if (poNode->nOperation == SWQ_BETWEEN &&
    3494           1 :                  poNode->nSubExprCount == 3 &&
    3495           1 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
    3496           1 :                  poNode->papoSubExpr[2]->eNodeType == SNT_CONSTANT &&
    3497           1 :                  (nFieldIdx =
    3498          14 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3499           1 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3500             :         {
    3501           1 :             json_object *poVal1 = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
    3502           1 :             if (poVal1 == nullptr)
    3503             :             {
    3504           0 :                 return nullptr;
    3505             :             }
    3506           1 :             json_object *poVal2 = GetValue(nFieldIdx, poNode->papoSubExpr[2]);
    3507           1 :             if (poVal2 == nullptr)
    3508             :             {
    3509           0 :                 json_object_put(poVal1);
    3510           0 :                 return nullptr;
    3511             :             }
    3512             : 
    3513           1 :             json_object *poRet = json_object_new_object();
    3514           1 :             json_object *poRange = json_object_new_object();
    3515           1 :             json_object_object_add(poRet, "range", poRange);
    3516           1 :             json_object *poFieldConstraint = json_object_new_object();
    3517             :             CPLString osFieldName(
    3518           1 :                 BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3519           1 :             json_object_object_add(poRange, osFieldName, poFieldConstraint);
    3520           1 :             json_object_object_add(poFieldConstraint, "gte", poVal1);
    3521           1 :             json_object_object_add(poFieldConstraint, "lte", poVal2);
    3522           1 :             return poRet;
    3523             :         }
    3524           4 :         else if (poNode->nOperation == SWQ_IN && poNode->nSubExprCount > 1 &&
    3525           4 :                  (nFieldIdx =
    3526          19 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) >= 0 &&
    3527           4 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3528             :         {
    3529           4 :             bool bAllConstant = true;
    3530          12 :             for (int i = 1; i < poNode->nSubExprCount; i++)
    3531             :             {
    3532           8 :                 if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT)
    3533             :                 {
    3534           0 :                     bAllConstant = false;
    3535           0 :                     break;
    3536             :                 }
    3537             :             }
    3538           4 :             if (bAllConstant)
    3539             :             {
    3540           4 :                 json_object *poRet = json_object_new_object();
    3541           4 :                 if (nFieldIdx == 0)
    3542             :                 {
    3543           1 :                     json_object *poIds = json_object_new_object();
    3544           1 :                     json_object *poValues = json_object_new_array();
    3545           1 :                     json_object_object_add(poIds, "values", poValues);
    3546           1 :                     json_object_object_add(poRet, "ids", poIds);
    3547           3 :                     for (int i = 1; i < poNode->nSubExprCount; i++)
    3548             :                     {
    3549             :                         json_object *poVal =
    3550           2 :                             GetValue(nFieldIdx, poNode->papoSubExpr[i]);
    3551           2 :                         if (poVal == nullptr)
    3552             :                         {
    3553           0 :                             json_object_put(poRet);
    3554           0 :                             return nullptr;
    3555             :                         }
    3556           2 :                         json_object_array_add(poValues, poVal);
    3557             :                     }
    3558             :                 }
    3559             :                 else
    3560             :                 {
    3561           3 :                     bool bNotAnalyzed = true;
    3562             :                     CPLString osPath(
    3563           3 :                         BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3564           3 :                     if (poNode->papoSubExpr[1]->field_type == SWQ_STRING)
    3565             :                     {
    3566             :                         const char *pszFieldName =
    3567           3 :                             m_poFeatureDefn->GetFieldDefn(nFieldIdx)
    3568           3 :                                 ->GetNameRef();
    3569           3 :                         bNotAnalyzed = CSLFindString(m_papszNotAnalyzedFields,
    3570             :                                                      pszFieldName) >= 0;
    3571           5 :                         if (!bNotAnalyzed &&
    3572           2 :                             CSLFindString(m_papszFieldsWithRawValue,
    3573             :                                           pszFieldName) >= 0)
    3574             :                         {
    3575           1 :                             osPath += ".raw";
    3576           1 :                             bNotAnalyzed = true;
    3577             :                         }
    3578             : 
    3579           3 :                         if (!bNotAnalyzed &&
    3580           1 :                             !m_bFilterMustBeClientSideEvaluated)
    3581             :                         {
    3582           1 :                             m_bFilterMustBeClientSideEvaluated = true;
    3583           1 :                             CPLDebug("ES",
    3584             :                                      "Part or full filter will have to be "
    3585             :                                      "evaluated on client side (IN test on a "
    3586             :                                      "analyzed field).");
    3587             :                         }
    3588             :                     }
    3589             : 
    3590           3 :                     if (bNotAnalyzed)
    3591             :                     {
    3592           2 :                         json_object *poTerms = json_object_new_object();
    3593           2 :                         json_object_object_add(poRet, "terms", poTerms);
    3594           2 :                         json_object *poTermsValues = json_object_new_array();
    3595           2 :                         json_object_object_add(poTerms, osPath, poTermsValues);
    3596           6 :                         for (int i = 1; i < poNode->nSubExprCount; i++)
    3597             :                         {
    3598             :                             json_object *poVal =
    3599           4 :                                 GetValue(nFieldIdx, poNode->papoSubExpr[i]);
    3600           4 :                             if (poVal == nullptr)
    3601             :                             {
    3602           0 :                                 json_object_put(poRet);
    3603           0 :                                 return nullptr;
    3604             :                             }
    3605           4 :                             json_object_array_add(poTermsValues, poVal);
    3606             :                         }
    3607             :                     }
    3608             :                     else
    3609             :                     {
    3610           1 :                         json_object *poBool = json_object_new_object();
    3611           1 :                         json_object_object_add(poRet, "bool", poBool);
    3612           1 :                         json_object *poShould = json_object_new_array();
    3613           1 :                         json_object_object_add(poBool, "should", poShould);
    3614           3 :                         for (int i = 1; i < poNode->nSubExprCount; i++)
    3615             :                         {
    3616             :                             json_object *poVal =
    3617           2 :                                 GetValue(nFieldIdx, poNode->papoSubExpr[i]);
    3618           2 :                             if (poVal == nullptr)
    3619             :                             {
    3620           0 :                                 json_object_put(poRet);
    3621           0 :                                 return nullptr;
    3622             :                             }
    3623           2 :                             json_object *poShouldElt = json_object_new_object();
    3624           2 :                             json_object *poMatch = json_object_new_object();
    3625           2 :                             json_object_object_add(poShouldElt, "match",
    3626             :                                                    poMatch);
    3627           2 :                             json_object_object_add(poMatch, osPath, poVal);
    3628             : 
    3629           2 :                             if (m_poDS->m_nMajorVersion < 2)
    3630             :                             {
    3631             :                                 json_object *poNewShouldElt =
    3632           0 :                                     json_object_new_object();
    3633           0 :                                 json_object_object_add(poNewShouldElt, "query",
    3634             :                                                        poShouldElt);
    3635           0 :                                 poShouldElt = poNewShouldElt;
    3636             :                             }
    3637           2 :                             json_object_array_add(poShould, poShouldElt);
    3638             :                         }
    3639             :                     }
    3640             :                 }
    3641           4 :                 return poRet;
    3642             :             }
    3643             :         }
    3644          17 :         else if ((poNode->nOperation == SWQ_LIKE ||
    3645           3 :                   poNode->nOperation ==
    3646           4 :                       SWQ_ILIKE) &&  // ES actual semantics doesn't match
    3647             :                                      // exactly either...
    3648           4 :                  poNode->nSubExprCount >= 2 &&
    3649           4 :                  (nFieldIdx =
    3650          18 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3651           4 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3652             :         {
    3653           4 :             char chEscape = '\0';
    3654           4 :             if (poNode->nSubExprCount == 3)
    3655           1 :                 chEscape = poNode->papoSubExpr[2]->string_value[0];
    3656           4 :             const char *pszPattern = poNode->papoSubExpr[1]->string_value;
    3657             :             const char *pszFieldName =
    3658           4 :                 m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
    3659             :             bool bNotAnalyzed =
    3660           4 :                 CSLFindString(m_papszNotAnalyzedFields, pszFieldName) >= 0;
    3661           4 :             CPLString osPath(BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3662           7 :             if (!bNotAnalyzed &&
    3663           3 :                 CSLFindString(m_papszFieldsWithRawValue, pszFieldName) >= 0)
    3664             :             {
    3665           1 :                 osPath += ".raw";
    3666           1 :                 bNotAnalyzed = true;
    3667             :             }
    3668             : 
    3669           4 :             if (strchr(pszPattern, '*') || strchr(pszPattern, '?'))
    3670             :             {
    3671           1 :                 CPLDebug("ES", "Cannot handle * or ? in LIKE pattern");
    3672             :             }
    3673           3 :             else if (!bNotAnalyzed)
    3674             :             {
    3675           1 :                 if (!m_bFilterMustBeClientSideEvaluated)
    3676             :                 {
    3677           1 :                     m_bFilterMustBeClientSideEvaluated = true;
    3678           1 :                     CPLDebug(
    3679             :                         "ES",
    3680             :                         "Part or full filter will have to be evaluated on "
    3681             :                         "client side (wildcard test on a analyzed field).");
    3682             :                 }
    3683             :             }
    3684             :             else
    3685             :             {
    3686           2 :                 CPLString osUnescaped;
    3687           8 :                 for (int i = 0; pszPattern[i] != '\0'; ++i)
    3688             :                 {
    3689           6 :                     if (chEscape == pszPattern[i])
    3690             :                     {
    3691           1 :                         if (pszPattern[i + 1] == '\0')
    3692           0 :                             break;
    3693           1 :                         osUnescaped += pszPattern[i + 1];
    3694           1 :                         i++;
    3695             :                     }
    3696           5 :                     else if (pszPattern[i] == '%')
    3697             :                     {
    3698           2 :                         osUnescaped += '*';
    3699             :                     }
    3700           3 :                     else if (pszPattern[i] == '_')
    3701             :                     {
    3702           2 :                         osUnescaped += '?';
    3703             :                     }
    3704             :                     else
    3705             :                     {
    3706           1 :                         osUnescaped += pszPattern[i];
    3707             :                     }
    3708             :                 }
    3709           2 :                 json_object *poRet = json_object_new_object();
    3710           2 :                 json_object *poWildcard = json_object_new_object();
    3711           2 :                 json_object_object_add(poRet, "wildcard", poWildcard);
    3712           2 :                 json_object_object_add(poWildcard, osPath,
    3713             :                                        json_object_new_string(osUnescaped));
    3714           2 :                 return poRet;
    3715             :             }
    3716             :         }
    3717             :     }
    3718             : 
    3719           5 :     if (!m_bFilterMustBeClientSideEvaluated)
    3720             :     {
    3721           4 :         m_bFilterMustBeClientSideEvaluated = true;
    3722           4 :         CPLDebug("ES", "Part or full filter will have to be evaluated on "
    3723             :                        "client side.");
    3724             :     }
    3725           5 :     return nullptr;
    3726             : }
    3727             : 
    3728             : /************************************************************************/
    3729             : /*                          SetAttributeFilter()                        */
    3730             : /************************************************************************/
    3731             : 
    3732          37 : OGRErr OGRElasticLayer::SetAttributeFilter(const char *pszFilter)
    3733             : {
    3734          37 :     m_bFilterMustBeClientSideEvaluated = false;
    3735          37 :     if (pszFilter != nullptr && pszFilter[0] == '{')
    3736             :     {
    3737           2 :         if (!m_osESSearch.empty())
    3738             :         {
    3739           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3740             :                      "Setting an Elasticsearch filter on a resulting layer "
    3741             :                      "is not supported");
    3742           0 :             return OGRERR_FAILURE;
    3743             :         }
    3744           2 :         OGRLayer::SetAttributeFilter(nullptr);
    3745           2 :         m_osJSONFilter = pszFilter;
    3746           2 :         return OGRERR_NONE;
    3747             :     }
    3748             :     else
    3749             :     {
    3750          35 :         m_osJSONFilter.clear();
    3751          35 :         json_object_put(m_poJSONFilter);
    3752          35 :         m_poJSONFilter = nullptr;
    3753          35 :         OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
    3754          35 :         if (eErr == OGRERR_NONE && m_poAttrQuery != nullptr)
    3755             :         {
    3756             :             swq_expr_node *poNode =
    3757          26 :                 reinterpret_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
    3758          26 :             m_poJSONFilter = TranslateSQLToFilter(poNode);
    3759             :         }
    3760          35 :         return eErr;
    3761             :     }
    3762             : }
    3763             : 
    3764             : /************************************************************************/
    3765             : /*                          ClampEnvelope()                             */
    3766             : /************************************************************************/
    3767             : 
    3768           7 : void OGRElasticLayer::ClampEnvelope(OGREnvelope &sEnvelope)
    3769             : {
    3770           7 :     if (sEnvelope.MinX < -180)
    3771           1 :         sEnvelope.MinX = -180;
    3772           7 :     if (sEnvelope.MinX > 180)
    3773           0 :         sEnvelope.MinX = 180;
    3774             : 
    3775           7 :     if (sEnvelope.MinY < -90)
    3776           1 :         sEnvelope.MinY = -90;
    3777           7 :     if (sEnvelope.MinY > 90)
    3778           0 :         sEnvelope.MinY = 90;
    3779             : 
    3780           7 :     if (sEnvelope.MaxX > 180)
    3781           1 :         sEnvelope.MaxX = 180;
    3782           7 :     if (sEnvelope.MaxX < -180)
    3783           0 :         sEnvelope.MaxX = -180;
    3784             : 
    3785           7 :     if (sEnvelope.MaxY > 90)
    3786           1 :         sEnvelope.MaxY = 90;
    3787           7 :     if (sEnvelope.MaxY < -90)
    3788           0 :         sEnvelope.MaxY = -90;
    3789           7 : }
    3790             : 
    3791             : /************************************************************************/
    3792             : /*                          SetSpatialFilter()                          */
    3793             : /************************************************************************/
    3794             : 
    3795          16 : void OGRElasticLayer::SetSpatialFilter(int iGeomField, OGRGeometry *poGeomIn)
    3796             : 
    3797             : {
    3798          16 :     FinalizeFeatureDefn();
    3799             : 
    3800          30 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
    3801          14 :         GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone)
    3802             :     {
    3803           2 :         if (iGeomField != 0)
    3804             :         {
    3805           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    3806             :                      "Invalid geometry field index : %d", iGeomField);
    3807             :         }
    3808          12 :         return;
    3809             :     }
    3810          14 :     m_iGeomFieldFilter = iGeomField;
    3811             : 
    3812          14 :     InstallFilter(poGeomIn);
    3813             : 
    3814          14 :     json_object_put(m_poSpatialFilter);
    3815          14 :     m_poSpatialFilter = nullptr;
    3816             : 
    3817          14 :     if (poGeomIn == nullptr)
    3818           9 :         return;
    3819             : 
    3820           5 :     if (!m_osESSearch.empty())
    3821             :     {
    3822           0 :         CPLError(
    3823             :             CE_Failure, CPLE_AppDefined,
    3824             :             "Setting a spatial filter on a resulting layer is not supported");
    3825           0 :         return;
    3826             :     }
    3827             : 
    3828           5 :     OGREnvelope sEnvelope;
    3829           5 :     poGeomIn->getEnvelope(&sEnvelope);
    3830           5 :     ClampEnvelope(sEnvelope);
    3831             : 
    3832           5 :     if (sEnvelope.MinX == -180 && sEnvelope.MinY == -90 &&
    3833           1 :         sEnvelope.MaxX == 180 && sEnvelope.MaxY == 90)
    3834             :     {
    3835           1 :         return;
    3836             :     }
    3837             : 
    3838           4 :     m_poSpatialFilter = json_object_new_object();
    3839             : 
    3840           4 :     if (m_abIsGeoPoint[iGeomField])
    3841             :     {
    3842           1 :         json_object *geo_bounding_box = json_object_new_object();
    3843           1 :         json_object_object_add(m_poSpatialFilter, "geo_bounding_box",
    3844             :                                geo_bounding_box);
    3845             : 
    3846           2 :         CPLString osPath = BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]);
    3847             : 
    3848           1 :         json_object *field = json_object_new_object();
    3849           1 :         json_object_object_add(geo_bounding_box, osPath.c_str(), field);
    3850             : 
    3851           1 :         json_object *top_left = json_object_new_object();
    3852           1 :         json_object_object_add(field, "top_left", top_left);
    3853           1 :         json_object_object_add(
    3854             :             top_left, "lat",
    3855             :             json_object_new_double_with_precision(sEnvelope.MaxY, 6));
    3856           1 :         json_object_object_add(
    3857             :             top_left, "lon",
    3858             :             json_object_new_double_with_precision(sEnvelope.MinX, 6));
    3859             : 
    3860           1 :         json_object *bottom_right = json_object_new_object();
    3861           1 :         json_object_object_add(field, "bottom_right", bottom_right);
    3862           1 :         json_object_object_add(
    3863             :             bottom_right, "lat",
    3864             :             json_object_new_double_with_precision(sEnvelope.MinY, 6));
    3865           1 :         json_object_object_add(
    3866             :             bottom_right, "lon",
    3867             :             json_object_new_double_with_precision(sEnvelope.MaxX, 6));
    3868             :     }
    3869             :     else
    3870             :     {
    3871           3 :         json_object *geo_shape = json_object_new_object();
    3872           3 :         json_object_object_add(m_poSpatialFilter, "geo_shape", geo_shape);
    3873             : 
    3874           6 :         CPLString osPath = BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]);
    3875             : 
    3876           3 :         json_object *field = json_object_new_object();
    3877           3 :         json_object_object_add(geo_shape, osPath.c_str(), field);
    3878             : 
    3879           3 :         json_object *shape = json_object_new_object();
    3880           3 :         json_object_object_add(field, "shape", shape);
    3881             : 
    3882           3 :         json_object_object_add(shape, "type",
    3883             :                                json_object_new_string("envelope"));
    3884             : 
    3885           3 :         json_object *coordinates = json_object_new_array();
    3886           3 :         json_object_object_add(shape, "coordinates", coordinates);
    3887             : 
    3888           3 :         json_object *top_left = json_object_new_array();
    3889           3 :         json_object_array_add(
    3890             :             top_left, json_object_new_double_with_precision(sEnvelope.MinX, 6));
    3891           3 :         json_object_array_add(
    3892             :             top_left, json_object_new_double_with_precision(sEnvelope.MaxY, 6));
    3893           3 :         json_object_array_add(coordinates, top_left);
    3894             : 
    3895           3 :         json_object *bottom_right = json_object_new_array();
    3896           3 :         json_object_array_add(
    3897             :             bottom_right,
    3898             :             json_object_new_double_with_precision(sEnvelope.MaxX, 6));
    3899           3 :         json_object_array_add(
    3900             :             bottom_right,
    3901             :             json_object_new_double_with_precision(sEnvelope.MinY, 6));
    3902           3 :         json_object_array_add(coordinates, bottom_right);
    3903             :     }
    3904             : }
    3905             : 
    3906             : /************************************************************************/
    3907             : /*                            GetExtent()                                */
    3908             : /************************************************************************/
    3909             : 
    3910           7 : OGRErr OGRElasticLayer::GetExtent(int iGeomField, OGREnvelope *psExtent,
    3911             :                                   int bForce)
    3912             : {
    3913           7 :     FinalizeFeatureDefn();
    3914             : 
    3915           7 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount())
    3916             :     {
    3917           2 :         if (iGeomField != 0)
    3918             :         {
    3919           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    3920             :                      "Invalid geometry field index : %d", iGeomField);
    3921             :         }
    3922           2 :         return OGRERR_FAILURE;
    3923             :     }
    3924             : 
    3925             :     // geo_shape aggregation is only available since ES 7.8, but only with XPack
    3926             :     // for now
    3927           6 :     if (!m_abIsGeoPoint[iGeomField] &&
    3928           1 :         !(m_poDS->m_nMajorVersion > 7 ||
    3929           1 :           (m_poDS->m_nMajorVersion == 7 && m_poDS->m_nMinorVersion >= 8)))
    3930             :     {
    3931           1 :         m_bUseSingleQueryParams = true;
    3932             :         const auto eRet =
    3933           1 :             OGRLayer::GetExtentInternal(iGeomField, psExtent, bForce);
    3934           1 :         m_bUseSingleQueryParams = false;
    3935           1 :         return eRet;
    3936             :     }
    3937             : 
    3938             :     CPLString osFilter = CPLSPrintf(
    3939             :         "{ \"size\": 0, \"aggs\" : { \"bbox\" : { \"geo_bounds\" : { \"field\" "
    3940             :         ": \"%s\" } } } }",
    3941           8 :         BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]).c_str());
    3942             :     CPLString osURL =
    3943           8 :         CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str());
    3944           4 :     if (m_poDS->m_nMajorVersion < 7)
    3945           3 :         osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3946           4 :     osURL += "/_search?pretty";
    3947           4 :     AddTimeoutTerminateAfterToURL(osURL);
    3948             : 
    3949           4 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    3950             :     json_object *poResponse =
    3951           4 :         m_poDS->RunRequest(osURL.c_str(), osFilter.c_str());
    3952           4 :     CPLPopErrorHandler();
    3953           4 :     if (poResponse == nullptr)
    3954             :     {
    3955           2 :         const char *pszLastErrorMsg = CPLGetLastErrorMsg();
    3956           2 :         if (!m_abIsGeoPoint[iGeomField] &&
    3957           0 :             strstr(pszLastErrorMsg, "Fielddata is not supported on field") !=
    3958             :                 nullptr)
    3959             :         {
    3960           0 :             CPLDebug("ES",
    3961             :                      "geo_bounds aggregation failed, likely because of lack "
    3962             :                      "of XPack. Using client-side method");
    3963           0 :             CPLErrorReset();
    3964             :         }
    3965             :         else
    3966             :         {
    3967           2 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", pszLastErrorMsg);
    3968             :         }
    3969             :     }
    3970             : 
    3971             :     json_object *poBounds =
    3972           4 :         json_ex_get_object_by_path(poResponse, "aggregations.bbox.bounds");
    3973           4 :     json_object *poTopLeft = json_ex_get_object_by_path(poBounds, "top_left");
    3974             :     json_object *poBottomRight =
    3975           4 :         json_ex_get_object_by_path(poBounds, "bottom_right");
    3976           4 :     json_object *poTopLeftLon = json_ex_get_object_by_path(poTopLeft, "lon");
    3977           4 :     json_object *poTopLeftLat = json_ex_get_object_by_path(poTopLeft, "lat");
    3978             :     json_object *poBottomRightLon =
    3979           4 :         json_ex_get_object_by_path(poBottomRight, "lon");
    3980             :     json_object *poBottomRightLat =
    3981           4 :         json_ex_get_object_by_path(poBottomRight, "lat");
    3982             : 
    3983             :     OGRErr eErr;
    3984           4 :     if (poTopLeftLon == nullptr || poTopLeftLat == nullptr ||
    3985           2 :         poBottomRightLon == nullptr || poBottomRightLat == nullptr)
    3986             :     {
    3987           2 :         m_bUseSingleQueryParams = true;
    3988             :         const auto eRet =
    3989           2 :             OGRLayer::GetExtentInternal(iGeomField, psExtent, bForce);
    3990           2 :         m_bUseSingleQueryParams = false;
    3991           2 :         return eRet;
    3992             :     }
    3993             :     else
    3994             :     {
    3995           2 :         double dfMinX = json_object_get_double(poTopLeftLon);
    3996           2 :         double dfMaxY = json_object_get_double(poTopLeftLat);
    3997           2 :         double dfMaxX = json_object_get_double(poBottomRightLon);
    3998           2 :         double dfMinY = json_object_get_double(poBottomRightLat);
    3999             : 
    4000           2 :         psExtent->MinX = dfMinX;
    4001           2 :         psExtent->MaxY = dfMaxY;
    4002           2 :         psExtent->MaxX = dfMaxX;
    4003           2 :         psExtent->MinY = dfMinY;
    4004             : 
    4005           2 :         eErr = OGRERR_NONE;
    4006             :     }
    4007           2 :     json_object_put(poResponse);
    4008             : 
    4009           2 :     return eErr;
    4010             : }
    4011             : 
    4012             : /************************************************************************/
    4013             : /*                             GetDataset()                             */
    4014             : /************************************************************************/
    4015             : 
    4016           1 : GDALDataset *OGRElasticLayer::GetDataset()
    4017             : {
    4018           1 :     return m_poDS;
    4019             : }

Generated by: LCOV version 1.14