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

Generated by: LCOV version 1.14