LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/elastic - ogrelasticlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1983 2112 93.9 %
Date: 2025-10-01 17:07:58 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          40 :                     poGeom = OGRGeoJSONReadGeometry(
    1723             :                                  it.val, /* bHasM = */ false,
    1724             :                                  /* OGRSpatialReference* = */ nullptr)
    1725          20 :                                  .release();
    1726             :                 }
    1727             :             }
    1728           1 :             else if (json_object_get_type(it.val) == json_type_string)
    1729             :             {
    1730             :                 // Assume this is WKT
    1731           1 :                 OGRGeometryFactory::createFromWkt(
    1732             :                     json_object_get_string(it.val), nullptr, &poGeom);
    1733             :             }
    1734             : 
    1735          64 :             if (poGeom != nullptr)
    1736             :             {
    1737          64 :                 poGeom->assignSpatialReference(poSRS);
    1738          64 :                 poFeature->SetGeomFieldDirectly(oIter->second, poGeom);
    1739             :             }
    1740             :         }
    1741         221 :         else if (json_object_get_type(it.val) == json_type_object &&
    1742          73 :                  (m_poDS->m_bFlattenNestedAttributes ||
    1743           2 :                   (osPath.empty() && m_osMappingName == "FeatureCollection" &&
    1744           0 :                    strcmp(it.key, "properties") == 0)))
    1745             :         {
    1746          71 :             BuildFeature(poFeature, it.val, osCurPath);
    1747             :         }
    1748          79 :         else if (json_object_get_type(it.val) == json_type_object &&
    1749           1 :                  !m_poDS->m_bFlattenNestedAttributes)
    1750             :         {
    1751           1 :             if (m_aosMapToGeomFieldIndex.find(osCurPath + ".coordinates") !=
    1752           2 :                 m_aosMapToGeomFieldIndex.end())
    1753             :             {
    1754           1 :                 BuildFeature(poFeature, it.val, osCurPath);
    1755             :             }
    1756             :         }
    1757             :     }
    1758         155 : }
    1759             : 
    1760             : /************************************************************************/
    1761             : /*                            AppendGroup()                             */
    1762             : /************************************************************************/
    1763             : 
    1764           4 : static json_object *AppendGroup(json_object *parent, const CPLString &name)
    1765             : {
    1766           4 :     json_object *obj = json_object_new_object();
    1767           4 :     json_object *properties = json_object_new_object();
    1768           4 :     json_object_object_add(parent, name, obj);
    1769           4 :     json_object_object_add(obj, "properties", properties);
    1770           4 :     return properties;
    1771             : }
    1772             : 
    1773             : /************************************************************************/
    1774             : /*                           AddPropertyMap()                           */
    1775             : /************************************************************************/
    1776             : 
    1777          18 : static json_object *AddPropertyMap(const CPLString &type)
    1778             : {
    1779          18 :     json_object *obj = json_object_new_object();
    1780          18 :     json_object_object_add(obj, "type", json_object_new_string(type.c_str()));
    1781          18 :     return obj;
    1782             : }
    1783             : 
    1784             : /************************************************************************/
    1785             : /*                      GetContainerForMapping()                        */
    1786             : /************************************************************************/
    1787             : 
    1788             : static json_object *
    1789          48 : GetContainerForMapping(json_object *poContainer,
    1790             :                        const std::vector<CPLString> &aosPath,
    1791             :                        std::map<std::vector<CPLString>, json_object *> &oMap)
    1792             : {
    1793          48 :     std::vector<CPLString> aosSubPath;
    1794          80 :     for (int j = 0; j < (int)aosPath.size() - 1; j++)
    1795             :     {
    1796          32 :         aosSubPath.push_back(aosPath[j]);
    1797             :         std::map<std::vector<CPLString>, json_object *>::iterator oIter =
    1798          32 :             oMap.find(aosSubPath);
    1799          32 :         if (oIter == oMap.end())
    1800             :         {
    1801          11 :             json_object *poNewContainer = json_object_new_object();
    1802          11 :             json_object *poProperties = json_object_new_object();
    1803          11 :             json_object_object_add(poContainer, aosPath[j], poNewContainer);
    1804          11 :             json_object_object_add(poNewContainer, "properties", poProperties);
    1805          11 :             oMap[aosSubPath] = poProperties;
    1806          11 :             poContainer = poProperties;
    1807             :         }
    1808             :         else
    1809             :         {
    1810          21 :             poContainer = oIter->second;
    1811             :         }
    1812             :     }
    1813          96 :     return poContainer;
    1814             : }
    1815             : 
    1816             : /************************************************************************/
    1817             : /*                             BuildMap()                               */
    1818             : /************************************************************************/
    1819             : 
    1820          13 : CPLString OGRElasticLayer::BuildMap()
    1821             : {
    1822          13 :     json_object *map = json_object_new_object();
    1823             : 
    1824          26 :     std::map<std::vector<CPLString>, json_object *> oMap;
    1825             : 
    1826             :     json_object *poMapping;
    1827          13 :     json_object *poMappingProperties = json_object_new_object();
    1828          13 :     if (m_poDS->m_nMajorVersion < 7)
    1829             :     {
    1830          11 :         poMapping = json_object_new_object();
    1831          11 :         json_object_object_add(map, m_osMappingName, poMapping);
    1832             :     }
    1833             :     else
    1834             :     {
    1835           2 :         poMapping = map;
    1836             :     }
    1837          13 :     json_object_object_add(poMapping, "properties", poMappingProperties);
    1838             : 
    1839          13 :     if (m_poDS->m_nMajorVersion < 7 && m_osMappingName == "FeatureCollection")
    1840             :     {
    1841           9 :         json_object_object_add(
    1842             :             poMappingProperties, "type",
    1843           9 :             AddPropertyMap(m_poDS->m_nMajorVersion >= 5 ? "text" : "string"));
    1844             : 
    1845          18 :         std::vector<CPLString> aosPath;
    1846           9 :         aosPath.push_back("properties");
    1847           9 :         aosPath.push_back("dummy");
    1848           9 :         GetContainerForMapping(poMappingProperties, aosPath, oMap);
    1849             :     }
    1850             : 
    1851             :     /* skip _id field */
    1852          34 :     for (int i = 1; i < m_poFeatureDefn->GetFieldCount(); i++)
    1853             :     {
    1854          21 :         OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    1855             : 
    1856          21 :         json_object *poContainer = GetContainerForMapping(
    1857          21 :             poMappingProperties, m_aaosFieldPaths[i], oMap);
    1858          21 :         const char *pszLastComponent = m_aaosFieldPaths[i].back();
    1859             : 
    1860          21 :         const char *pszType = "string";
    1861          21 :         const char *pszFormat = nullptr;
    1862             : 
    1863          21 :         switch (poFieldDefn->GetType())
    1864             :         {
    1865           4 :             case OFTInteger:
    1866             :             case OFTIntegerList:
    1867             :             {
    1868           4 :                 if (poFieldDefn->GetSubType() == OFSTBoolean)
    1869           1 :                     pszType = "boolean";
    1870             :                 else
    1871           3 :                     pszType = "integer";
    1872           4 :                 break;
    1873             :             }
    1874           2 :             case OFTInteger64:
    1875             :             case OFTInteger64List:
    1876           2 :                 pszType = "long";
    1877           2 :                 break;
    1878           3 :             case OFTReal:
    1879             :             case OFTRealList:
    1880           3 :                 pszType = "double";
    1881           3 :                 break;
    1882           2 :             case OFTDateTime:
    1883             :             case OFTDate:
    1884           2 :                 pszType = "date";
    1885           2 :                 pszFormat = "yyyy/MM/dd HH:mm:ss.SSSZZ||yyyy/MM/dd "
    1886             :                             "HH:mm:ss.SSS||yyyy/MM/dd";
    1887           2 :                 break;
    1888           1 :             case OFTTime:
    1889           1 :                 pszType = "date";
    1890           1 :                 pszFormat = "HH:mm:ss.SSS";
    1891           1 :                 break;
    1892           1 :             case OFTBinary:
    1893           1 :                 pszType = "binary";
    1894           1 :                 break;
    1895           8 :             default:
    1896           8 :                 break;
    1897             :         }
    1898             : 
    1899          21 :         bool bAnalyzed = EQUAL(pszType, "string");
    1900          21 :         json_object *poPropertyMap = json_object_new_object();
    1901          21 :         if (m_poDS->m_nMajorVersion >= 5 && EQUAL(pszType, "string"))
    1902             :         {
    1903           0 :             if (CSLFindString(m_papszNotAnalyzedFields,
    1904           0 :                               poFieldDefn->GetNameRef()) >= 0 ||
    1905           0 :                 (CSLCount(m_papszNotAnalyzedFields) == 1 &&
    1906           0 :                  EQUAL(m_papszNotAnalyzedFields[0], "{ALL}")))
    1907             :             {
    1908           0 :                 bAnalyzed = false;
    1909           0 :                 pszType = "keyword";
    1910             :             }
    1911             :             else
    1912           0 :                 pszType = "text";
    1913             :         }
    1914          21 :         json_object_object_add(poPropertyMap, "type",
    1915             :                                json_object_new_string(pszType));
    1916          21 :         if (pszFormat)
    1917           3 :             json_object_object_add(poPropertyMap, "format",
    1918             :                                    json_object_new_string(pszFormat));
    1919          41 :         if (m_bStoreFields ||
    1920          20 :             CSLFindString(m_papszStoredFields, poFieldDefn->GetNameRef()) >= 0)
    1921           2 :             json_object_object_add(poPropertyMap, "store",
    1922             :                                    json_object_new_string("yes"));
    1923          42 :         if (m_poDS->m_nMajorVersion < 5 &&
    1924          21 :             (CSLFindString(m_papszNotAnalyzedFields,
    1925          20 :                            poFieldDefn->GetNameRef()) >= 0 ||
    1926          20 :              (CSLCount(m_papszNotAnalyzedFields) == 1 &&
    1927          13 :               EQUAL(m_papszNotAnalyzedFields[0], "{ALL}"))))
    1928             :         {
    1929           1 :             bAnalyzed = false;
    1930           1 :             json_object_object_add(poPropertyMap, "index",
    1931             :                                    json_object_new_string("not_analyzed"));
    1932             :         }
    1933          20 :         else if (CSLFindString(m_papszNotIndexedFields,
    1934          20 :                                poFieldDefn->GetNameRef()) >= 0)
    1935           1 :             json_object_object_add(poPropertyMap, "index",
    1936             :                                    json_object_new_string("no"));
    1937             : 
    1938          28 :         if (bAnalyzed && (CSLFindString(m_papszFieldsWithRawValue,
    1939           7 :                                         poFieldDefn->GetNameRef()) >= 0 ||
    1940           7 :                           (CSLCount(m_papszFieldsWithRawValue) == 1 &&
    1941           0 :                            EQUAL(m_papszFieldsWithRawValue[0], "{ALL}"))))
    1942             :         {
    1943           0 :             json_object *poFields = json_object_new_object();
    1944           0 :             json_object *poRaw = json_object_new_object();
    1945           0 :             json_object_object_add(poFields, "raw", poRaw);
    1946           0 :             if (m_poDS->m_nMajorVersion >= 5)
    1947             :             {
    1948           0 :                 json_object_object_add(poRaw, "type",
    1949             :                                        json_object_new_string("keyword"));
    1950             :             }
    1951             :             else
    1952             :             {
    1953           0 :                 json_object_object_add(poRaw, "type",
    1954             :                                        json_object_new_string("string"));
    1955           0 :                 json_object_object_add(poRaw, "index",
    1956             :                                        json_object_new_string("not_analyzed"));
    1957             :             }
    1958           0 :             json_object_object_add(poPropertyMap, "fields", poFields);
    1959             :         }
    1960             : 
    1961          21 :         json_object_object_add(poContainer, pszLastComponent, poPropertyMap);
    1962             :     }
    1963             : 
    1964          31 :     for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
    1965             :     {
    1966          36 :         std::vector<CPLString> aosPath = m_aaosGeomFieldPaths[i];
    1967          18 :         bool bAddGeoJSONType = false;
    1968          22 :         if (m_abIsGeoPoint[i] && aosPath.size() >= 2 &&
    1969           4 :             aosPath.back() == "coordinates")
    1970             :         {
    1971           4 :             bAddGeoJSONType = true;
    1972           4 :             aosPath.resize((int)aosPath.size() - 1);
    1973             :         }
    1974             : 
    1975             :         json_object *poContainer =
    1976          18 :             GetContainerForMapping(poMappingProperties, aosPath, oMap);
    1977          18 :         const char *pszLastComponent = aosPath.back();
    1978             : 
    1979          18 :         if (m_abIsGeoPoint[i])
    1980             :         {
    1981           5 :             json_object *geo_point = AddPropertyMap("geo_point");
    1982           5 :             if (bAddGeoJSONType)
    1983             :             {
    1984             :                 json_object *geometry =
    1985           4 :                     AppendGroup(poContainer, pszLastComponent);
    1986           4 :                 json_object_object_add(
    1987             :                     geometry, "type",
    1988           4 :                     AddPropertyMap(m_poDS->m_nMajorVersion >= 5 ? "text"
    1989             :                                                                 : "string"));
    1990           4 :                 json_object_object_add(geometry, "coordinates", geo_point);
    1991             :             }
    1992             :             else
    1993             :             {
    1994           1 :                 json_object_object_add(poContainer, pszLastComponent,
    1995             :                                        geo_point);
    1996             :             }
    1997           5 :             if (!m_osPrecision.empty())
    1998             :             {
    1999           1 :                 json_object *field_data = json_object_new_object();
    2000           1 :                 json_object_object_add(geo_point, "fielddata", field_data);
    2001           1 :                 json_object_object_add(field_data, "format",
    2002             :                                        json_object_new_string("compressed"));
    2003           1 :                 json_object_object_add(
    2004             :                     field_data, "precision",
    2005             :                     json_object_new_string(m_osPrecision.c_str()));
    2006             :             }
    2007             :         }
    2008             :         else
    2009             :         {
    2010          13 :             json_object *geometry = json_object_new_object();
    2011          13 :             json_object_object_add(poContainer, pszLastComponent, geometry);
    2012          13 :             json_object_object_add(geometry, "type",
    2013             :                                    json_object_new_string("geo_shape"));
    2014          13 :             if (!m_osPrecision.empty())
    2015           1 :                 json_object_object_add(
    2016             :                     geometry, "precision",
    2017             :                     json_object_new_string(m_osPrecision.c_str()));
    2018             :         }
    2019             :     }
    2020             : 
    2021          13 :     json_object *poMeta = nullptr;
    2022          13 :     json_object *poGeomFields = nullptr;
    2023          13 :     json_object *poFields = nullptr;
    2024          13 :     if (!m_osFID.empty())
    2025             :     {
    2026           5 :         poMeta = json_object_new_object();
    2027           5 :         json_object_object_add(poMeta, "fid",
    2028             :                                json_object_new_string(m_osFID.c_str()));
    2029             :     }
    2030          31 :     for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
    2031             :     {
    2032             :         OGRGeomFieldDefn *poGeomFieldDefn =
    2033          18 :             m_poFeatureDefn->GetGeomFieldDefn(i);
    2034          18 :         if (!m_abIsGeoPoint[i] && poGeomFieldDefn->GetType() != wkbUnknown)
    2035             :         {
    2036           1 :             if (poMeta == nullptr)
    2037           1 :                 poMeta = json_object_new_object();
    2038           1 :             if (poGeomFields == nullptr)
    2039             :             {
    2040           1 :                 poGeomFields = json_object_new_object();
    2041           1 :                 json_object_object_add(poMeta, "geomfields", poGeomFields);
    2042             :             }
    2043           1 :             json_object_object_add(poGeomFields, poGeomFieldDefn->GetNameRef(),
    2044             :                                    json_object_new_string(OGRToOGCGeomType(
    2045             :                                        poGeomFieldDefn->GetType())));
    2046             :         }
    2047             :     }
    2048          47 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    2049             :     {
    2050          34 :         OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    2051          34 :         OGRFieldType eType = poFieldDefn->GetType();
    2052          34 :         if (eType == OFTIntegerList || eType == OFTInteger64List ||
    2053          31 :             eType == OFTRealList || eType == OFTStringList)
    2054             :         {
    2055           4 :             if (poMeta == nullptr)
    2056           1 :                 poMeta = json_object_new_object();
    2057           4 :             if (poFields == nullptr)
    2058             :             {
    2059           1 :                 poFields = json_object_new_object();
    2060           1 :                 json_object_object_add(poMeta, "fields", poFields);
    2061             :             }
    2062           4 :             json_object_object_add(
    2063             :                 poFields, poFieldDefn->GetNameRef(),
    2064             :                 json_object_new_string(OGR_GetFieldTypeName(eType)));
    2065             :         }
    2066             :     }
    2067          13 :     if (poMeta)
    2068           7 :         json_object_object_add(poMapping, "_meta", poMeta);
    2069             : 
    2070          13 :     CPLString jsonMap(json_object_to_json_string(map));
    2071          13 :     json_object_put(map);
    2072             : 
    2073             :     // Got personally caught by that...
    2074          13 :     if (CSLCount(m_papszStoredFields) == 1 &&
    2075           1 :         (EQUAL(m_papszStoredFields[0], "YES") ||
    2076          14 :          EQUAL(m_papszStoredFields[0], "TRUE")) &&
    2077           0 :         m_poFeatureDefn->GetFieldIndex(m_papszStoredFields[0]) < 0)
    2078             :     {
    2079           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    2080             :                  "STORED_FIELDS=%s was specified. Perhaps you meant "
    2081             :                  "STORE_FIELDS=%s instead?",
    2082           0 :                  m_papszStoredFields[0], m_papszStoredFields[0]);
    2083             :     }
    2084             : 
    2085          26 :     return jsonMap;
    2086             : }
    2087             : 
    2088             : /************************************************************************/
    2089             : /*                       BuildGeoJSONGeometry()                         */
    2090             : /************************************************************************/
    2091             : 
    2092          19 : static void BuildGeoJSONGeometry(json_object *geometry,
    2093             :                                  const OGRGeometry *poGeom)
    2094             : {
    2095          19 :     const int nPrecision = 10;
    2096          19 :     double dfEps = pow(10.0, -(double)nPrecision);
    2097          19 :     const char *pszGeomType = "";
    2098          19 :     switch (wkbFlatten(poGeom->getGeometryType()))
    2099             :     {
    2100           6 :         case wkbPoint:
    2101           6 :             pszGeomType = "point";
    2102           6 :             break;
    2103           3 :         case wkbLineString:
    2104           3 :             pszGeomType = "linestring";
    2105           3 :             break;
    2106           2 :         case wkbPolygon:
    2107           2 :             pszGeomType = "polygon";
    2108           2 :             break;
    2109           2 :         case wkbMultiPoint:
    2110           2 :             pszGeomType = "multipoint";
    2111           2 :             break;
    2112           2 :         case wkbMultiLineString:
    2113           2 :             pszGeomType = "multilinestring";
    2114           2 :             break;
    2115           2 :         case wkbMultiPolygon:
    2116           2 :             pszGeomType = "multipolygon";
    2117           2 :             break;
    2118           2 :         case wkbGeometryCollection:
    2119           2 :             pszGeomType = "geometrycollection";
    2120           2 :             break;
    2121           0 :         default:
    2122           0 :             break;
    2123             :     }
    2124          19 :     json_object_object_add(geometry, "type",
    2125             :                            json_object_new_string(pszGeomType));
    2126             : 
    2127          19 :     switch (wkbFlatten(poGeom->getGeometryType()))
    2128             :     {
    2129           6 :         case wkbPoint:
    2130             :         {
    2131           6 :             const OGRPoint *poPoint = poGeom->toPoint();
    2132           6 :             json_object *coordinates = json_object_new_array();
    2133           6 :             json_object_object_add(geometry, "coordinates", coordinates);
    2134           6 :             json_object_array_add(coordinates,
    2135             :                                   json_object_new_double_with_precision(
    2136             :                                       poPoint->getX(), nPrecision));
    2137           6 :             json_object_array_add(coordinates,
    2138             :                                   json_object_new_double_with_precision(
    2139             :                                       poPoint->getY(), nPrecision));
    2140           6 :             break;
    2141             :         }
    2142             : 
    2143           3 :         case wkbLineString:
    2144             :         {
    2145           3 :             const OGRLineString *poLS = poGeom->toLineString();
    2146           3 :             json_object *coordinates = json_object_new_array();
    2147           3 :             json_object_object_add(geometry, "coordinates", coordinates);
    2148           9 :             for (int i = 0; i < poLS->getNumPoints(); i++)
    2149             :             {
    2150           6 :                 json_object *point = json_object_new_array();
    2151           6 :                 json_object_array_add(coordinates, point);
    2152           6 :                 json_object_array_add(
    2153             :                     point, json_object_new_double_with_precision(poLS->getX(i),
    2154             :                                                                  nPrecision));
    2155           6 :                 json_object_array_add(
    2156             :                     point, json_object_new_double_with_precision(poLS->getY(i),
    2157             :                                                                  nPrecision));
    2158             :             }
    2159           3 :             break;
    2160             :         }
    2161             : 
    2162           2 :         case wkbPolygon:
    2163             :         {
    2164           2 :             const OGRPolygon *poPoly = poGeom->toPolygon();
    2165           2 :             json_object *coordinates = json_object_new_array();
    2166           2 :             json_object_object_add(geometry, "coordinates", coordinates);
    2167           6 :             for (auto &&poLS : *poPoly)
    2168             :             {
    2169           4 :                 json_object *ring = json_object_new_array();
    2170           4 :                 json_object_array_add(coordinates, ring);
    2171          20 :                 for (int j = 0; j < poLS->getNumPoints(); j++)
    2172             :                 {
    2173          28 :                     if (j > 0 &&
    2174          20 :                         fabs(poLS->getX(j) - poLS->getX(j - 1)) < dfEps &&
    2175           4 :                         fabs(poLS->getY(j) - poLS->getY(j - 1)) < dfEps)
    2176           0 :                         continue;
    2177          16 :                     json_object *point = json_object_new_array();
    2178          16 :                     json_object_array_add(ring, point);
    2179          16 :                     json_object_array_add(point,
    2180             :                                           json_object_new_double_with_precision(
    2181          16 :                                               poLS->getX(j), nPrecision));
    2182          16 :                     json_object_array_add(point,
    2183             :                                           json_object_new_double_with_precision(
    2184          16 :                                               poLS->getY(j), nPrecision));
    2185             :                 }
    2186             :             }
    2187           2 :             break;
    2188             :         }
    2189             : 
    2190           2 :         case wkbMultiPoint:
    2191             :         {
    2192           2 :             const OGRMultiPoint *poMP = poGeom->toMultiPoint();
    2193           2 :             json_object *coordinates = json_object_new_array();
    2194           2 :             json_object_object_add(geometry, "coordinates", coordinates);
    2195           6 :             for (auto &&poPoint : *poMP)
    2196             :             {
    2197           4 :                 json_object *point = json_object_new_array();
    2198           4 :                 json_object_array_add(coordinates, point);
    2199           4 :                 json_object_array_add(point,
    2200             :                                       json_object_new_double_with_precision(
    2201             :                                           poPoint->getX(), nPrecision));
    2202           4 :                 json_object_array_add(point,
    2203             :                                       json_object_new_double_with_precision(
    2204             :                                           poPoint->getY(), nPrecision));
    2205             :             }
    2206           2 :             break;
    2207             :         }
    2208             : 
    2209           2 :         case wkbMultiLineString:
    2210             :         {
    2211           2 :             const OGRMultiLineString *poMLS = poGeom->toMultiLineString();
    2212           2 :             json_object *coordinates = json_object_new_array();
    2213           2 :             json_object_object_add(geometry, "coordinates", coordinates);
    2214           6 :             for (auto &&poLS : *poMLS)
    2215             :             {
    2216           4 :                 json_object *ls = json_object_new_array();
    2217           4 :                 json_object_array_add(coordinates, ls);
    2218          12 :                 for (auto &&oPoint : *poLS)
    2219             :                 {
    2220           8 :                     json_object *point = json_object_new_array();
    2221           8 :                     json_object_array_add(ls, point);
    2222           8 :                     json_object_array_add(point,
    2223             :                                           json_object_new_double_with_precision(
    2224             :                                               oPoint.getX(), nPrecision));
    2225           8 :                     json_object_array_add(point,
    2226             :                                           json_object_new_double_with_precision(
    2227             :                                               oPoint.getY(), nPrecision));
    2228             :                 }
    2229             :             }
    2230           2 :             break;
    2231             :         }
    2232             : 
    2233           2 :         case wkbMultiPolygon:
    2234             :         {
    2235           2 :             const OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
    2236           2 :             json_object *coordinates = json_object_new_array();
    2237           2 :             json_object_object_add(geometry, "coordinates", coordinates);
    2238           6 :             for (auto &&poPoly : *poMP)
    2239             :             {
    2240           4 :                 json_object *poly = json_object_new_array();
    2241           4 :                 json_object_array_add(coordinates, poly);
    2242          10 :                 for (auto &&poLS : *poPoly)
    2243             :                 {
    2244           6 :                     json_object *ring = json_object_new_array();
    2245           6 :                     json_object_array_add(poly, ring);
    2246          30 :                     for (int k = 0; k < poLS->getNumPoints(); k++)
    2247             :                     {
    2248          42 :                         if (k > 0 &&
    2249          30 :                             fabs(poLS->getX(k) - poLS->getX(k - 1)) < dfEps &&
    2250           6 :                             fabs(poLS->getY(k) - poLS->getY(k - 1)) < dfEps)
    2251           0 :                             continue;
    2252          24 :                         json_object *point = json_object_new_array();
    2253          24 :                         json_object_array_add(ring, point);
    2254          24 :                         json_object_array_add(
    2255             :                             point, json_object_new_double_with_precision(
    2256          24 :                                        poLS->getX(k), nPrecision));
    2257          24 :                         json_object_array_add(
    2258             :                             point, json_object_new_double_with_precision(
    2259          24 :                                        poLS->getY(k), nPrecision));
    2260             :                     }
    2261             :                 }
    2262             :             }
    2263           2 :             break;
    2264             :         }
    2265             : 
    2266           2 :         case wkbGeometryCollection:
    2267             :         {
    2268           2 :             const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    2269           2 :             json_object *geometries = json_object_new_array();
    2270           2 :             json_object_object_add(geometry, "geometries", geometries);
    2271          14 :             for (auto &&poSubGeom : *poGC)
    2272             :             {
    2273          12 :                 json_object *subgeom = json_object_new_object();
    2274          12 :                 json_object_array_add(geometries, subgeom);
    2275          12 :                 BuildGeoJSONGeometry(subgeom, poSubGeom);
    2276             :             }
    2277           2 :             break;
    2278             :         }
    2279             : 
    2280           0 :         default:
    2281           0 :             break;
    2282             :     }
    2283          19 : }
    2284             : 
    2285             : /************************************************************************/
    2286             : /*                       WriteMapIfNecessary()                          */
    2287             : /************************************************************************/
    2288             : 
    2289          86 : OGRErr OGRElasticLayer::WriteMapIfNecessary()
    2290             : {
    2291          86 :     if (m_bManualMapping)
    2292          11 :         return OGRERR_NONE;
    2293             : 
    2294             :     // Check to see if the user has elected to only write out the mapping file
    2295             :     // This method will only write out one layer from the vector file in cases
    2296             :     // where there are multiple layers
    2297          75 :     if (!m_osWriteMapFilename.empty())
    2298             :     {
    2299           4 :         if (m_bSerializeMapping)
    2300             :         {
    2301           2 :             m_bSerializeMapping = false;
    2302           4 :             CPLString map = BuildMap();
    2303             : 
    2304             :             // Write the map to a file
    2305           2 :             VSILFILE *f = VSIFOpenL(m_osWriteMapFilename, "wb");
    2306           2 :             if (f)
    2307             :             {
    2308           2 :                 VSIFWriteL(map.c_str(), 1, map.length(), f);
    2309           2 :                 VSIFCloseL(f);
    2310             :             }
    2311             :         }
    2312           4 :         return OGRERR_NONE;
    2313             :     }
    2314             : 
    2315             :     // Check to see if we have any fields to upload to this index
    2316          71 :     if (m_osWriteMapFilename.empty() && m_bSerializeMapping)
    2317             :     {
    2318          11 :         m_bSerializeMapping = false;
    2319          11 :         CPLString osURL = BuildMappingURL(true);
    2320          11 :         if (!m_poDS->UploadFile(osURL.c_str(), BuildMap()))
    2321             :         {
    2322           1 :             return OGRERR_FAILURE;
    2323             :         }
    2324             :     }
    2325             : 
    2326          70 :     return OGRERR_NONE;
    2327             : }
    2328             : 
    2329             : /************************************************************************/
    2330             : /*                      GetContainerForFeature()                        */
    2331             : /************************************************************************/
    2332             : 
    2333             : static json_object *
    2334          66 : GetContainerForFeature(json_object *poContainer,
    2335             :                        const std::vector<CPLString> &aosPath,
    2336             :                        std::map<std::vector<CPLString>, json_object *> &oMap)
    2337             : {
    2338          66 :     std::vector<CPLString> aosSubPath;
    2339         121 :     for (int j = 0; j < (int)aosPath.size() - 1; j++)
    2340             :     {
    2341          55 :         aosSubPath.push_back(aosPath[j]);
    2342             :         std::map<std::vector<CPLString>, json_object *>::iterator oIter =
    2343          55 :             oMap.find(aosSubPath);
    2344          55 :         if (oIter == oMap.end())
    2345             :         {
    2346          22 :             json_object *poNewContainer = json_object_new_object();
    2347          22 :             json_object_object_add(poContainer, aosPath[j], poNewContainer);
    2348          22 :             oMap[aosSubPath] = poNewContainer;
    2349          22 :             poContainer = poNewContainer;
    2350             :         }
    2351             :         else
    2352             :         {
    2353          33 :             poContainer = oIter->second;
    2354             :         }
    2355             :     }
    2356         132 :     return poContainer;
    2357             : }
    2358             : 
    2359             : /************************************************************************/
    2360             : /*                        BuildMappingURL()                             */
    2361             : /************************************************************************/
    2362          90 : CPLString OGRElasticLayer::BuildMappingURL(bool bMappingApi)
    2363             : {
    2364             :     CPLString osURL =
    2365          90 :         CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str());
    2366          90 :     if (bMappingApi)
    2367          11 :         osURL += "/_mapping";
    2368          90 :     if (m_poDS->m_nMajorVersion < 7)
    2369          85 :         osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    2370          90 :     return osURL;
    2371             : }
    2372             : 
    2373             : /************************************************************************/
    2374             : /*                        BuildJSonFromFeature()                        */
    2375             : /************************************************************************/
    2376             : 
    2377          24 : CPLString OGRElasticLayer::BuildJSonFromFeature(OGRFeature *poFeature)
    2378             : {
    2379             : 
    2380          24 :     CPLString fields;
    2381          24 :     int nJSonFieldIndex = m_poFeatureDefn->GetFieldIndex("_json");
    2382          25 :     if (nJSonFieldIndex >= 0 &&
    2383           1 :         poFeature->IsFieldSetAndNotNull(nJSonFieldIndex))
    2384             :     {
    2385           1 :         fields = poFeature->GetFieldAsString(nJSonFieldIndex);
    2386             :     }
    2387             :     else
    2388             :     {
    2389          23 :         json_object *fieldObject = json_object_new_object();
    2390             : 
    2391          23 :         if (poFeature->GetFID() >= 0 && !m_osFID.empty())
    2392             :         {
    2393           9 :             json_object_object_add(fieldObject, m_osFID.c_str(),
    2394           9 :                                    json_object_new_int64(poFeature->GetFID()));
    2395             :         }
    2396             : 
    2397          46 :         std::map<std::vector<CPLString>, json_object *> oMap;
    2398             : 
    2399          47 :         for (int i = 0; i < poFeature->GetGeomFieldCount(); i++)
    2400             :         {
    2401          24 :             OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
    2402          24 :             if (poGeom != nullptr && !poGeom->IsEmpty())
    2403             :             {
    2404          13 :                 OGREnvelope env;
    2405          13 :                 poGeom->getEnvelope(&env);
    2406             : 
    2407          13 :                 if (m_apoCT[i] != nullptr)
    2408           1 :                     poGeom->transform(m_apoCT[i]);
    2409          12 :                 else if (env.MinX < -180 || env.MinY < -90 || env.MaxX > 180 ||
    2410          11 :                          env.MaxY > 90)
    2411             :                 {
    2412           1 :                     CPLErrorOnce(
    2413             :                         CE_Warning, CPLE_AppDefined,
    2414             :                         "At least one geometry has a bounding box outside "
    2415             :                         "of [-180,180] longitude range and/or [-90,90] "
    2416             :                         "latitude range. Undefined behavior");
    2417             :                 }
    2418             : 
    2419          26 :                 std::vector<CPLString> aosPath = m_aaosGeomFieldPaths[i];
    2420          13 :                 bool bAddGeoJSONType = false;
    2421          17 :                 if (m_abIsGeoPoint[i] && aosPath.size() >= 2 &&
    2422           4 :                     aosPath.back() == "coordinates")
    2423             :                 {
    2424           4 :                     bAddGeoJSONType = true;
    2425           4 :                     aosPath.resize((int)aosPath.size() - 1);
    2426             :                 }
    2427             : 
    2428             :                 json_object *poContainer =
    2429          13 :                     GetContainerForFeature(fieldObject, aosPath, oMap);
    2430          13 :                 const char *pszLastComponent = aosPath.back();
    2431             : 
    2432          13 :                 if (m_abIsGeoPoint[i])
    2433             :                 {
    2434           5 :                     json_object *coordinates = json_object_new_array();
    2435           5 :                     const int nPrecision = 10;
    2436           5 :                     json_object_array_add(
    2437             :                         coordinates,
    2438             :                         json_object_new_double_with_precision(
    2439           5 :                             (env.MaxX + env.MinX) * 0.5, nPrecision));
    2440           5 :                     json_object_array_add(
    2441             :                         coordinates,
    2442             :                         json_object_new_double_with_precision(
    2443           5 :                             (env.MaxY + env.MinY) * 0.5, nPrecision));
    2444             : 
    2445           5 :                     if (bAddGeoJSONType)
    2446             :                     {
    2447           4 :                         json_object *geometry = json_object_new_object();
    2448           4 :                         json_object_object_add(poContainer, pszLastComponent,
    2449             :                                                geometry);
    2450           4 :                         json_object_object_add(geometry, "type",
    2451             :                                                json_object_new_string("Point"));
    2452           4 :                         json_object_object_add(geometry, "coordinates",
    2453             :                                                coordinates);
    2454             :                     }
    2455             :                     else
    2456             :                     {
    2457           1 :                         json_object_object_add(poContainer, pszLastComponent,
    2458             :                                                coordinates);
    2459             :                     }
    2460             :                 }
    2461             :                 else
    2462             :                 {
    2463           8 :                     if (m_bGeoShapeAsGeoJSON)
    2464             :                     {
    2465           7 :                         json_object *geometry = json_object_new_object();
    2466           7 :                         json_object_object_add(poContainer, pszLastComponent,
    2467             :                                                geometry);
    2468           7 :                         BuildGeoJSONGeometry(geometry, poGeom);
    2469             :                     }
    2470             :                     else
    2471             :                     {
    2472           1 :                         char *pszWKT = nullptr;
    2473           1 :                         poGeom->exportToWkt(&pszWKT);
    2474           1 :                         json_object_object_add(poContainer, pszLastComponent,
    2475             :                                                json_object_new_string(pszWKT));
    2476           1 :                         CPLFree(pszWKT);
    2477             :                     }
    2478             :                 }
    2479             :             }
    2480             :         }
    2481             : 
    2482          23 :         if (m_osMappingName == "FeatureCollection")
    2483             :         {
    2484          37 :             if (poFeature->GetGeomFieldCount() == 1 &&
    2485          17 :                 poFeature->GetGeomFieldRef(0))
    2486             :             {
    2487           7 :                 json_object_object_add(fieldObject, "type",
    2488             :                                        json_object_new_string("Feature"));
    2489             :             }
    2490             : 
    2491          40 :             std::vector<CPLString> aosPath;
    2492          20 :             aosPath.push_back("properties");
    2493          20 :             aosPath.push_back("dummy");
    2494          20 :             GetContainerForFeature(fieldObject, aosPath, oMap);
    2495             :         }
    2496             : 
    2497             :         // For every field (except _id)
    2498          23 :         int fieldCount = m_poFeatureDefn->GetFieldCount();
    2499         115 :         for (int i = 1; i < fieldCount; i++)
    2500             :         {
    2501          92 :             if (!poFeature->IsFieldSet(i))
    2502             :             {
    2503          59 :                 continue;
    2504             :             }
    2505             : 
    2506             :             json_object *poContainer =
    2507          33 :                 GetContainerForFeature(fieldObject, m_aaosFieldPaths[i], oMap);
    2508          33 :             const char *pszLastComponent = m_aaosFieldPaths[i].back();
    2509             : 
    2510          33 :             if (poFeature->IsFieldNull(i))
    2511             :             {
    2512           1 :                 json_object_object_add(poContainer, pszLastComponent, nullptr);
    2513           1 :                 continue;
    2514             :             }
    2515             : 
    2516          32 :             switch (m_poFeatureDefn->GetFieldDefn(i)->GetType())
    2517             :             {
    2518           5 :                 case OFTInteger:
    2519           5 :                     if (m_poFeatureDefn->GetFieldDefn(i)->GetSubType() ==
    2520             :                         OFSTBoolean)
    2521           2 :                         json_object_object_add(
    2522             :                             poContainer, pszLastComponent,
    2523             :                             json_object_new_boolean(
    2524             :                                 poFeature->GetFieldAsInteger(i)));
    2525             :                     else
    2526           3 :                         json_object_object_add(
    2527             :                             poContainer, pszLastComponent,
    2528             :                             json_object_new_int(
    2529             :                                 poFeature->GetFieldAsInteger(i)));
    2530           5 :                     break;
    2531           2 :                 case OFTInteger64:
    2532           2 :                     json_object_object_add(
    2533             :                         poContainer, pszLastComponent,
    2534             :                         json_object_new_int64(
    2535           2 :                             poFeature->GetFieldAsInteger64(i)));
    2536           2 :                     break;
    2537           2 :                 case OFTReal:
    2538           2 :                     json_object_object_add(
    2539             :                         poContainer, pszLastComponent,
    2540             :                         json_object_new_double_with_significant_figures(
    2541             :                             poFeature->GetFieldAsDouble(i), -1));
    2542           2 :                     break;
    2543           2 :                 case OFTIntegerList:
    2544             :                 {
    2545           2 :                     int nCount = 0;
    2546             :                     const int *panValues =
    2547           2 :                         poFeature->GetFieldAsIntegerList(i, &nCount);
    2548           2 :                     json_object *poArray = json_object_new_array();
    2549           6 :                     for (int j = 0; j < nCount; j++)
    2550           4 :                         json_object_array_add(
    2551           4 :                             poArray, json_object_new_int(panValues[j]));
    2552           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2553             :                                            poArray);
    2554           2 :                     break;
    2555             :                 }
    2556           2 :                 case OFTInteger64List:
    2557             :                 {
    2558           2 :                     int nCount = 0;
    2559             :                     const GIntBig *panValues =
    2560           2 :                         poFeature->GetFieldAsInteger64List(i, &nCount);
    2561           2 :                     json_object *poArray = json_object_new_array();
    2562           6 :                     for (int j = 0; j < nCount; j++)
    2563           4 :                         json_object_array_add(
    2564           4 :                             poArray, json_object_new_int64(panValues[j]));
    2565           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2566             :                                            poArray);
    2567           2 :                     break;
    2568             :                 }
    2569           2 :                 case OFTRealList:
    2570             :                 {
    2571           2 :                     int nCount = 0;
    2572             :                     const double *padfValues =
    2573           2 :                         poFeature->GetFieldAsDoubleList(i, &nCount);
    2574           2 :                     json_object *poArray = json_object_new_array();
    2575           6 :                     for (int j = 0; j < nCount; j++)
    2576           4 :                         json_object_array_add(
    2577             :                             poArray,
    2578             :                             json_object_new_double_with_significant_figures(
    2579           4 :                                 padfValues[j], -1));
    2580           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2581             :                                            poArray);
    2582           2 :                     break;
    2583             :                 }
    2584           2 :                 case OFTStringList:
    2585             :                 {
    2586           2 :                     char **papszValues = poFeature->GetFieldAsStringList(i);
    2587           2 :                     json_object *poArray = json_object_new_array();
    2588           6 :                     for (int j = 0; papszValues[j] != nullptr; j++)
    2589           4 :                         json_object_array_add(
    2590           4 :                             poArray, json_object_new_string(papszValues[j]));
    2591           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2592             :                                            poArray);
    2593           2 :                     break;
    2594             :                 }
    2595           2 :                 case OFTBinary:
    2596             :                 {
    2597           2 :                     int nCount = 0;
    2598           2 :                     GByte *pabyVal = poFeature->GetFieldAsBinary(i, &nCount);
    2599           2 :                     char *pszVal = CPLBase64Encode(nCount, pabyVal);
    2600           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2601             :                                            json_object_new_string(pszVal));
    2602           2 :                     CPLFree(pszVal);
    2603           2 :                     break;
    2604             :                 }
    2605           3 :                 case OFTDateTime:
    2606             :                 {
    2607           3 :                     int nYear = 0;
    2608           3 :                     int nMonth = 0;
    2609           3 :                     int nDay = 0;
    2610           3 :                     int nHour = 0;
    2611           3 :                     int nMin = 0;
    2612           3 :                     int nTZ = 0;
    2613           3 :                     float fSec = 0.0f;
    2614           3 :                     poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay,
    2615             :                                                   &nHour, &nMin, &fSec, &nTZ);
    2616           3 :                     if (nTZ == 0)
    2617             :                     {
    2618           2 :                         json_object_object_add(
    2619             :                             poContainer, pszLastComponent,
    2620             :                             json_object_new_string(CPLSPrintf(
    2621             :                                 "%04d/%02d/%02d %02d:%02d:%06.3f", nYear,
    2622             :                                 nMonth, nDay, nHour, nMin, fSec)));
    2623             :                     }
    2624             :                     else
    2625             :                     {
    2626           1 :                         const int TZOffset = std::abs(nTZ - 100) * 15;
    2627           1 :                         const int TZHour = TZOffset / 60;
    2628           1 :                         const int TZMinute = TZOffset - TZHour * 60;
    2629           1 :                         json_object_object_add(
    2630             :                             poContainer, pszLastComponent,
    2631             :                             json_object_new_string(CPLSPrintf(
    2632             :                                 "%04d/%02d/%02d %02d:%02d:%06.3f%c%02d:%02d",
    2633             :                                 nYear, nMonth, nDay, nHour, nMin, fSec,
    2634           1 :                                 (nTZ >= 100) ? '+' : '-', TZHour, TZMinute)));
    2635             :                     }
    2636           3 :                     break;
    2637             :                 }
    2638          10 :                 default:
    2639             :                 {
    2640          10 :                     const char *pszVal = poFeature->GetFieldAsString(i);
    2641          10 :                     json_object_object_add(poContainer, pszLastComponent,
    2642             :                                            json_object_new_string(pszVal));
    2643             :                 }
    2644             :             }
    2645             :         }
    2646             : 
    2647             :         // Build the field string
    2648          23 :         fields = json_object_to_json_string(fieldObject);
    2649          23 :         json_object_put(fieldObject);
    2650             :     }
    2651             : 
    2652          24 :     return fields;
    2653             : }
    2654             : 
    2655             : /************************************************************************/
    2656             : /*                          ICreateFeature()                            */
    2657             : /************************************************************************/
    2658             : 
    2659          20 : OGRErr OGRElasticLayer::ICreateFeature(OGRFeature *poFeature)
    2660             : {
    2661          20 :     if (m_poDS->GetAccess() != GA_Update)
    2662             :     {
    2663           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2664             :                  "Dataset opened in read-only mode");
    2665           1 :         return OGRERR_FAILURE;
    2666             :     }
    2667             : 
    2668          19 :     FinalizeFeatureDefn();
    2669             : 
    2670          19 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    2671           1 :         return OGRERR_FAILURE;
    2672             : 
    2673          18 :     if (!m_osWriteMapFilename.empty())
    2674           2 :         return OGRERR_NONE;
    2675             : 
    2676          16 :     if (poFeature->GetFID() < 0)
    2677             :     {
    2678          14 :         if (m_nNextFID < 0)
    2679           1 :             m_nNextFID = GetFeatureCount(FALSE);
    2680          14 :         poFeature->SetFID(++m_nNextFID);
    2681             :     }
    2682             : 
    2683          32 :     CPLString osFields(BuildJSonFromFeature(poFeature));
    2684             : 
    2685          16 :     const char *pszId = nullptr;
    2686          16 :     if (poFeature->IsFieldSetAndNotNull(0) && !m_bIgnoreSourceID)
    2687           1 :         pszId = poFeature->GetFieldAsString(0);
    2688             : 
    2689             :     // Check to see if we're using bulk uploading
    2690          16 :     if (m_nBulkUpload > 0)
    2691             :     {
    2692             :         m_osBulkContent +=
    2693           7 :             CPLSPrintf("{\"index\" :{\"_index\":\"%s\"", m_osIndexName.c_str());
    2694           7 :         if (m_poDS->m_nMajorVersion < 7)
    2695             :             m_osBulkContent +=
    2696           6 :                 CPLSPrintf(", \"_type\":\"%s\"", m_osMappingName.c_str());
    2697           7 :         if (pszId)
    2698           0 :             m_osBulkContent += CPLSPrintf(",\"_id\":\"%s\"", pszId);
    2699           7 :         m_osBulkContent += "}}\n" + osFields + "\n\n";
    2700             : 
    2701             :         // Only push the data if we are over our bulk upload limit
    2702           7 :         if ((int)m_osBulkContent.length() > m_nBulkUpload)
    2703             :         {
    2704           0 :             if (!PushIndex())
    2705             :             {
    2706           0 :                 return OGRERR_FAILURE;
    2707             :             }
    2708             :         }
    2709             :     }
    2710             :     else
    2711             :     {
    2712             :         // Fall back to using single item upload for every feature.
    2713           9 :         CPLString osURL(BuildMappingURL(false));
    2714           9 :         if (pszId)
    2715           1 :             osURL += CPLSPrintf("/%s", pszId);
    2716           9 :         json_object *poRes = m_poDS->RunRequest(osURL, osFields);
    2717           9 :         if (poRes == nullptr)
    2718             :         {
    2719           1 :             return OGRERR_FAILURE;
    2720             :         }
    2721           8 :         if (pszId == nullptr)
    2722             :         {
    2723           7 :             json_object *poId = CPL_json_object_object_get(poRes, "_id");
    2724           8 :             if (poId != nullptr &&
    2725           1 :                 json_object_get_type(poId) == json_type_string)
    2726             :             {
    2727           1 :                 pszId = json_object_get_string(poId);
    2728           1 :                 poFeature->SetField(0, pszId);
    2729             :             }
    2730             :         }
    2731           8 :         json_object_put(poRes);
    2732             :     }
    2733             : 
    2734          15 :     return OGRERR_NONE;
    2735             : }
    2736             : 
    2737             : /************************************************************************/
    2738             : /*                           ISetFeature()                              */
    2739             : /************************************************************************/
    2740             : 
    2741           6 : OGRErr OGRElasticLayer::ISetFeature(OGRFeature *poFeature)
    2742             : {
    2743           6 :     if (m_poDS->GetAccess() != GA_Update)
    2744             :     {
    2745           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2746             :                  "Dataset opened in read-only mode");
    2747           1 :         return OGRERR_FAILURE;
    2748             :     }
    2749             : 
    2750           5 :     FinalizeFeatureDefn();
    2751             : 
    2752           5 :     if (!poFeature->IsFieldSetAndNotNull(0))
    2753             :     {
    2754           1 :         CPLError(CE_Failure, CPLE_AppDefined, "_id field not set");
    2755           1 :         return OGRERR_FAILURE;
    2756             :     }
    2757           4 :     if (poFeature->GetFID() < 0 && !m_osFID.empty())
    2758             :     {
    2759           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid FID");
    2760           0 :         return OGRERR_FAILURE;
    2761             :     }
    2762             : 
    2763           4 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    2764           0 :         return OGRERR_FAILURE;
    2765           4 :     PushIndex();
    2766             : 
    2767           8 :     CPLString osFields(BuildJSonFromFeature(poFeature));
    2768             : 
    2769             :     // TODO? we should theoretically detect if the provided _id doesn't exist
    2770             :     CPLString osURL(
    2771           8 :         CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str()));
    2772           4 :     if (m_poDS->m_nMajorVersion < 7)
    2773           4 :         osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    2774           4 :     osURL += CPLSPrintf("/%s", poFeature->GetFieldAsString(0));
    2775           4 :     json_object *poRes = m_poDS->RunRequest(osURL, osFields);
    2776           4 :     if (poRes == nullptr)
    2777             :     {
    2778           1 :         return OGRERR_FAILURE;
    2779             :     }
    2780             :     // CPLDebug("ES", "SetFeature(): %s", json_object_to_json_string(poRes));
    2781           3 :     json_object_put(poRes);
    2782             : 
    2783           3 :     return OGRERR_NONE;
    2784             : }
    2785             : 
    2786             : /************************************************************************/
    2787             : /*                          IUpsertFeature()                            */
    2788             : /************************************************************************/
    2789             : 
    2790           5 : OGRErr OGRElasticLayer::IUpsertFeature(OGRFeature *poFeature)
    2791             : {
    2792           5 :     if (m_poDS->GetAccess() != GA_Update)
    2793             :     {
    2794           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2795             :                  "Dataset opened in read-only mode");
    2796           1 :         return OGRERR_FAILURE;
    2797             :     }
    2798             : 
    2799           4 :     FinalizeFeatureDefn();
    2800             : 
    2801           4 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    2802           0 :         return OGRERR_FAILURE;
    2803             : 
    2804           4 :     if (!m_osWriteMapFilename.empty())
    2805           0 :         return OGRERR_NONE;
    2806             : 
    2807           4 :     if (poFeature->GetFID() < 0)
    2808             :     {
    2809           1 :         if (m_nNextFID < 0)
    2810           0 :             m_nNextFID = GetFeatureCount(FALSE);
    2811           1 :         poFeature->SetFID(++m_nNextFID);
    2812             :     }
    2813             : 
    2814           8 :     CPLString osFields(BuildJSonFromFeature(poFeature));
    2815             : 
    2816           4 :     const char *pszId = nullptr;
    2817           4 :     if (poFeature->IsFieldSetAndNotNull(0))
    2818             :     {
    2819           4 :         pszId = poFeature->GetFieldAsString(0);
    2820             :     }
    2821             :     else
    2822             :     {
    2823           0 :         return OGRERR_FAILURE;
    2824             :     }
    2825             : 
    2826             :     // Check to see if we're using bulk uploading
    2827           4 :     if (m_nBulkUpload > 0)
    2828             :     {
    2829             :         m_osBulkContent +=
    2830             :             CPLSPrintf("{\"update\":{\"_index\":\"%s\",\"_id\":\"%s\"",
    2831           2 :                        m_osIndexName.c_str(), pszId);
    2832           2 :         if (m_poDS->m_nMajorVersion < 7)
    2833             :         {
    2834             :             m_osBulkContent +=
    2835           2 :                 CPLSPrintf(", \"_type\":\"%s\"", m_osMappingName.c_str());
    2836             :         }
    2837             :         m_osBulkContent +=
    2838           2 :             "}}\n{\"doc\":" + osFields + ",\"doc_as_upsert\":true}\n\n";
    2839             : 
    2840             :         // Only push the data if we are over our bulk upload limit
    2841           2 :         if (m_osBulkContent.length() > static_cast<size_t>(m_nBulkUpload))
    2842             :         {
    2843           0 :             if (!PushIndex())
    2844             :             {
    2845           0 :                 return OGRERR_FAILURE;
    2846             :             }
    2847             :         }
    2848             :     }
    2849             :     else
    2850             :     {
    2851             :         // Fall back to using single item upload for every feature.
    2852           2 :         CPLString osURL(BuildMappingURL(false));
    2853           2 :         if (m_poDS->m_nMajorVersion < 7)
    2854             :         {
    2855           2 :             osURL += CPLSPrintf("/%s/_update", pszId);
    2856             :         }
    2857             :         else
    2858             :         {
    2859           0 :             osURL += CPLSPrintf("/_update/%s", pszId);
    2860             :         }
    2861             : 
    2862             :         const CPLString osUpdate =
    2863           2 :             CPLSPrintf("{\"doc\":%s,\"doc_as_upsert\":true}", osFields.c_str());
    2864           2 :         const CPLString osMethod = "POST";
    2865           2 :         if (!m_poDS->UploadFile(osURL, osUpdate, osMethod))
    2866             :         {
    2867           0 :             return OGRERR_FAILURE;
    2868             :         }
    2869             :     }
    2870             : 
    2871           4 :     return OGRERR_NONE;
    2872             : }
    2873             : 
    2874             : /************************************************************************/
    2875             : /*                             PushIndex()                              */
    2876             : /************************************************************************/
    2877             : 
    2878          63 : bool OGRElasticLayer::PushIndex()
    2879             : {
    2880          63 :     if (m_osBulkContent.empty())
    2881             :     {
    2882          56 :         return true;
    2883             :     }
    2884             : 
    2885          14 :     const bool bRet = m_poDS->UploadFile(
    2886           7 :         CPLSPrintf("%s/_bulk", m_poDS->GetURL()), m_osBulkContent);
    2887           7 :     m_osBulkContent.clear();
    2888             : 
    2889           7 :     return bRet;
    2890             : }
    2891             : 
    2892             : /************************************************************************/
    2893             : /*                            CreateField()                             */
    2894             : /************************************************************************/
    2895             : 
    2896          17 : OGRErr OGRElasticLayer::CreateField(const OGRFieldDefn *poFieldDefn,
    2897             :                                     int /*bApproxOK*/)
    2898             : {
    2899          17 :     if (m_poDS->GetAccess() != GA_Update)
    2900             :     {
    2901           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2902             :                  "Dataset opened in read-only mode");
    2903           1 :         return OGRERR_FAILURE;
    2904             :     }
    2905             : 
    2906          16 :     FinalizeFeatureDefn();
    2907          16 :     ResetReading();
    2908             : 
    2909          16 :     if (m_poFeatureDefn->GetFieldIndex(poFieldDefn->GetNameRef()) >= 0)
    2910             :     {
    2911           0 :         if (!EQUAL(poFieldDefn->GetNameRef(), "_id") &&
    2912           0 :             !EQUAL(poFieldDefn->GetNameRef(), "_json"))
    2913             :         {
    2914           0 :             CPLError(
    2915             :                 CE_Failure, CPLE_AppDefined,
    2916             :                 "CreateField() called with an already existing field name: %s",
    2917             :                 poFieldDefn->GetNameRef());
    2918             :         }
    2919           0 :         return OGRERR_FAILURE;
    2920             :     }
    2921             : 
    2922          16 :     std::vector<CPLString> aosPath;
    2923          16 :     if (m_osMappingName == "FeatureCollection")
    2924          14 :         aosPath.push_back("properties");
    2925             : 
    2926          16 :     if (m_bDotAsNestedField)
    2927             :     {
    2928             :         char **papszTokens =
    2929          16 :             CSLTokenizeString2(poFieldDefn->GetNameRef(), ".", 0);
    2930          33 :         for (int i = 0; papszTokens[i]; i++)
    2931          17 :             aosPath.push_back(papszTokens[i]);
    2932          16 :         CSLDestroy(papszTokens);
    2933             :     }
    2934             :     else
    2935           0 :         aosPath.push_back(poFieldDefn->GetNameRef());
    2936             : 
    2937          16 :     AddFieldDefn(poFieldDefn->GetNameRef(), poFieldDefn->GetType(), aosPath,
    2938             :                  poFieldDefn->GetSubType());
    2939             : 
    2940          16 :     m_bSerializeMapping = true;
    2941             : 
    2942          16 :     return OGRERR_NONE;
    2943             : }
    2944             : 
    2945             : /************************************************************************/
    2946             : /*                           CreateGeomField()                          */
    2947             : /************************************************************************/
    2948             : 
    2949          20 : OGRErr OGRElasticLayer::CreateGeomField(const OGRGeomFieldDefn *poFieldIn,
    2950             :                                         int /*bApproxOK*/)
    2951             : 
    2952             : {
    2953          20 :     if (m_poDS->GetAccess() != GA_Update)
    2954             :     {
    2955           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2956             :                  "Dataset opened in read-only mode");
    2957           1 :         return OGRERR_FAILURE;
    2958             :     }
    2959             : 
    2960          19 :     FinalizeFeatureDefn();
    2961          19 :     ResetReading();
    2962             : 
    2963          19 :     if (m_poFeatureDefn->GetGeomFieldIndex(poFieldIn->GetNameRef()) >= 0)
    2964             :     {
    2965           0 :         CPLError(
    2966             :             CE_Failure, CPLE_AppDefined,
    2967             :             "CreateGeomField() called with an already existing field name: %s",
    2968             :             poFieldIn->GetNameRef());
    2969           0 :         return OGRERR_FAILURE;
    2970             :     }
    2971             : 
    2972          38 :     OGRGeomFieldDefn oFieldDefn(poFieldIn);
    2973          19 :     auto poSRSOri = poFieldIn->GetSpatialRef();
    2974          19 :     if (poSRSOri)
    2975             :     {
    2976          17 :         auto poSRS = poSRSOri->Clone();
    2977          17 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2978          17 :         oFieldDefn.SetSpatialRef(poSRS);
    2979          17 :         poSRS->Release();
    2980             :     }
    2981          19 :     if (EQUAL(oFieldDefn.GetNameRef(), ""))
    2982           0 :         oFieldDefn.SetName("geometry");
    2983             : 
    2984          19 :     std::vector<CPLString> aosPath;
    2985          19 :     if (m_bDotAsNestedField)
    2986             :     {
    2987             :         char **papszTokens =
    2988          19 :             CSLTokenizeString2(oFieldDefn.GetNameRef(), ".", 0);
    2989          39 :         for (int i = 0; papszTokens[i]; i++)
    2990          20 :             aosPath.push_back(papszTokens[i]);
    2991          19 :         CSLDestroy(papszTokens);
    2992             :     }
    2993             :     else
    2994           0 :         aosPath.push_back(oFieldDefn.GetNameRef());
    2995             : 
    2996          37 :     if (m_eGeomTypeMapping == ES_GEOMTYPE_GEO_SHAPE ||
    2997          35 :         (m_eGeomTypeMapping == ES_GEOMTYPE_AUTO &&
    2998          17 :          poFieldIn->GetType() != wkbPoint))
    2999             :     {
    3000          16 :         m_abIsGeoPoint.push_back(FALSE);
    3001             :     }
    3002             :     else
    3003             :     {
    3004           3 :         m_abIsGeoPoint.push_back(TRUE);
    3005           3 :         aosPath.push_back("coordinates");
    3006             :     }
    3007             : 
    3008          19 :     m_aaosGeomFieldPaths.push_back(aosPath);
    3009             : 
    3010          19 :     m_aosMapToGeomFieldIndex[BuildPathFromArray(aosPath)] =
    3011          19 :         m_poFeatureDefn->GetGeomFieldCount();
    3012             : 
    3013          19 :     m_poFeatureDefn->AddGeomFieldDefn(&oFieldDefn);
    3014             : 
    3015          19 :     OGRCoordinateTransformation *poCT = nullptr;
    3016          19 :     if (oFieldDefn.GetSpatialRef() != nullptr)
    3017             :     {
    3018          34 :         OGRSpatialReference oSRS_WGS84;
    3019          17 :         oSRS_WGS84.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
    3020          17 :         oSRS_WGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    3021          17 :         if (!oSRS_WGS84.IsSame(oFieldDefn.GetSpatialRef()))
    3022             :         {
    3023           1 :             poCT = OGRCreateCoordinateTransformation(oFieldDefn.GetSpatialRef(),
    3024             :                                                      &oSRS_WGS84);
    3025           1 :             if (poCT == nullptr)
    3026             :             {
    3027           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3028             :                          "On-the-fly reprojection to WGS84 long/lat would be "
    3029             :                          "needed, but instantiation of transformer failed");
    3030             :             }
    3031             :         }
    3032             :     }
    3033             :     else
    3034             :     {
    3035           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    3036             :                  "No SRS given for geometry column %s. SRS is assumed to "
    3037             :                  "be EPSG:4326 (WGS84 long/lat)",
    3038             :                  oFieldDefn.GetNameRef());
    3039             :     }
    3040             : 
    3041          19 :     m_apoCT.push_back(poCT);
    3042             : 
    3043          19 :     m_bSerializeMapping = true;
    3044             : 
    3045          19 :     return OGRERR_NONE;
    3046             : }
    3047             : 
    3048             : /************************************************************************/
    3049             : /*                           TestCapability()                           */
    3050             : /************************************************************************/
    3051             : 
    3052          67 : int OGRElasticLayer::TestCapability(const char *pszCap) const
    3053             : {
    3054          67 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    3055           1 :         return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr;
    3056             : 
    3057          66 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    3058          27 :         return TRUE;
    3059             : 
    3060          39 :     else if (EQUAL(pszCap, OLCSequentialWrite) ||
    3061          38 :              EQUAL(pszCap, OLCUpsertFeature) || EQUAL(pszCap, OLCRandomWrite))
    3062           1 :         return m_poDS->GetAccess() == GA_Update;
    3063          38 :     else if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCCreateGeomField))
    3064           2 :         return m_poDS->GetAccess() == GA_Update;
    3065             :     else
    3066          36 :         return FALSE;
    3067             : }
    3068             : 
    3069             : /************************************************************************/
    3070             : /*                   AddTimeoutTerminateAfterToURL()                    */
    3071             : /************************************************************************/
    3072             : 
    3073          16 : void OGRElasticLayer::AddTimeoutTerminateAfterToURL(CPLString &osURL)
    3074             : {
    3075          16 :     if (!m_osSingleQueryTimeout.empty())
    3076           5 :         osURL += "&timeout=" + m_osSingleQueryTimeout;
    3077          16 :     if (!m_osSingleQueryTerminateAfter.empty())
    3078           5 :         osURL += "&terminate_after=" + m_osSingleQueryTerminateAfter;
    3079          16 : }
    3080             : 
    3081             : /************************************************************************/
    3082             : /*                          GetFeatureCount()                           */
    3083             : /************************************************************************/
    3084             : 
    3085          12 : GIntBig OGRElasticLayer::GetFeatureCount(int bForce)
    3086             : {
    3087          12 :     if (m_bFilterMustBeClientSideEvaluated)
    3088             :     {
    3089           0 :         m_bUseSingleQueryParams = true;
    3090           0 :         const auto nRet = OGRLayer::GetFeatureCount(bForce);
    3091           0 :         m_bUseSingleQueryParams = false;
    3092           0 :         return nRet;
    3093             :     }
    3094             : 
    3095          12 :     json_object *poResponse = nullptr;
    3096          24 :     CPLString osURL(CPLSPrintf("%s", m_poDS->GetURL()));
    3097          24 :     CPLString osFilter = "";
    3098          12 :     if (!m_osESSearch.empty())
    3099             :     {
    3100           1 :         if (m_osESSearch[0] != '{')
    3101           0 :             return OGRLayer::GetFeatureCount(bForce);
    3102           1 :         osURL += "/_search?pretty";
    3103           1 :         osFilter = "{ \"size\": 0 ";
    3104           1 :         if (m_osESSearch == "{}")
    3105           0 :             osFilter += '}';
    3106             :         else
    3107           1 :             osFilter += ", " + m_osESSearch.substr(1);
    3108             :     }
    3109          11 :     else if ((m_poSpatialFilter && m_osJSONFilter.empty()) || m_poJSONFilter)
    3110             :     {
    3111           3 :         osFilter = BuildQuery(true);
    3112           3 :         osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
    3113           3 :         if (m_poDS->m_nMajorVersion < 7)
    3114           2 :             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3115           3 :         if (m_poDS->m_nMajorVersion >= 5 && m_osSingleQueryTimeout.empty())
    3116             :         {
    3117           1 :             osURL += "/_count?pretty";
    3118             :         }
    3119             :         else
    3120             :         {
    3121           2 :             osURL += "/_search?pretty";
    3122             :         }
    3123             :     }
    3124           8 :     else if (!m_osJSONFilter.empty())
    3125             :     {
    3126           2 :         osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
    3127           2 :         if (m_poDS->m_nMajorVersion < 7)
    3128           1 :             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3129           2 :         osURL += "/_search?pretty";
    3130           2 :         osFilter = ("{ \"size\": 0, " + m_osJSONFilter.substr(1));
    3131             :     }
    3132             :     else
    3133             :     {
    3134           6 :         osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
    3135           6 :         if (m_poDS->m_nMajorVersion < 7)
    3136           5 :             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3137           6 :         if (m_osSingleQueryTimeout.empty())
    3138             :         {
    3139           5 :             osURL += "/_count?pretty";
    3140             :         }
    3141             :         else
    3142             :         {
    3143           1 :             osFilter = "{ \"size\": 0 }";
    3144           1 :             osURL += CPLSPrintf("/_search?pretty");
    3145             :         }
    3146             :     }
    3147          12 :     AddTimeoutTerminateAfterToURL(osURL);
    3148             : 
    3149          12 :     poResponse = m_poDS->RunRequest(osURL.c_str(), osFilter.c_str());
    3150             : 
    3151          12 :     json_object *poCount = json_ex_get_object_by_path(poResponse, "hits.count");
    3152          12 :     if (poCount == nullptr)
    3153             :     {
    3154             :         // For _search request
    3155          10 :         poCount = json_ex_get_object_by_path(poResponse, "hits.total");
    3156          10 :         if (poCount && json_object_get_type(poCount) == json_type_object)
    3157             :         {
    3158             :             // Since ES 7.0
    3159           4 :             poCount = json_ex_get_object_by_path(poCount, "value");
    3160             :         }
    3161             :     }
    3162          12 :     if (poCount == nullptr)
    3163             :     {
    3164             :         // For _count request
    3165           4 :         poCount = json_ex_get_object_by_path(poResponse, "count");
    3166             :     }
    3167          12 :     if (poCount == nullptr || json_object_get_type(poCount) != json_type_int)
    3168             :     {
    3169           3 :         json_object_put(poResponse);
    3170           3 :         CPLDebug("ES", "Cannot find hits in GetFeatureCount() response. "
    3171             :                        "Falling back to slow implementation");
    3172           3 :         m_bUseSingleQueryParams = true;
    3173           3 :         const auto nRet = OGRLayer::GetFeatureCount(bForce);
    3174           3 :         m_bUseSingleQueryParams = false;
    3175           3 :         return nRet;
    3176             :     }
    3177             : 
    3178           9 :     GIntBig nCount = json_object_get_int64(poCount);
    3179           9 :     json_object_put(poResponse);
    3180           9 :     return nCount;
    3181             : }
    3182             : 
    3183             : /************************************************************************/
    3184             : /*                            GetValue()                                */
    3185             : /************************************************************************/
    3186             : 
    3187          28 : json_object *OGRElasticLayer::GetValue(int nFieldIdx, swq_expr_node *poValNode)
    3188             : {
    3189          28 :     json_object *poVal = nullptr;
    3190          28 :     if (poValNode->field_type == SWQ_FLOAT)
    3191           1 :         poVal = json_object_new_double(poValNode->float_value);
    3192          27 :     else if (poValNode->field_type == SWQ_INTEGER ||
    3193          26 :              poValNode->field_type == SWQ_INTEGER64)
    3194           2 :         poVal = json_object_new_int64(poValNode->int_value);
    3195          25 :     else if (poValNode->field_type == SWQ_STRING)
    3196          23 :         poVal = json_object_new_string(poValNode->string_value);
    3197           2 :     else if (poValNode->field_type == SWQ_TIMESTAMP)
    3198             :     {
    3199           2 :         int nYear = 0;
    3200           2 :         int nMonth = 0;
    3201           2 :         int nDay = 0;
    3202           2 :         int nHour = 0;
    3203           2 :         int nMinute = 0;
    3204           2 :         float fSecond = 0;
    3205           4 :         if (sscanf(poValNode->string_value, "%04d/%02d/%02d %02d:%02d:%f",
    3206           2 :                    &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) >= 3 ||
    3207           0 :             sscanf(poValNode->string_value, "%04d-%02d-%02dT%02d:%02d:%f",
    3208             :                    &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) >= 3)
    3209             :         {
    3210             :             OGRFieldType eType(
    3211           2 :                 m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType());
    3212           2 :             if (eType == OFTDateTime)
    3213           2 :                 poVal = json_object_new_string(
    3214             :                     CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02.03f", nYear,
    3215             :                                nMonth, nDay, nHour, nMinute, fSecond));
    3216           0 :             else if (eType == OFTDate)
    3217           0 :                 poVal = json_object_new_string(
    3218             :                     CPLSPrintf("%04d/%02d/%02d", nYear, nMonth, nDay));
    3219             :             else
    3220           0 :                 poVal = json_object_new_string(
    3221             :                     CPLSPrintf("%02d:%02d:%02.03f", nHour, nMinute, fSecond));
    3222             :         }
    3223             :         else
    3224             :         {
    3225           0 :             return nullptr;
    3226             :         }
    3227             :     }
    3228             :     else
    3229             :     {
    3230           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unhandled type: %d",
    3231           0 :                  poValNode->field_type);
    3232             :     }
    3233          28 :     return poVal;
    3234             : }
    3235             : 
    3236             : /************************************************************************/
    3237             : /*                      OGRESGetFieldIndexFromSQL()                     */
    3238             : /************************************************************************/
    3239             : 
    3240          31 : static int OGRESGetFieldIndexFromSQL(const swq_expr_node *poNode)
    3241             : {
    3242          31 :     if (poNode->eNodeType == SNT_COLUMN)
    3243          27 :         return poNode->field_index;
    3244             : 
    3245           4 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_CAST &&
    3246           1 :         poNode->nSubExprCount >= 1 &&
    3247           1 :         poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN)
    3248           1 :         return poNode->papoSubExpr[0]->field_index;
    3249             : 
    3250           3 :     return -1;
    3251             : }
    3252             : 
    3253             : /************************************************************************/
    3254             : /*                        TranslateSQLToFilter()                        */
    3255             : /************************************************************************/
    3256             : 
    3257          40 : json_object *OGRElasticLayer::TranslateSQLToFilter(swq_expr_node *poNode)
    3258             : {
    3259          40 :     if (poNode->eNodeType == SNT_OPERATION)
    3260             :     {
    3261          40 :         int nFieldIdx = 0;
    3262          40 :         CPL_IGNORE_RET_VAL(nFieldIdx);  // to make cppcheck happy
    3263          40 :         if (poNode->nOperation == SWQ_AND && poNode->nSubExprCount == 2)
    3264             :         {
    3265             :             // For AND, we can deal with a failure in one of the branch
    3266             :             // since client-side will do that extra filtering
    3267             :             json_object *poFilter1 =
    3268           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[0]);
    3269             :             json_object *poFilter2 =
    3270           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[1]);
    3271           3 :             if (poFilter1 && poFilter2)
    3272             :             {
    3273           1 :                 json_object *poRet = json_object_new_object();
    3274           1 :                 json_object *poBool = json_object_new_object();
    3275           1 :                 json_object_object_add(poRet, "bool", poBool);
    3276           1 :                 json_object *poMust = json_object_new_array();
    3277           1 :                 json_object_object_add(poBool, "must", poMust);
    3278           1 :                 json_object_array_add(poMust, poFilter1);
    3279           1 :                 json_object_array_add(poMust, poFilter2);
    3280          35 :                 return poRet;
    3281             :             }
    3282           2 :             else if (poFilter1)
    3283           1 :                 return poFilter1;
    3284             :             else
    3285           1 :                 return poFilter2;
    3286             :         }
    3287          37 :         else if (poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
    3288             :         {
    3289             :             json_object *poFilter1 =
    3290           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[0]);
    3291             :             json_object *poFilter2 =
    3292           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[1]);
    3293           3 :             if (poFilter1 && poFilter2)
    3294             :             {
    3295           2 :                 json_object *poRet = json_object_new_object();
    3296           2 :                 json_object *poBool = json_object_new_object();
    3297           2 :                 json_object_object_add(poRet, "bool", poBool);
    3298           2 :                 json_object *poShould = json_object_new_array();
    3299           2 :                 json_object_object_add(poBool, "should", poShould);
    3300           2 :                 json_object_array_add(poShould, poFilter1);
    3301           2 :                 json_object_array_add(poShould, poFilter2);
    3302           2 :                 return poRet;
    3303             :             }
    3304             :             else
    3305             :             {
    3306           1 :                 json_object_put(poFilter1);
    3307           1 :                 json_object_put(poFilter2);
    3308           1 :                 return nullptr;
    3309             :             }
    3310             :         }
    3311          34 :         else if (poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
    3312             :         {
    3313           6 :             if (poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
    3314           2 :                 poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
    3315           1 :                 poNode->papoSubExpr[0]->nSubExprCount == 1 &&
    3316           5 :                 poNode->papoSubExpr[0]->papoSubExpr[0]->field_index != 0 &&
    3317           1 :                 poNode->papoSubExpr[0]->papoSubExpr[0]->field_index <
    3318           1 :                     m_poFeatureDefn->GetFieldCount())
    3319             :             {
    3320           1 :                 json_object *poRet = json_object_new_object();
    3321           1 :                 json_object *poExists = json_object_new_object();
    3322             :                 CPLString osFieldName(BuildPathFromArray(
    3323             :                     m_aaosFieldPaths
    3324           1 :                         [poNode->papoSubExpr[0]->papoSubExpr[0]->field_index]));
    3325           1 :                 json_object_object_add(poExists, "field",
    3326             :                                        json_object_new_string(osFieldName));
    3327           1 :                 json_object_object_add(poRet, "exists", poExists);
    3328           1 :                 return poRet;
    3329             :             }
    3330             :             else
    3331             :             {
    3332             :                 json_object *poFilter =
    3333           1 :                     TranslateSQLToFilter(poNode->papoSubExpr[0]);
    3334           1 :                 if (poFilter)
    3335             :                 {
    3336           1 :                     json_object *poRet = json_object_new_object();
    3337           1 :                     json_object *poBool = json_object_new_object();
    3338           1 :                     json_object_object_add(poRet, "bool", poBool);
    3339           1 :                     json_object_object_add(poBool, "must_not", poFilter);
    3340           1 :                     return poRet;
    3341             :                 }
    3342             :                 else
    3343             :                 {
    3344           0 :                     return nullptr;
    3345             :                 }
    3346             :             }
    3347             :         }
    3348          65 :         else if (poNode->nOperation == SWQ_ISNULL &&
    3349           1 :                  poNode->nSubExprCount == 1 &&
    3350           1 :                  (nFieldIdx =
    3351          34 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3352           1 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3353             :         {
    3354           1 :             json_object *poRet = json_object_new_object();
    3355           1 :             json_object *poExists = json_object_new_object();
    3356             :             CPLString osFieldName(
    3357           1 :                 BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3358           1 :             json_object_object_add(poExists, "field",
    3359             :                                    json_object_new_string(osFieldName));
    3360           1 :             json_object *poBool = json_object_new_object();
    3361           1 :             json_object_object_add(poRet, "bool", poBool);
    3362           1 :             json_object *poMustNot = json_object_new_object();
    3363           1 :             json_object_object_add(poMustNot, "exists", poExists);
    3364           1 :             json_object_object_add(poBool, "must_not", poMustNot);
    3365           1 :             return poRet;
    3366             :         }
    3367          31 :         else if (poNode->nOperation == SWQ_NE)
    3368             :         {
    3369           1 :             poNode->nOperation = SWQ_EQ;
    3370           1 :             json_object *poFilter = TranslateSQLToFilter(poNode);
    3371           1 :             poNode->nOperation = SWQ_NE;
    3372           1 :             if (poFilter)
    3373             :             {
    3374           1 :                 json_object *poRet = json_object_new_object();
    3375           1 :                 json_object *poBool = json_object_new_object();
    3376           1 :                 json_object_object_add(poRet, "bool", poBool);
    3377           1 :                 json_object_object_add(poBool, "must_not", poFilter);
    3378           1 :                 return poRet;
    3379             :             }
    3380             :             else
    3381             :             {
    3382           0 :                 return nullptr;
    3383             :             }
    3384             :         }
    3385          16 :         else if (poNode->nOperation == SWQ_EQ && poNode->nSubExprCount == 2 &&
    3386          16 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
    3387          16 :                  (nFieldIdx =
    3388          62 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) >= 0 &&
    3389          13 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3390             :         {
    3391          13 :             json_object *poVal = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
    3392          13 :             if (poVal == nullptr)
    3393             :             {
    3394           0 :                 return nullptr;
    3395             :             }
    3396          13 :             json_object *poRet = json_object_new_object();
    3397          13 :             if (nFieldIdx == 0)
    3398             :             {
    3399           1 :                 json_object *poIds = json_object_new_object();
    3400           1 :                 json_object *poValues = json_object_new_array();
    3401           1 :                 json_object_object_add(poIds, "values", poValues);
    3402           1 :                 json_object_array_add(poValues, poVal);
    3403           1 :                 json_object_object_add(poRet, "ids", poIds);
    3404             :             }
    3405             :             else
    3406             :             {
    3407          12 :                 json_object *poTerm = json_object_new_object();
    3408             :                 CPLString osPath(
    3409          24 :                     BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3410          12 :                 bool bNotAnalyzed = true;
    3411          12 :                 if (poNode->papoSubExpr[1]->field_type == SWQ_STRING)
    3412             :                 {
    3413             :                     const char *pszFieldName =
    3414          12 :                         m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
    3415          12 :                     bNotAnalyzed = CSLFindString(m_papszNotAnalyzedFields,
    3416             :                                                  pszFieldName) >= 0;
    3417          12 :                     if (!bNotAnalyzed)
    3418             :                     {
    3419           3 :                         if (CSLFindString(m_papszFieldsWithRawValue,
    3420           3 :                                           pszFieldName) >= 0)
    3421             :                         {
    3422           1 :                             osPath += ".raw";
    3423           1 :                             bNotAnalyzed = true;
    3424             :                         }
    3425           2 :                         else if (!m_bFilterMustBeClientSideEvaluated)
    3426             :                         {
    3427           2 :                             m_bFilterMustBeClientSideEvaluated = true;
    3428           2 :                             CPLDebug("ES",
    3429             :                                      "Part or full filter will have to be "
    3430             :                                      "evaluated on "
    3431             :                                      "client side (equality test on a analyzed "
    3432             :                                      "field).");
    3433             :                         }
    3434             :                     }
    3435             :                 }
    3436          12 :                 json_object_object_add(poRet, bNotAnalyzed ? "term" : "match",
    3437             :                                        poTerm);
    3438          12 :                 json_object_object_add(poTerm, osPath, poVal);
    3439             : 
    3440          12 :                 if (!bNotAnalyzed && m_poDS->m_nMajorVersion < 2)
    3441             :                 {
    3442           0 :                     json_object *poNewRet = json_object_new_object();
    3443           0 :                     json_object_object_add(poNewRet, "query", poRet);
    3444           0 :                     poRet = poNewRet;
    3445             :                 }
    3446             :             }
    3447          13 :             return poRet;
    3448             :         }
    3449          50 :         else if ((poNode->nOperation == SWQ_LT ||
    3450          16 :                   poNode->nOperation == SWQ_LE ||
    3451          15 :                   poNode->nOperation == SWQ_GT ||
    3452          14 :                   poNode->nOperation == SWQ_GE) &&
    3453           5 :                  poNode->nSubExprCount == 2 &&
    3454           5 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
    3455           5 :                  (nFieldIdx =
    3456          39 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3457           5 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3458             :         {
    3459           5 :             json_object *poVal = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
    3460           5 :             if (poVal == nullptr)
    3461             :             {
    3462           0 :                 return nullptr;
    3463             :             }
    3464           5 :             json_object *poRet = json_object_new_object();
    3465           5 :             json_object *poRange = json_object_new_object();
    3466           5 :             json_object_object_add(poRet, "range", poRange);
    3467           5 :             json_object *poFieldConstraint = json_object_new_object();
    3468             :             CPLString osFieldName(
    3469           5 :                 BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3470           5 :             json_object_object_add(poRange, osFieldName, poFieldConstraint);
    3471           9 :             const char *pszOp = (poNode->nOperation == SWQ_LT)   ? "lt"
    3472           7 :                                 : (poNode->nOperation == SWQ_LE) ? "lte"
    3473           3 :                                 : (poNode->nOperation == SWQ_GT)
    3474           3 :                                     ? "gt"
    3475             :                                     :
    3476             :                                     /*(poNode->nOperation == SWQ_GE) ?*/ "gte";
    3477           5 :             json_object_object_add(poFieldConstraint, pszOp, poVal);
    3478           5 :             return poRet;
    3479             :         }
    3480          25 :         else if (poNode->nOperation == SWQ_BETWEEN &&
    3481           1 :                  poNode->nSubExprCount == 3 &&
    3482           1 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
    3483           1 :                  poNode->papoSubExpr[2]->eNodeType == SNT_CONSTANT &&
    3484           1 :                  (nFieldIdx =
    3485          14 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3486           1 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3487             :         {
    3488           1 :             json_object *poVal1 = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
    3489           1 :             if (poVal1 == nullptr)
    3490             :             {
    3491           0 :                 return nullptr;
    3492             :             }
    3493           1 :             json_object *poVal2 = GetValue(nFieldIdx, poNode->papoSubExpr[2]);
    3494           1 :             if (poVal2 == nullptr)
    3495             :             {
    3496           0 :                 json_object_put(poVal1);
    3497           0 :                 return nullptr;
    3498             :             }
    3499             : 
    3500           1 :             json_object *poRet = json_object_new_object();
    3501           1 :             json_object *poRange = json_object_new_object();
    3502           1 :             json_object_object_add(poRet, "range", poRange);
    3503           1 :             json_object *poFieldConstraint = json_object_new_object();
    3504             :             CPLString osFieldName(
    3505           1 :                 BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3506           1 :             json_object_object_add(poRange, osFieldName, poFieldConstraint);
    3507           1 :             json_object_object_add(poFieldConstraint, "gte", poVal1);
    3508           1 :             json_object_object_add(poFieldConstraint, "lte", poVal2);
    3509           1 :             return poRet;
    3510             :         }
    3511           4 :         else if (poNode->nOperation == SWQ_IN && poNode->nSubExprCount > 1 &&
    3512           4 :                  (nFieldIdx =
    3513          19 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) >= 0 &&
    3514           4 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3515             :         {
    3516           4 :             bool bAllConstant = true;
    3517          12 :             for (int i = 1; i < poNode->nSubExprCount; i++)
    3518             :             {
    3519           8 :                 if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT)
    3520             :                 {
    3521           0 :                     bAllConstant = false;
    3522           0 :                     break;
    3523             :                 }
    3524             :             }
    3525           4 :             if (bAllConstant)
    3526             :             {
    3527           4 :                 json_object *poRet = json_object_new_object();
    3528           4 :                 if (nFieldIdx == 0)
    3529             :                 {
    3530           1 :                     json_object *poIds = json_object_new_object();
    3531           1 :                     json_object *poValues = json_object_new_array();
    3532           1 :                     json_object_object_add(poIds, "values", poValues);
    3533           1 :                     json_object_object_add(poRet, "ids", poIds);
    3534           3 :                     for (int i = 1; i < poNode->nSubExprCount; i++)
    3535             :                     {
    3536             :                         json_object *poVal =
    3537           2 :                             GetValue(nFieldIdx, poNode->papoSubExpr[i]);
    3538           2 :                         if (poVal == nullptr)
    3539             :                         {
    3540           0 :                             json_object_put(poRet);
    3541           0 :                             return nullptr;
    3542             :                         }
    3543           2 :                         json_object_array_add(poValues, poVal);
    3544             :                     }
    3545             :                 }
    3546             :                 else
    3547             :                 {
    3548           3 :                     bool bNotAnalyzed = true;
    3549             :                     CPLString osPath(
    3550           3 :                         BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3551           3 :                     if (poNode->papoSubExpr[1]->field_type == SWQ_STRING)
    3552             :                     {
    3553             :                         const char *pszFieldName =
    3554           3 :                             m_poFeatureDefn->GetFieldDefn(nFieldIdx)
    3555           3 :                                 ->GetNameRef();
    3556           3 :                         bNotAnalyzed = CSLFindString(m_papszNotAnalyzedFields,
    3557             :                                                      pszFieldName) >= 0;
    3558           5 :                         if (!bNotAnalyzed &&
    3559           2 :                             CSLFindString(m_papszFieldsWithRawValue,
    3560             :                                           pszFieldName) >= 0)
    3561             :                         {
    3562           1 :                             osPath += ".raw";
    3563           1 :                             bNotAnalyzed = true;
    3564             :                         }
    3565             : 
    3566           3 :                         if (!bNotAnalyzed &&
    3567           1 :                             !m_bFilterMustBeClientSideEvaluated)
    3568             :                         {
    3569           1 :                             m_bFilterMustBeClientSideEvaluated = true;
    3570           1 :                             CPLDebug("ES",
    3571             :                                      "Part or full filter will have to be "
    3572             :                                      "evaluated on client side (IN test on a "
    3573             :                                      "analyzed field).");
    3574             :                         }
    3575             :                     }
    3576             : 
    3577           3 :                     if (bNotAnalyzed)
    3578             :                     {
    3579           2 :                         json_object *poTerms = json_object_new_object();
    3580           2 :                         json_object_object_add(poRet, "terms", poTerms);
    3581           2 :                         json_object *poTermsValues = json_object_new_array();
    3582           2 :                         json_object_object_add(poTerms, osPath, poTermsValues);
    3583           6 :                         for (int i = 1; i < poNode->nSubExprCount; i++)
    3584             :                         {
    3585             :                             json_object *poVal =
    3586           4 :                                 GetValue(nFieldIdx, poNode->papoSubExpr[i]);
    3587           4 :                             if (poVal == nullptr)
    3588             :                             {
    3589           0 :                                 json_object_put(poRet);
    3590           0 :                                 return nullptr;
    3591             :                             }
    3592           4 :                             json_object_array_add(poTermsValues, poVal);
    3593             :                         }
    3594             :                     }
    3595             :                     else
    3596             :                     {
    3597           1 :                         json_object *poBool = json_object_new_object();
    3598           1 :                         json_object_object_add(poRet, "bool", poBool);
    3599           1 :                         json_object *poShould = json_object_new_array();
    3600           1 :                         json_object_object_add(poBool, "should", poShould);
    3601           3 :                         for (int i = 1; i < poNode->nSubExprCount; i++)
    3602             :                         {
    3603             :                             json_object *poVal =
    3604           2 :                                 GetValue(nFieldIdx, poNode->papoSubExpr[i]);
    3605           2 :                             if (poVal == nullptr)
    3606             :                             {
    3607           0 :                                 json_object_put(poRet);
    3608           0 :                                 return nullptr;
    3609             :                             }
    3610           2 :                             json_object *poShouldElt = json_object_new_object();
    3611           2 :                             json_object *poMatch = json_object_new_object();
    3612           2 :                             json_object_object_add(poShouldElt, "match",
    3613             :                                                    poMatch);
    3614           2 :                             json_object_object_add(poMatch, osPath, poVal);
    3615             : 
    3616           2 :                             if (m_poDS->m_nMajorVersion < 2)
    3617             :                             {
    3618             :                                 json_object *poNewShouldElt =
    3619           0 :                                     json_object_new_object();
    3620           0 :                                 json_object_object_add(poNewShouldElt, "query",
    3621             :                                                        poShouldElt);
    3622           0 :                                 poShouldElt = poNewShouldElt;
    3623             :                             }
    3624           2 :                             json_object_array_add(poShould, poShouldElt);
    3625             :                         }
    3626             :                     }
    3627             :                 }
    3628           4 :                 return poRet;
    3629             :             }
    3630             :         }
    3631          17 :         else if ((poNode->nOperation == SWQ_LIKE ||
    3632           3 :                   poNode->nOperation ==
    3633           4 :                       SWQ_ILIKE) &&  // ES actual semantics doesn't match
    3634             :                                      // exactly either...
    3635           4 :                  poNode->nSubExprCount >= 2 &&
    3636           4 :                  (nFieldIdx =
    3637          18 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3638           4 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3639             :         {
    3640           4 :             char chEscape = '\0';
    3641           4 :             if (poNode->nSubExprCount == 3)
    3642           1 :                 chEscape = poNode->papoSubExpr[2]->string_value[0];
    3643           4 :             const char *pszPattern = poNode->papoSubExpr[1]->string_value;
    3644             :             const char *pszFieldName =
    3645           4 :                 m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
    3646             :             bool bNotAnalyzed =
    3647           4 :                 CSLFindString(m_papszNotAnalyzedFields, pszFieldName) >= 0;
    3648           4 :             CPLString osPath(BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3649           7 :             if (!bNotAnalyzed &&
    3650           3 :                 CSLFindString(m_papszFieldsWithRawValue, pszFieldName) >= 0)
    3651             :             {
    3652           1 :                 osPath += ".raw";
    3653           1 :                 bNotAnalyzed = true;
    3654             :             }
    3655             : 
    3656           4 :             if (strchr(pszPattern, '*') || strchr(pszPattern, '?'))
    3657             :             {
    3658           1 :                 CPLDebug("ES", "Cannot handle * or ? in LIKE pattern");
    3659             :             }
    3660           3 :             else if (!bNotAnalyzed)
    3661             :             {
    3662           1 :                 if (!m_bFilterMustBeClientSideEvaluated)
    3663             :                 {
    3664           1 :                     m_bFilterMustBeClientSideEvaluated = true;
    3665           1 :                     CPLDebug(
    3666             :                         "ES",
    3667             :                         "Part or full filter will have to be evaluated on "
    3668             :                         "client side (wildcard test on a analyzed field).");
    3669             :                 }
    3670             :             }
    3671             :             else
    3672             :             {
    3673           2 :                 CPLString osUnescaped;
    3674           8 :                 for (int i = 0; pszPattern[i] != '\0'; ++i)
    3675             :                 {
    3676           6 :                     if (chEscape == pszPattern[i])
    3677             :                     {
    3678           1 :                         if (pszPattern[i + 1] == '\0')
    3679           0 :                             break;
    3680           1 :                         osUnescaped += pszPattern[i + 1];
    3681           1 :                         i++;
    3682             :                     }
    3683           5 :                     else if (pszPattern[i] == '%')
    3684             :                     {
    3685           2 :                         osUnescaped += '*';
    3686             :                     }
    3687           3 :                     else if (pszPattern[i] == '_')
    3688             :                     {
    3689           2 :                         osUnescaped += '?';
    3690             :                     }
    3691             :                     else
    3692             :                     {
    3693           1 :                         osUnescaped += pszPattern[i];
    3694             :                     }
    3695             :                 }
    3696           2 :                 json_object *poRet = json_object_new_object();
    3697           2 :                 json_object *poWildcard = json_object_new_object();
    3698           2 :                 json_object_object_add(poRet, "wildcard", poWildcard);
    3699           2 :                 json_object_object_add(poWildcard, osPath,
    3700             :                                        json_object_new_string(osUnescaped));
    3701           2 :                 return poRet;
    3702             :             }
    3703             :         }
    3704             :     }
    3705             : 
    3706           5 :     if (!m_bFilterMustBeClientSideEvaluated)
    3707             :     {
    3708           4 :         m_bFilterMustBeClientSideEvaluated = true;
    3709           4 :         CPLDebug("ES", "Part or full filter will have to be evaluated on "
    3710             :                        "client side.");
    3711             :     }
    3712           5 :     return nullptr;
    3713             : }
    3714             : 
    3715             : /************************************************************************/
    3716             : /*                          SetAttributeFilter()                        */
    3717             : /************************************************************************/
    3718             : 
    3719          37 : OGRErr OGRElasticLayer::SetAttributeFilter(const char *pszFilter)
    3720             : {
    3721          37 :     m_bFilterMustBeClientSideEvaluated = false;
    3722          37 :     if (pszFilter != nullptr && pszFilter[0] == '{')
    3723             :     {
    3724           2 :         if (!m_osESSearch.empty())
    3725             :         {
    3726           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3727             :                      "Setting an Elasticsearch filter on a resulting layer "
    3728             :                      "is not supported");
    3729           0 :             return OGRERR_FAILURE;
    3730             :         }
    3731           2 :         OGRLayer::SetAttributeFilter(nullptr);
    3732           2 :         m_osJSONFilter = pszFilter;
    3733           2 :         return OGRERR_NONE;
    3734             :     }
    3735             :     else
    3736             :     {
    3737          35 :         m_osJSONFilter.clear();
    3738          35 :         json_object_put(m_poJSONFilter);
    3739          35 :         m_poJSONFilter = nullptr;
    3740          35 :         OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
    3741          35 :         if (eErr == OGRERR_NONE && m_poAttrQuery != nullptr)
    3742             :         {
    3743             :             swq_expr_node *poNode =
    3744          26 :                 reinterpret_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
    3745          26 :             m_poJSONFilter = TranslateSQLToFilter(poNode);
    3746             :         }
    3747          35 :         return eErr;
    3748             :     }
    3749             : }
    3750             : 
    3751             : /************************************************************************/
    3752             : /*                          ClampEnvelope()                             */
    3753             : /************************************************************************/
    3754             : 
    3755           7 : void OGRElasticLayer::ClampEnvelope(OGREnvelope &sEnvelope)
    3756             : {
    3757           7 :     if (sEnvelope.MinX < -180)
    3758           1 :         sEnvelope.MinX = -180;
    3759           7 :     if (sEnvelope.MinX > 180)
    3760           0 :         sEnvelope.MinX = 180;
    3761             : 
    3762           7 :     if (sEnvelope.MinY < -90)
    3763           1 :         sEnvelope.MinY = -90;
    3764           7 :     if (sEnvelope.MinY > 90)
    3765           0 :         sEnvelope.MinY = 90;
    3766             : 
    3767           7 :     if (sEnvelope.MaxX > 180)
    3768           1 :         sEnvelope.MaxX = 180;
    3769           7 :     if (sEnvelope.MaxX < -180)
    3770           0 :         sEnvelope.MaxX = -180;
    3771             : 
    3772           7 :     if (sEnvelope.MaxY > 90)
    3773           1 :         sEnvelope.MaxY = 90;
    3774           7 :     if (sEnvelope.MaxY < -90)
    3775           0 :         sEnvelope.MaxY = -90;
    3776           7 : }
    3777             : 
    3778             : /************************************************************************/
    3779             : /*                          ISetSpatialFilter()                         */
    3780             : /************************************************************************/
    3781             : 
    3782          14 : OGRErr OGRElasticLayer::ISetSpatialFilter(int iGeomField,
    3783             :                                           const OGRGeometry *poGeomIn)
    3784             : 
    3785             : {
    3786          14 :     FinalizeFeatureDefn();
    3787             : 
    3788          14 :     m_iGeomFieldFilter = iGeomField;
    3789             : 
    3790          14 :     InstallFilter(poGeomIn);
    3791             : 
    3792          14 :     json_object_put(m_poSpatialFilter);
    3793          14 :     m_poSpatialFilter = nullptr;
    3794             : 
    3795          14 :     if (poGeomIn == nullptr)
    3796           9 :         return OGRERR_NONE;
    3797             : 
    3798           5 :     if (!m_osESSearch.empty())
    3799             :     {
    3800           0 :         CPLError(
    3801             :             CE_Failure, CPLE_AppDefined,
    3802             :             "Setting a spatial filter on a resulting layer is not supported");
    3803           0 :         return OGRERR_FAILURE;
    3804             :     }
    3805             : 
    3806           5 :     OGREnvelope sEnvelope;
    3807           5 :     poGeomIn->getEnvelope(&sEnvelope);
    3808           5 :     ClampEnvelope(sEnvelope);
    3809             : 
    3810           5 :     if (sEnvelope.MinX == -180 && sEnvelope.MinY == -90 &&
    3811           1 :         sEnvelope.MaxX == 180 && sEnvelope.MaxY == 90)
    3812             :     {
    3813           1 :         return OGRERR_NONE;
    3814             :     }
    3815             : 
    3816           4 :     m_poSpatialFilter = json_object_new_object();
    3817             : 
    3818           4 :     if (m_abIsGeoPoint[iGeomField])
    3819             :     {
    3820           1 :         json_object *geo_bounding_box = json_object_new_object();
    3821           1 :         json_object_object_add(m_poSpatialFilter, "geo_bounding_box",
    3822             :                                geo_bounding_box);
    3823             : 
    3824           2 :         CPLString osPath = BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]);
    3825             : 
    3826           1 :         json_object *field = json_object_new_object();
    3827           1 :         json_object_object_add(geo_bounding_box, osPath.c_str(), field);
    3828             : 
    3829           1 :         json_object *top_left = json_object_new_object();
    3830           1 :         json_object_object_add(field, "top_left", top_left);
    3831           1 :         json_object_object_add(
    3832             :             top_left, "lat",
    3833             :             json_object_new_double_with_precision(sEnvelope.MaxY, 6));
    3834           1 :         json_object_object_add(
    3835             :             top_left, "lon",
    3836             :             json_object_new_double_with_precision(sEnvelope.MinX, 6));
    3837             : 
    3838           1 :         json_object *bottom_right = json_object_new_object();
    3839           1 :         json_object_object_add(field, "bottom_right", bottom_right);
    3840           1 :         json_object_object_add(
    3841             :             bottom_right, "lat",
    3842             :             json_object_new_double_with_precision(sEnvelope.MinY, 6));
    3843           1 :         json_object_object_add(
    3844             :             bottom_right, "lon",
    3845             :             json_object_new_double_with_precision(sEnvelope.MaxX, 6));
    3846             :     }
    3847             :     else
    3848             :     {
    3849           3 :         json_object *geo_shape = json_object_new_object();
    3850           3 :         json_object_object_add(m_poSpatialFilter, "geo_shape", geo_shape);
    3851             : 
    3852           6 :         CPLString osPath = BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]);
    3853             : 
    3854           3 :         json_object *field = json_object_new_object();
    3855           3 :         json_object_object_add(geo_shape, osPath.c_str(), field);
    3856             : 
    3857           3 :         json_object *shape = json_object_new_object();
    3858           3 :         json_object_object_add(field, "shape", shape);
    3859             : 
    3860           3 :         json_object_object_add(shape, "type",
    3861             :                                json_object_new_string("envelope"));
    3862             : 
    3863           3 :         json_object *coordinates = json_object_new_array();
    3864           3 :         json_object_object_add(shape, "coordinates", coordinates);
    3865             : 
    3866           3 :         json_object *top_left = json_object_new_array();
    3867           3 :         json_object_array_add(
    3868             :             top_left, json_object_new_double_with_precision(sEnvelope.MinX, 6));
    3869           3 :         json_object_array_add(
    3870             :             top_left, json_object_new_double_with_precision(sEnvelope.MaxY, 6));
    3871           3 :         json_object_array_add(coordinates, top_left);
    3872             : 
    3873           3 :         json_object *bottom_right = json_object_new_array();
    3874           3 :         json_object_array_add(
    3875             :             bottom_right,
    3876             :             json_object_new_double_with_precision(sEnvelope.MaxX, 6));
    3877           3 :         json_object_array_add(
    3878             :             bottom_right,
    3879             :             json_object_new_double_with_precision(sEnvelope.MinY, 6));
    3880           3 :         json_object_array_add(coordinates, bottom_right);
    3881             :     }
    3882             : 
    3883           4 :     return OGRERR_NONE;
    3884             : }
    3885             : 
    3886             : /************************************************************************/
    3887             : /*                           IGetExtent()                                */
    3888             : /************************************************************************/
    3889             : 
    3890           5 : OGRErr OGRElasticLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
    3891             :                                    bool bForce)
    3892             : {
    3893           5 :     FinalizeFeatureDefn();
    3894             : 
    3895             :     // geo_shape aggregation is only available since ES 7.8, but only with XPack
    3896             :     // for now
    3897           6 :     if (!m_abIsGeoPoint[iGeomField] &&
    3898           1 :         !(m_poDS->m_nMajorVersion > 7 ||
    3899           1 :           (m_poDS->m_nMajorVersion == 7 && m_poDS->m_nMinorVersion >= 8)))
    3900             :     {
    3901           1 :         m_bUseSingleQueryParams = true;
    3902           1 :         const auto eRet = OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
    3903           1 :         m_bUseSingleQueryParams = false;
    3904           1 :         return eRet;
    3905             :     }
    3906             : 
    3907             :     CPLString osFilter = CPLSPrintf(
    3908             :         "{ \"size\": 0, \"aggs\" : { \"bbox\" : { \"geo_bounds\" : { \"field\" "
    3909             :         ": \"%s\" } } } }",
    3910           8 :         BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]).c_str());
    3911             :     CPLString osURL =
    3912           8 :         CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str());
    3913           4 :     if (m_poDS->m_nMajorVersion < 7)
    3914           3 :         osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3915           4 :     osURL += "/_search?pretty";
    3916           4 :     AddTimeoutTerminateAfterToURL(osURL);
    3917             : 
    3918           4 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    3919             :     json_object *poResponse =
    3920           4 :         m_poDS->RunRequest(osURL.c_str(), osFilter.c_str());
    3921           4 :     CPLPopErrorHandler();
    3922           4 :     if (poResponse == nullptr)
    3923             :     {
    3924           2 :         const char *pszLastErrorMsg = CPLGetLastErrorMsg();
    3925           2 :         if (!m_abIsGeoPoint[iGeomField] &&
    3926           0 :             strstr(pszLastErrorMsg, "Fielddata is not supported on field") !=
    3927             :                 nullptr)
    3928             :         {
    3929           0 :             CPLDebug("ES",
    3930             :                      "geo_bounds aggregation failed, likely because of lack "
    3931             :                      "of XPack. Using client-side method");
    3932           0 :             CPLErrorReset();
    3933             :         }
    3934             :         else
    3935             :         {
    3936           2 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", pszLastErrorMsg);
    3937             :         }
    3938             :     }
    3939             : 
    3940             :     json_object *poBounds =
    3941           4 :         json_ex_get_object_by_path(poResponse, "aggregations.bbox.bounds");
    3942           4 :     json_object *poTopLeft = json_ex_get_object_by_path(poBounds, "top_left");
    3943             :     json_object *poBottomRight =
    3944           4 :         json_ex_get_object_by_path(poBounds, "bottom_right");
    3945           4 :     json_object *poTopLeftLon = json_ex_get_object_by_path(poTopLeft, "lon");
    3946           4 :     json_object *poTopLeftLat = json_ex_get_object_by_path(poTopLeft, "lat");
    3947             :     json_object *poBottomRightLon =
    3948           4 :         json_ex_get_object_by_path(poBottomRight, "lon");
    3949             :     json_object *poBottomRightLat =
    3950           4 :         json_ex_get_object_by_path(poBottomRight, "lat");
    3951             : 
    3952             :     OGRErr eErr;
    3953           4 :     if (poTopLeftLon == nullptr || poTopLeftLat == nullptr ||
    3954           2 :         poBottomRightLon == nullptr || poBottomRightLat == nullptr)
    3955             :     {
    3956           2 :         m_bUseSingleQueryParams = true;
    3957           2 :         const auto eRet = OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
    3958           2 :         m_bUseSingleQueryParams = false;
    3959           2 :         return eRet;
    3960             :     }
    3961             :     else
    3962             :     {
    3963           2 :         double dfMinX = json_object_get_double(poTopLeftLon);
    3964           2 :         double dfMaxY = json_object_get_double(poTopLeftLat);
    3965           2 :         double dfMaxX = json_object_get_double(poBottomRightLon);
    3966           2 :         double dfMinY = json_object_get_double(poBottomRightLat);
    3967             : 
    3968           2 :         psExtent->MinX = dfMinX;
    3969           2 :         psExtent->MaxY = dfMaxY;
    3970           2 :         psExtent->MaxX = dfMaxX;
    3971           2 :         psExtent->MinY = dfMinY;
    3972             : 
    3973           2 :         eErr = OGRERR_NONE;
    3974             :     }
    3975           2 :     json_object_put(poResponse);
    3976             : 
    3977           2 :     return eErr;
    3978             : }
    3979             : 
    3980             : /************************************************************************/
    3981             : /*                             GetDataset()                             */
    3982             : /************************************************************************/
    3983             : 
    3984           1 : GDALDataset *OGRElasticLayer::GetDataset()
    3985             : {
    3986           1 :     return m_poDS;
    3987             : }

Generated by: LCOV version 1.14