LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/elastic - ogrelasticlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1992 2121 93.9 %
Date: 2024-11-21 22:18:42 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         607 : void OGRElasticLayer::FinalizeFeatureDefn(bool bReadFeatures)
     647             : {
     648         607 :     if (m_bFeatureDefnFinalized)
     649         570 :         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         415 : OGRFeatureDefn *OGRElasticLayer::GetLayerDefn()
    1046             : {
    1047             : 
    1048         415 :     FinalizeFeatureDefn();
    1049             : 
    1050         415 :     return m_poFeatureDefn;
    1051             : }
    1052             : 
    1053             : /************************************************************************/
    1054             : /*                            GetFIDColumn()                            */
    1055             : /************************************************************************/
    1056             : 
    1057          35 : const char *OGRElasticLayer::GetFIDColumn()
    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(
    1135          10 :             nIdx == 0 ? "_uid" : BuildPathFromArray(m_aaosFieldPaths[nIdx]));
    1136           5 :         if (CSLFindString(m_papszFieldsWithRawValue,
    1137          10 :                           m_aoSortColumns[i].osColumn) >= 0)
    1138             :         {
    1139           1 :             osFieldName += ".raw";
    1140             :         }
    1141           5 :         json_object *poSortCol = json_object_new_object();
    1142           5 :         json_object *poSortProp = json_object_new_object();
    1143           5 :         json_object_array_add(poRet, poSortCol);
    1144           5 :         json_object_object_add(
    1145             :             poSortProp, "order",
    1146           5 :             json_object_new_string(m_aoSortColumns[i].bAsc ? "asc" : "desc"));
    1147           5 :         json_object_object_add(poSortCol, osFieldName, poSortProp);
    1148             :     }
    1149           3 :     return poRet;
    1150             : }
    1151             : 
    1152             : /************************************************************************/
    1153             : /*                           BuildQuery()                               */
    1154             : /************************************************************************/
    1155             : 
    1156          28 : CPLString OGRElasticLayer::BuildQuery(bool bCountOnly)
    1157             : {
    1158          28 :     CPLString osRet = "{ ";
    1159          31 :     if (bCountOnly &&
    1160           3 :         (m_poDS->m_nMajorVersion < 5 || !m_osSingleQueryTimeout.empty()))
    1161             :     {
    1162           2 :         osRet += "\"size\": 0, ";
    1163             :     }
    1164          28 :     if (m_poSpatialFilter && m_poJSONFilter)
    1165             :     {
    1166           1 :         osRet += CPLSPrintf("\"query\": { \"constant_score\" : { \"filter\": "
    1167             :                             "{ \"bool\" : { \"must\" : [%s, %s] } } } }",
    1168             :                             json_object_to_json_string(m_poSpatialFilter),
    1169           1 :                             json_object_to_json_string(m_poJSONFilter));
    1170             :     }
    1171             :     else
    1172             :     {
    1173             :         osRet += CPLSPrintf(
    1174             :             "\"query\": { \"constant_score\" : { \"filter\": %s } }",
    1175          27 :             json_object_to_json_string(m_poSpatialFilter ? m_poSpatialFilter
    1176          27 :                                                          : m_poJSONFilter));
    1177             :     }
    1178          28 :     if (!bCountOnly && !m_aoSortColumns.empty())
    1179             :     {
    1180           1 :         json_object *poSort = BuildSort();
    1181             :         osRet +=
    1182           1 :             CPLSPrintf(", \"sort\" : %s", json_object_to_json_string(poSort));
    1183           1 :         json_object_put(poSort);
    1184             :     }
    1185          28 :     osRet += " }";
    1186          28 :     return osRet;
    1187             : }
    1188             : 
    1189             : /************************************************************************/
    1190             : /*                         GetNextRawFeature()                          */
    1191             : /************************************************************************/
    1192             : 
    1193          83 : OGRFeature *OGRElasticLayer::GetNextRawFeature()
    1194             : {
    1195          83 :     json_object *poResponse = nullptr;
    1196             : 
    1197          83 :     if (m_dfEndTimeStamp > 0 && GetTimestamp() >= m_dfEndTimeStamp)
    1198             :     {
    1199           1 :         CPLDebug("ES", "Terminating request due to timeout");
    1200           1 :         return nullptr;
    1201             :     }
    1202         164 :     const auto nTerminateAfter = m_bUseSingleQueryParams
    1203          82 :                                      ? m_nSingleQueryTerminateAfter
    1204             :                                      : m_nFeatureIterationTerminateAfter;
    1205          82 :     if (nTerminateAfter > 0 &&
    1206           5 :         m_nReadFeaturesSinceResetReading >= nTerminateAfter)
    1207             :     {
    1208           1 :         CPLDebug("ES", "Terminating request due to terminate_after reached");
    1209           1 :         return nullptr;
    1210             :     }
    1211             : 
    1212          81 :     if (m_bEOF)
    1213           1 :         return nullptr;
    1214             : 
    1215          80 :     if (m_iCurFeatureInPage < (int)m_apoCachedFeatures.size())
    1216             :     {
    1217          15 :         OGRFeature *poRet = m_apoCachedFeatures[m_iCurFeatureInPage];
    1218          15 :         m_apoCachedFeatures[m_iCurFeatureInPage] = nullptr;
    1219          15 :         m_iCurFeatureInPage++;
    1220          15 :         m_nReadFeaturesSinceResetReading++;
    1221          15 :         return poRet;
    1222             :     }
    1223             : 
    1224          74 :     for (int i = 0; i < (int)m_apoCachedFeatures.size(); i++)
    1225           9 :         delete m_apoCachedFeatures[i];
    1226          65 :     m_apoCachedFeatures.resize(0);
    1227          65 :     m_iCurFeatureInPage = 0;
    1228             : 
    1229         130 :     CPLString osRequest, osPostData;
    1230          65 :     if (m_nReadFeaturesSinceResetReading == 0)
    1231             :     {
    1232          60 :         if (!m_osESSearch.empty())
    1233             :         {
    1234             :             osRequest = CPLSPrintf("%s/_search?scroll=1m&size=%d",
    1235           3 :                                    m_poDS->GetURL(), m_poDS->m_nBatchSize);
    1236           3 :             osPostData = m_osESSearch;
    1237             :         }
    1238         110 :         else if ((m_poSpatialFilter && m_osJSONFilter.empty()) ||
    1239          53 :                  m_poJSONFilter)
    1240             :         {
    1241          25 :             osPostData = BuildQuery(false);
    1242          25 :             osRequest = BuildMappingURL(false);
    1243             :             osRequest +=
    1244          25 :                 CPLSPrintf("/_search?scroll=1m&size=%d", m_poDS->m_nBatchSize);
    1245             :         }
    1246          32 :         else if (!m_aoSortColumns.empty() && m_osJSONFilter.empty())
    1247             :         {
    1248           2 :             osRequest = BuildMappingURL(false);
    1249             :             osRequest +=
    1250           2 :                 CPLSPrintf("/_search?scroll=1m&size=%d", m_poDS->m_nBatchSize);
    1251           2 :             json_object *poSort = BuildSort();
    1252             :             osPostData = CPLSPrintf("{ \"sort\": %s }",
    1253           2 :                                     json_object_to_json_string(poSort));
    1254           2 :             json_object_put(poSort);
    1255             :         }
    1256             :         else
    1257             :         {
    1258          30 :             osRequest = BuildMappingURL(false);
    1259             :             osRequest +=
    1260          30 :                 CPLSPrintf("/_search?scroll=1m&size=%d", m_poDS->m_nBatchSize);
    1261          30 :             osPostData = m_osJSONFilter;
    1262             :         }
    1263             :     }
    1264             :     else
    1265             :     {
    1266           5 :         if (m_osScrollID.empty())
    1267             :         {
    1268           1 :             m_bEOF = true;
    1269           1 :             return nullptr;
    1270             :         }
    1271             :         osRequest = CPLSPrintf("%s/_search/scroll?scroll=1m&scroll_id=%s",
    1272           4 :                                m_poDS->GetURL(), m_osScrollID.c_str());
    1273             :     }
    1274             : 
    1275          64 :     if (m_bAddPretty)
    1276           0 :         osRequest += "&pretty";
    1277          64 :     poResponse = m_poDS->RunRequest(osRequest, osPostData);
    1278          64 :     if (poResponse == nullptr)
    1279             :     {
    1280           4 :         m_bEOF = true;
    1281           4 :         return nullptr;
    1282             :     }
    1283          60 :     m_osScrollID.clear();
    1284             :     json_object *poScrollID =
    1285          60 :         CPL_json_object_object_get(poResponse, "_scroll_id");
    1286          60 :     if (poScrollID)
    1287             :     {
    1288          40 :         const char *pszScrollID = json_object_get_string(poScrollID);
    1289          40 :         if (pszScrollID)
    1290          40 :             m_osScrollID = pszScrollID;
    1291             :     }
    1292             : 
    1293          60 :     json_object *poHits = CPL_json_object_object_get(poResponse, "hits");
    1294          60 :     if (poHits == nullptr || json_object_get_type(poHits) != json_type_object)
    1295             :     {
    1296           2 :         m_bEOF = true;
    1297           2 :         json_object_put(poResponse);
    1298           2 :         return nullptr;
    1299             :     }
    1300          58 :     poHits = CPL_json_object_object_get(poHits, "hits");
    1301          58 :     if (poHits == nullptr || json_object_get_type(poHits) != json_type_array)
    1302             :     {
    1303           1 :         m_bEOF = true;
    1304           1 :         json_object_put(poResponse);
    1305           1 :         return nullptr;
    1306             :     }
    1307          57 :     const auto nHits = json_object_array_length(poHits);
    1308          57 :     if (nHits == 0)
    1309             :     {
    1310           3 :         m_osScrollID = "";
    1311           3 :         m_bEOF = true;
    1312           3 :         json_object_put(poResponse);
    1313           3 :         return nullptr;
    1314             :     }
    1315         140 :     for (auto i = decltype(nHits){0}; i < nHits; i++)
    1316             :     {
    1317          86 :         json_object *poHit = json_object_array_get_idx(poHits, i);
    1318          86 :         if (poHit == nullptr || json_object_get_type(poHit) != json_type_object)
    1319             :         {
    1320           3 :             continue;
    1321             :         }
    1322          85 :         json_object *poSource = CPL_json_object_object_get(poHit, "_source");
    1323         168 :         if (poSource == nullptr ||
    1324          83 :             json_object_get_type(poSource) != json_type_object)
    1325             :         {
    1326           2 :             continue;
    1327             :         }
    1328             : 
    1329          83 :         const char *pszId = nullptr;
    1330          83 :         json_object *poId = CPL_json_object_object_get(poHit, "_id");
    1331          83 :         if (poId != nullptr && json_object_get_type(poId) == json_type_string)
    1332          43 :             pszId = json_object_get_string(poId);
    1333             : 
    1334          83 :         OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
    1335          83 :         if (pszId)
    1336          43 :             poFeature->SetField("_id", pszId);
    1337             : 
    1338          83 :         if (m_bAddSourceIndexName)
    1339             :         {
    1340           2 :             json_object *poIndex = CPL_json_object_object_get(poHit, "_index");
    1341           4 :             if (poId != nullptr &&
    1342           2 :                 json_object_get_type(poId) == json_type_string)
    1343           2 :                 poFeature->SetField("_index", json_object_get_string(poIndex));
    1344             :         }
    1345             : 
    1346          83 :         if (!m_osESSearch.empty())
    1347             :         {
    1348           3 :             json_object *poIndex = CPL_json_object_object_get(poHit, "_index");
    1349           6 :             if (poIndex != nullptr &&
    1350           3 :                 json_object_get_type(poIndex) == json_type_string)
    1351           3 :                 poFeature->SetField("_index", json_object_get_string(poIndex));
    1352             : 
    1353           3 :             json_object *poType = CPL_json_object_object_get(poHit, "_type");
    1354           5 :             if (poType != nullptr &&
    1355           2 :                 json_object_get_type(poType) == json_type_string)
    1356           2 :                 poFeature->SetField("_type", json_object_get_string(poType));
    1357             :         }
    1358             : 
    1359          83 :         if (m_poDS->m_bJSonField)
    1360           4 :             poFeature->SetField("_json", json_object_to_json_string(poSource));
    1361             : 
    1362          83 :         BuildFeature(poFeature, poSource, CPLString());
    1363          83 :         if (poFeature->GetFID() < 0)
    1364          76 :             poFeature->SetFID(++m_iCurID);
    1365          83 :         m_apoCachedFeatures.push_back(poFeature);
    1366             :     }
    1367             : 
    1368          54 :     json_object_put(poResponse);
    1369          54 :     if (!m_apoCachedFeatures.empty())
    1370             :     {
    1371          53 :         OGRFeature *poRet = m_apoCachedFeatures[0];
    1372          53 :         m_apoCachedFeatures[0] = nullptr;
    1373          53 :         m_iCurFeatureInPage++;
    1374          53 :         m_nReadFeaturesSinceResetReading++;
    1375          53 :         return poRet;
    1376             :     }
    1377           1 :     return nullptr;
    1378             : }
    1379             : 
    1380             : /************************************************************************/
    1381             : /*                      decode_geohash_bbox()                           */
    1382             : /************************************************************************/
    1383             : 
    1384             : /* Derived from routine from
    1385             :  * https://github.com/davetroy/geohash/blob/master/ext/geohash_native.c */
    1386             : /* (c) 2008-2010 David Troy, davetroy@gmail.com, (The MIT License) */
    1387             : 
    1388             : static const char BASE32[] = "0123456789bcdefghjkmnpqrstuvwxyz";
    1389             : 
    1390           4 : static void decode_geohash_bbox(const char *geohash, double lat[2],
    1391             :                                 double lon[2])
    1392             : {
    1393             :     int i;
    1394             :     int j;
    1395             :     int hashlen;
    1396             :     char c;
    1397             :     char cd;
    1398             :     char mask;
    1399           4 :     char is_even = 1;
    1400             :     static const char bits[] = {16, 8, 4, 2, 1};
    1401           4 :     lat[0] = -90.0;
    1402           4 :     lat[1] = 90.0;
    1403           4 :     lon[0] = -180.0;
    1404           4 :     lon[1] = 180.0;
    1405           4 :     hashlen = static_cast<int>(strlen(geohash));
    1406          84 :     for (i = 0; i < hashlen; i++)
    1407             :     {
    1408          80 :         c = static_cast<char>(
    1409          80 :             CPLTolower(static_cast<unsigned char>(geohash[i])));
    1410          80 :         cd = static_cast<char>(strchr(BASE32, c) - BASE32);
    1411         480 :         for (j = 0; j < 5; j++)
    1412             :         {
    1413         400 :             mask = bits[j];
    1414         400 :             if (is_even)
    1415             :             {
    1416         200 :                 lon[!(cd & mask)] = (lon[0] + lon[1]) / 2;
    1417             :             }
    1418             :             else
    1419             :             {
    1420         200 :                 lat[!(cd & mask)] = (lat[0] + lat[1]) / 2;
    1421             :             }
    1422         400 :             is_even = !is_even;
    1423             :         }
    1424             :     }
    1425           4 : }
    1426             : 
    1427             : /************************************************************************/
    1428             : /*                            BuildFeature()                            */
    1429             : /************************************************************************/
    1430             : 
    1431         155 : void OGRElasticLayer::BuildFeature(OGRFeature *poFeature, json_object *poSource,
    1432             :                                    CPLString osPath)
    1433             : {
    1434             :     json_object_iter it;
    1435         155 :     it.key = nullptr;
    1436         155 :     it.val = nullptr;
    1437         155 :     it.entry = nullptr;
    1438         310 :     CPLString osCurPath;
    1439         538 :     json_object_object_foreachC(poSource, it)
    1440             :     {
    1441         383 :         if (osPath.empty() && !m_osFID.empty() && EQUAL(m_osFID, it.key))
    1442             :         {
    1443           7 :             json_type eJSONType = json_object_get_type(it.val);
    1444           7 :             if (eJSONType == json_type_int)
    1445             :             {
    1446           7 :                 poFeature->SetFID((GIntBig)json_object_get_int64(it.val));
    1447           7 :                 continue;
    1448             :             }
    1449             :         }
    1450             : 
    1451         376 :         if (!osPath.empty())
    1452         190 :             osCurPath = osPath + "." + it.key;
    1453             :         else
    1454         186 :             osCurPath = it.key;
    1455             :         std::map<CPLString, int>::iterator oIter =
    1456         376 :             m_aosMapToFieldIndex.find(osCurPath);
    1457         376 :         if (oIter != m_aosMapToFieldIndex.end())
    1458             :         {
    1459         163 :             switch (json_object_get_type(it.val))
    1460             :             {
    1461           1 :                 case json_type_null:
    1462           1 :                     poFeature->SetFieldNull(oIter->second);
    1463           1 :                     break;
    1464           7 :                 case json_type_boolean:
    1465           7 :                     poFeature->SetField(oIter->second,
    1466           7 :                                         json_object_get_boolean(it.val));
    1467           7 :                     break;
    1468          35 :                 case json_type_int:
    1469          35 :                     poFeature->SetField(oIter->second,
    1470          35 :                                         (GIntBig)json_object_get_int64(it.val));
    1471          35 :                     break;
    1472          14 :                 case json_type_double:
    1473          14 :                     poFeature->SetField(oIter->second,
    1474          14 :                                         json_object_get_double(it.val));
    1475          14 :                     break;
    1476          28 :                 case json_type_array:
    1477             :                 {
    1478          28 :                     if (m_poFeatureDefn->GetFieldDefn(oIter->second)
    1479          28 :                             ->GetType() == OFTIntegerList)
    1480             :                     {
    1481          14 :                         std::vector<int> anValues;
    1482           7 :                         const auto nLength = json_object_array_length(it.val);
    1483          14 :                         for (auto i = decltype(nLength){0}; i < nLength; i++)
    1484             :                         {
    1485           7 :                             anValues.push_back(json_object_get_int(
    1486           7 :                                 json_object_array_get_idx(it.val, i)));
    1487             :                         }
    1488           7 :                         if (nLength)
    1489           7 :                             poFeature->SetField(oIter->second,
    1490             :                                                 static_cast<int>(nLength),
    1491           7 :                                                 &anValues[0]);
    1492             :                     }
    1493          21 :                     else if (m_poFeatureDefn->GetFieldDefn(oIter->second)
    1494          21 :                                  ->GetType() == OFTInteger64List)
    1495             :                     {
    1496          14 :                         std::vector<GIntBig> anValues;
    1497           7 :                         const auto nLength = json_object_array_length(it.val);
    1498          14 :                         for (auto i = decltype(nLength){0}; i < nLength; i++)
    1499             :                         {
    1500           7 :                             anValues.push_back(json_object_get_int64(
    1501           7 :                                 json_object_array_get_idx(it.val, i)));
    1502             :                         }
    1503           7 :                         if (nLength)
    1504           7 :                             poFeature->SetField(oIter->second,
    1505             :                                                 static_cast<int>(nLength),
    1506           7 :                                                 &anValues[0]);
    1507             :                     }
    1508          14 :                     else if (m_poFeatureDefn->GetFieldDefn(oIter->second)
    1509          14 :                                  ->GetType() == OFTRealList)
    1510             :                     {
    1511          14 :                         std::vector<double> adfValues;
    1512           7 :                         const auto nLength = json_object_array_length(it.val);
    1513          14 :                         for (auto i = decltype(nLength){0}; i < nLength; i++)
    1514             :                         {
    1515           7 :                             adfValues.push_back(json_object_get_double(
    1516           7 :                                 json_object_array_get_idx(it.val, i)));
    1517             :                         }
    1518           7 :                         if (nLength)
    1519           7 :                             poFeature->SetField(oIter->second,
    1520             :                                                 static_cast<int>(nLength),
    1521           7 :                                                 &adfValues[0]);
    1522             :                     }
    1523           7 :                     else if (m_poFeatureDefn->GetFieldDefn(oIter->second)
    1524           7 :                                  ->GetType() == OFTStringList)
    1525             :                     {
    1526          14 :                         std::vector<char *> apszValues;
    1527           7 :                         const auto nLength = json_object_array_length(it.val);
    1528          14 :                         for (auto i = decltype(nLength){0}; i < nLength; i++)
    1529             :                         {
    1530           7 :                             apszValues.push_back(
    1531           7 :                                 CPLStrdup(json_object_get_string(
    1532           7 :                                     json_object_array_get_idx(it.val, i))));
    1533             :                         }
    1534           7 :                         apszValues.push_back(nullptr);
    1535           7 :                         poFeature->SetField(oIter->second, &apszValues[0]);
    1536          14 :                         for (auto i = decltype(nLength){0}; i < nLength; i++)
    1537             :                         {
    1538           7 :                             CPLFree(apszValues[i]);
    1539             :                         }
    1540             :                     }
    1541          28 :                     break;
    1542             :                 }
    1543          78 :                 default:
    1544             :                 {
    1545          78 :                     if (m_poFeatureDefn->GetFieldDefn(oIter->second)
    1546          78 :                             ->GetType() == OFTBinary)
    1547             :                     {
    1548             :                         GByte *pabyBase64 =
    1549           7 :                             (GByte *)CPLStrdup(json_object_get_string(it.val));
    1550           7 :                         int nBytes = CPLBase64DecodeInPlace(pabyBase64);
    1551           7 :                         poFeature->SetField(oIter->second, nBytes, pabyBase64);
    1552           7 :                         CPLFree(pabyBase64);
    1553             :                     }
    1554             :                     else
    1555             :                     {
    1556          71 :                         poFeature->SetField(oIter->second,
    1557             :                                             json_object_get_string(it.val));
    1558             :                     }
    1559          78 :                     break;
    1560             :                 }
    1561             :             }
    1562             :         }
    1563         213 :         else if ((oIter = m_aosMapToGeomFieldIndex.find(osCurPath)) !=
    1564         426 :                  m_aosMapToGeomFieldIndex.end())
    1565             :         {
    1566          64 :             const auto poSRS = m_poFeatureDefn->GetGeomFieldDefn(oIter->second)
    1567          64 :                                    ->GetSpatialRef();
    1568          64 :             OGRGeometry *poGeom = nullptr;
    1569          64 :             if (m_abIsGeoPoint[oIter->second])
    1570             :             {
    1571          39 :                 json_type eJSONType = json_object_get_type(it.val);
    1572          62 :                 if (eJSONType == json_type_array &&
    1573          23 :                     json_object_array_length(it.val) == 2)
    1574             :                 {
    1575          23 :                     json_object *poX = json_object_array_get_idx(it.val, 0);
    1576          23 :                     json_object *poY = json_object_array_get_idx(it.val, 1);
    1577          23 :                     if (poX != nullptr && poY != nullptr)
    1578             :                     {
    1579          46 :                         poGeom = new OGRPoint(json_object_get_double(poX),
    1580          23 :                                               json_object_get_double(poY));
    1581             :                     }
    1582             :                 }
    1583          16 :                 else if (eJSONType == json_type_object)
    1584             :                 {
    1585             :                     json_object *poX =
    1586           4 :                         CPL_json_object_object_get(it.val, "lon");
    1587             :                     json_object *poY =
    1588           4 :                         CPL_json_object_object_get(it.val, "lat");
    1589           4 :                     if (poX != nullptr && poY != nullptr)
    1590             :                     {
    1591           8 :                         poGeom = new OGRPoint(json_object_get_double(poX),
    1592           4 :                                               json_object_get_double(poY));
    1593             :                     }
    1594             :                 }
    1595          12 :                 else if (eJSONType == json_type_string)
    1596             :                 {
    1597          12 :                     const char *pszLatLon = json_object_get_string(it.val);
    1598          12 :                     char **papszTokens = CSLTokenizeString2(pszLatLon, ",", 0);
    1599          12 :                     if (CSLCount(papszTokens) == 2)
    1600             :                     {
    1601          16 :                         poGeom = new OGRPoint(CPLAtof(papszTokens[1]),
    1602           8 :                                               CPLAtof(papszTokens[0]));
    1603             :                     }
    1604             :                     else
    1605             :                     {
    1606           4 :                         double lat[2] = {0.0, 0.0};
    1607           4 :                         double lon[2] = {0.0, 0.0};
    1608           4 :                         decode_geohash_bbox(pszLatLon, lat, lon);
    1609           4 :                         poGeom = new OGRPoint((lon[0] + lon[1]) / 2,
    1610           4 :                                               (lat[0] + lat[1]) / 2);
    1611             :                     }
    1612             : 
    1613          12 :                     CSLDestroy(papszTokens);
    1614             :                 }
    1615             :             }
    1616          25 :             else if (json_object_get_type(it.val) == json_type_object)
    1617             :             {
    1618             :                 json_object *poType =
    1619          24 :                     CPL_json_object_object_get(it.val, "type");
    1620             :                 json_object *poRadius =
    1621          24 :                     CPL_json_object_object_get(it.val, "radius");
    1622             :                 json_object *poCoordinates =
    1623          24 :                     CPL_json_object_object_get(it.val, "coordinates");
    1624          24 :                 if (poType && poRadius && poCoordinates &&
    1625           3 :                     json_object_get_type(poType) == json_type_string &&
    1626           3 :                     EQUAL(json_object_get_string(poType), "circle") &&
    1627           3 :                     (json_object_get_type(poRadius) == json_type_string ||
    1628           1 :                      json_object_get_type(poRadius) == json_type_double ||
    1629           1 :                      json_object_get_type(poRadius) == json_type_int) &&
    1630          51 :                     json_object_get_type(poCoordinates) == json_type_array &&
    1631           3 :                     json_object_array_length(poCoordinates) == 2)
    1632             :                 {
    1633           3 :                     const char *pszRadius = json_object_get_string(poRadius);
    1634           6 :                     const double dfX = json_object_get_double(
    1635           3 :                         json_object_array_get_idx(poCoordinates, 0));
    1636           6 :                     const double dfY = json_object_get_double(
    1637           3 :                         json_object_array_get_idx(poCoordinates, 1));
    1638           3 :                     const int nRadiusLength = (int)strlen(pszRadius);
    1639           3 :                     double dfRadius = CPLAtof(pszRadius);
    1640           3 :                     double dfUnit = 0.0;
    1641           3 :                     if (nRadiusLength >= 1 &&
    1642           3 :                         pszRadius[nRadiusLength - 1] == 'm')
    1643             :                     {
    1644           2 :                         if (nRadiusLength >= 2 &&
    1645           2 :                             pszRadius[nRadiusLength - 2] == 'k')
    1646           1 :                             dfUnit = 1000;
    1647           1 :                         else if (nRadiusLength >= 2 &&
    1648           1 :                                  pszRadius[nRadiusLength - 2] >= '0' &&
    1649           1 :                                  pszRadius[nRadiusLength - 2] <= '9')
    1650           1 :                             dfUnit = 1;
    1651             :                     }
    1652           1 :                     else if (nRadiusLength >= 1 &&
    1653           1 :                              pszRadius[nRadiusLength - 1] >= '0' &&
    1654           1 :                              pszRadius[nRadiusLength - 1] <= '9')
    1655             :                     {
    1656           1 :                         dfUnit = 1;
    1657             :                     }
    1658             : 
    1659           3 :                     if (dfRadius == 0)
    1660           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1661             :                                  "Unknown unit in %s", pszRadius);
    1662             :                     else
    1663             :                     {
    1664           3 :                         dfRadius *= dfUnit;
    1665           3 :                         OGRLinearRing *poRing = new OGRLinearRing();
    1666           3 :                         double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS;
    1667           3 :                         if (poSRS && poSRS->IsGeographic())
    1668           3 :                             dfSemiMajor = poSRS->GetSemiMajor();
    1669         276 :                         for (double dfStep = 0; dfStep <= 360; dfStep += 4)
    1670             :                         {
    1671         273 :                             double dfLat = 0.0;
    1672         273 :                             double dfLon = 0.0;
    1673         273 :                             OGR_GreatCircle_ExtendPosition(dfY, dfX, dfRadius,
    1674             :                                                            dfSemiMajor, dfStep,
    1675             :                                                            &dfLat, &dfLon);
    1676         273 :                             poRing->addPoint(dfLon, dfLat);
    1677             :                         }
    1678           3 :                         OGRPolygon *poPoly = new OGRPolygon();
    1679           3 :                         poPoly->addRingDirectly(poRing);
    1680           3 :                         poGeom = poPoly;
    1681             :                     }
    1682             :                 }
    1683          21 :                 else if (poType && poCoordinates &&
    1684          21 :                          json_object_get_type(poType) == json_type_string &&
    1685          21 :                          EQUAL(json_object_get_string(poType), "envelope") &&
    1686           1 :                          json_object_get_type(poCoordinates) ==
    1687          42 :                              json_type_array &&
    1688           1 :                          json_object_array_length(poCoordinates) == 2)
    1689             :                 {
    1690             :                     json_object *poCorner1 =
    1691           1 :                         json_object_array_get_idx(poCoordinates, 0);
    1692             :                     json_object *poCorner2 =
    1693           1 :                         json_object_array_get_idx(poCoordinates, 1);
    1694           1 :                     if (poCorner1 && poCorner2 &&
    1695           1 :                         json_object_get_type(poCorner1) == json_type_array &&
    1696           1 :                         json_object_array_length(poCorner1) == 2 &&
    1697           3 :                         json_object_get_type(poCorner2) == json_type_array &&
    1698           1 :                         json_object_array_length(poCorner2) == 2)
    1699             :                     {
    1700           2 :                         const double dfX1 = json_object_get_double(
    1701           1 :                             json_object_array_get_idx(poCorner1, 0));
    1702           2 :                         const double dfY1 = json_object_get_double(
    1703           1 :                             json_object_array_get_idx(poCorner1, 1));
    1704           2 :                         const double dfX2 = json_object_get_double(
    1705           1 :                             json_object_array_get_idx(poCorner2, 0));
    1706           2 :                         const double dfY2 = json_object_get_double(
    1707           1 :                             json_object_array_get_idx(poCorner2, 1));
    1708           1 :                         OGRLinearRing *poRing = new OGRLinearRing();
    1709           1 :                         poRing->addPoint(dfX1, dfY1);
    1710           1 :                         poRing->addPoint(dfX2, dfY1);
    1711           1 :                         poRing->addPoint(dfX2, dfY2);
    1712           1 :                         poRing->addPoint(dfX1, dfY2);
    1713           1 :                         poRing->addPoint(dfX1, dfY1);
    1714           1 :                         OGRPolygon *poPoly = new OGRPolygon();
    1715           1 :                         poPoly->addRingDirectly(poRing);
    1716           1 :                         poGeom = poPoly;
    1717             :                     }
    1718             :                 }
    1719             :                 else
    1720             :                 {
    1721          20 :                     poGeom = OGRGeoJSONReadGeometry(it.val);
    1722             :                 }
    1723             :             }
    1724           1 :             else if (json_object_get_type(it.val) == json_type_string)
    1725             :             {
    1726             :                 // Assume this is WKT
    1727           1 :                 OGRGeometryFactory::createFromWkt(
    1728             :                     json_object_get_string(it.val), nullptr, &poGeom);
    1729             :             }
    1730             : 
    1731          64 :             if (poGeom != nullptr)
    1732             :             {
    1733          64 :                 poGeom->assignSpatialReference(poSRS);
    1734          64 :                 poFeature->SetGeomFieldDirectly(oIter->second, poGeom);
    1735             :             }
    1736             :         }
    1737         221 :         else if (json_object_get_type(it.val) == json_type_object &&
    1738          73 :                  (m_poDS->m_bFlattenNestedAttributes ||
    1739           2 :                   (osPath.empty() && m_osMappingName == "FeatureCollection" &&
    1740           0 :                    strcmp(it.key, "properties") == 0)))
    1741             :         {
    1742          71 :             BuildFeature(poFeature, it.val, osCurPath);
    1743             :         }
    1744          79 :         else if (json_object_get_type(it.val) == json_type_object &&
    1745           1 :                  !m_poDS->m_bFlattenNestedAttributes)
    1746             :         {
    1747           1 :             if (m_aosMapToGeomFieldIndex.find(osCurPath + ".coordinates") !=
    1748           2 :                 m_aosMapToGeomFieldIndex.end())
    1749             :             {
    1750           1 :                 BuildFeature(poFeature, it.val, osCurPath);
    1751             :             }
    1752             :         }
    1753             :     }
    1754         155 : }
    1755             : 
    1756             : /************************************************************************/
    1757             : /*                            AppendGroup()                             */
    1758             : /************************************************************************/
    1759             : 
    1760           4 : static json_object *AppendGroup(json_object *parent, const CPLString &name)
    1761             : {
    1762           4 :     json_object *obj = json_object_new_object();
    1763           4 :     json_object *properties = json_object_new_object();
    1764           4 :     json_object_object_add(parent, name, obj);
    1765           4 :     json_object_object_add(obj, "properties", properties);
    1766           4 :     return properties;
    1767             : }
    1768             : 
    1769             : /************************************************************************/
    1770             : /*                           AddPropertyMap()                           */
    1771             : /************************************************************************/
    1772             : 
    1773          18 : static json_object *AddPropertyMap(const CPLString &type)
    1774             : {
    1775          18 :     json_object *obj = json_object_new_object();
    1776          18 :     json_object_object_add(obj, "type", json_object_new_string(type.c_str()));
    1777          18 :     return obj;
    1778             : }
    1779             : 
    1780             : /************************************************************************/
    1781             : /*                      GetContainerForMapping()                        */
    1782             : /************************************************************************/
    1783             : 
    1784             : static json_object *
    1785          48 : GetContainerForMapping(json_object *poContainer,
    1786             :                        const std::vector<CPLString> &aosPath,
    1787             :                        std::map<std::vector<CPLString>, json_object *> &oMap)
    1788             : {
    1789          48 :     std::vector<CPLString> aosSubPath;
    1790          80 :     for (int j = 0; j < (int)aosPath.size() - 1; j++)
    1791             :     {
    1792          32 :         aosSubPath.push_back(aosPath[j]);
    1793             :         std::map<std::vector<CPLString>, json_object *>::iterator oIter =
    1794          32 :             oMap.find(aosSubPath);
    1795          32 :         if (oIter == oMap.end())
    1796             :         {
    1797          11 :             json_object *poNewContainer = json_object_new_object();
    1798          11 :             json_object *poProperties = json_object_new_object();
    1799          11 :             json_object_object_add(poContainer, aosPath[j], poNewContainer);
    1800          11 :             json_object_object_add(poNewContainer, "properties", poProperties);
    1801          11 :             oMap[aosSubPath] = poProperties;
    1802          11 :             poContainer = poProperties;
    1803             :         }
    1804             :         else
    1805             :         {
    1806          21 :             poContainer = oIter->second;
    1807             :         }
    1808             :     }
    1809          96 :     return poContainer;
    1810             : }
    1811             : 
    1812             : /************************************************************************/
    1813             : /*                             BuildMap()                               */
    1814             : /************************************************************************/
    1815             : 
    1816          13 : CPLString OGRElasticLayer::BuildMap()
    1817             : {
    1818          13 :     json_object *map = json_object_new_object();
    1819             : 
    1820          26 :     std::map<std::vector<CPLString>, json_object *> oMap;
    1821             : 
    1822             :     json_object *poMapping;
    1823          13 :     json_object *poMappingProperties = json_object_new_object();
    1824          13 :     if (m_poDS->m_nMajorVersion < 7)
    1825             :     {
    1826          11 :         poMapping = json_object_new_object();
    1827          11 :         json_object_object_add(map, m_osMappingName, poMapping);
    1828             :     }
    1829             :     else
    1830             :     {
    1831           2 :         poMapping = map;
    1832             :     }
    1833          13 :     json_object_object_add(poMapping, "properties", poMappingProperties);
    1834             : 
    1835          13 :     if (m_poDS->m_nMajorVersion < 7 && m_osMappingName == "FeatureCollection")
    1836             :     {
    1837           9 :         json_object_object_add(
    1838             :             poMappingProperties, "type",
    1839           9 :             AddPropertyMap(m_poDS->m_nMajorVersion >= 5 ? "text" : "string"));
    1840             : 
    1841          18 :         std::vector<CPLString> aosPath;
    1842           9 :         aosPath.push_back("properties");
    1843           9 :         aosPath.push_back("dummy");
    1844           9 :         GetContainerForMapping(poMappingProperties, aosPath, oMap);
    1845             :     }
    1846             : 
    1847             :     /* skip _id field */
    1848          34 :     for (int i = 1; i < m_poFeatureDefn->GetFieldCount(); i++)
    1849             :     {
    1850          21 :         OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    1851             : 
    1852          21 :         json_object *poContainer = GetContainerForMapping(
    1853          21 :             poMappingProperties, m_aaosFieldPaths[i], oMap);
    1854          21 :         const char *pszLastComponent = m_aaosFieldPaths[i].back();
    1855             : 
    1856          21 :         const char *pszType = "string";
    1857          21 :         const char *pszFormat = nullptr;
    1858             : 
    1859          21 :         switch (poFieldDefn->GetType())
    1860             :         {
    1861           4 :             case OFTInteger:
    1862             :             case OFTIntegerList:
    1863             :             {
    1864           4 :                 if (poFieldDefn->GetSubType() == OFSTBoolean)
    1865           1 :                     pszType = "boolean";
    1866             :                 else
    1867           3 :                     pszType = "integer";
    1868           4 :                 break;
    1869             :             }
    1870           2 :             case OFTInteger64:
    1871             :             case OFTInteger64List:
    1872           2 :                 pszType = "long";
    1873           2 :                 break;
    1874           3 :             case OFTReal:
    1875             :             case OFTRealList:
    1876           3 :                 pszType = "double";
    1877           3 :                 break;
    1878           2 :             case OFTDateTime:
    1879             :             case OFTDate:
    1880           2 :                 pszType = "date";
    1881           2 :                 pszFormat = "yyyy/MM/dd HH:mm:ss.SSSZZ||yyyy/MM/dd "
    1882             :                             "HH:mm:ss.SSS||yyyy/MM/dd";
    1883           2 :                 break;
    1884           1 :             case OFTTime:
    1885           1 :                 pszType = "date";
    1886           1 :                 pszFormat = "HH:mm:ss.SSS";
    1887           1 :                 break;
    1888           1 :             case OFTBinary:
    1889           1 :                 pszType = "binary";
    1890           1 :                 break;
    1891           8 :             default:
    1892           8 :                 break;
    1893             :         }
    1894             : 
    1895          21 :         bool bAnalyzed = EQUAL(pszType, "string");
    1896          21 :         json_object *poPropertyMap = json_object_new_object();
    1897          21 :         if (m_poDS->m_nMajorVersion >= 5 && EQUAL(pszType, "string"))
    1898             :         {
    1899           0 :             if (CSLFindString(m_papszNotAnalyzedFields,
    1900           0 :                               poFieldDefn->GetNameRef()) >= 0 ||
    1901           0 :                 (CSLCount(m_papszNotAnalyzedFields) == 1 &&
    1902           0 :                  EQUAL(m_papszNotAnalyzedFields[0], "{ALL}")))
    1903             :             {
    1904           0 :                 bAnalyzed = false;
    1905           0 :                 pszType = "keyword";
    1906             :             }
    1907             :             else
    1908           0 :                 pszType = "text";
    1909             :         }
    1910          21 :         json_object_object_add(poPropertyMap, "type",
    1911             :                                json_object_new_string(pszType));
    1912          21 :         if (pszFormat)
    1913           3 :             json_object_object_add(poPropertyMap, "format",
    1914             :                                    json_object_new_string(pszFormat));
    1915          41 :         if (m_bStoreFields ||
    1916          20 :             CSLFindString(m_papszStoredFields, poFieldDefn->GetNameRef()) >= 0)
    1917           2 :             json_object_object_add(poPropertyMap, "store",
    1918             :                                    json_object_new_string("yes"));
    1919          42 :         if (m_poDS->m_nMajorVersion < 5 &&
    1920          21 :             (CSLFindString(m_papszNotAnalyzedFields,
    1921          20 :                            poFieldDefn->GetNameRef()) >= 0 ||
    1922          20 :              (CSLCount(m_papszNotAnalyzedFields) == 1 &&
    1923          13 :               EQUAL(m_papszNotAnalyzedFields[0], "{ALL}"))))
    1924             :         {
    1925           1 :             bAnalyzed = false;
    1926           1 :             json_object_object_add(poPropertyMap, "index",
    1927             :                                    json_object_new_string("not_analyzed"));
    1928             :         }
    1929          20 :         else if (CSLFindString(m_papszNotIndexedFields,
    1930          20 :                                poFieldDefn->GetNameRef()) >= 0)
    1931           1 :             json_object_object_add(poPropertyMap, "index",
    1932             :                                    json_object_new_string("no"));
    1933             : 
    1934          28 :         if (bAnalyzed && (CSLFindString(m_papszFieldsWithRawValue,
    1935           7 :                                         poFieldDefn->GetNameRef()) >= 0 ||
    1936           7 :                           (CSLCount(m_papszFieldsWithRawValue) == 1 &&
    1937           0 :                            EQUAL(m_papszFieldsWithRawValue[0], "{ALL}"))))
    1938             :         {
    1939           0 :             json_object *poFields = json_object_new_object();
    1940           0 :             json_object *poRaw = json_object_new_object();
    1941           0 :             json_object_object_add(poFields, "raw", poRaw);
    1942           0 :             if (m_poDS->m_nMajorVersion >= 5)
    1943             :             {
    1944           0 :                 json_object_object_add(poRaw, "type",
    1945             :                                        json_object_new_string("keyword"));
    1946             :             }
    1947             :             else
    1948             :             {
    1949           0 :                 json_object_object_add(poRaw, "type",
    1950             :                                        json_object_new_string("string"));
    1951           0 :                 json_object_object_add(poRaw, "index",
    1952             :                                        json_object_new_string("not_analyzed"));
    1953             :             }
    1954           0 :             json_object_object_add(poPropertyMap, "fields", poFields);
    1955             :         }
    1956             : 
    1957          21 :         json_object_object_add(poContainer, pszLastComponent, poPropertyMap);
    1958             :     }
    1959             : 
    1960          31 :     for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
    1961             :     {
    1962          36 :         std::vector<CPLString> aosPath = m_aaosGeomFieldPaths[i];
    1963          18 :         bool bAddGeoJSONType = false;
    1964          22 :         if (m_abIsGeoPoint[i] && aosPath.size() >= 2 &&
    1965           4 :             aosPath.back() == "coordinates")
    1966             :         {
    1967           4 :             bAddGeoJSONType = true;
    1968           4 :             aosPath.resize((int)aosPath.size() - 1);
    1969             :         }
    1970             : 
    1971             :         json_object *poContainer =
    1972          18 :             GetContainerForMapping(poMappingProperties, aosPath, oMap);
    1973          18 :         const char *pszLastComponent = aosPath.back();
    1974             : 
    1975          18 :         if (m_abIsGeoPoint[i])
    1976             :         {
    1977           5 :             json_object *geo_point = AddPropertyMap("geo_point");
    1978           5 :             if (bAddGeoJSONType)
    1979             :             {
    1980             :                 json_object *geometry =
    1981           4 :                     AppendGroup(poContainer, pszLastComponent);
    1982           4 :                 json_object_object_add(
    1983             :                     geometry, "type",
    1984           4 :                     AddPropertyMap(m_poDS->m_nMajorVersion >= 5 ? "text"
    1985             :                                                                 : "string"));
    1986           4 :                 json_object_object_add(geometry, "coordinates", geo_point);
    1987             :             }
    1988             :             else
    1989             :             {
    1990           1 :                 json_object_object_add(poContainer, pszLastComponent,
    1991             :                                        geo_point);
    1992             :             }
    1993           5 :             if (!m_osPrecision.empty())
    1994             :             {
    1995           1 :                 json_object *field_data = json_object_new_object();
    1996           1 :                 json_object_object_add(geo_point, "fielddata", field_data);
    1997           1 :                 json_object_object_add(field_data, "format",
    1998             :                                        json_object_new_string("compressed"));
    1999           1 :                 json_object_object_add(
    2000             :                     field_data, "precision",
    2001             :                     json_object_new_string(m_osPrecision.c_str()));
    2002             :             }
    2003             :         }
    2004             :         else
    2005             :         {
    2006          13 :             json_object *geometry = json_object_new_object();
    2007          13 :             json_object_object_add(poContainer, pszLastComponent, geometry);
    2008          13 :             json_object_object_add(geometry, "type",
    2009             :                                    json_object_new_string("geo_shape"));
    2010          13 :             if (!m_osPrecision.empty())
    2011           1 :                 json_object_object_add(
    2012             :                     geometry, "precision",
    2013             :                     json_object_new_string(m_osPrecision.c_str()));
    2014             :         }
    2015             :     }
    2016             : 
    2017          13 :     json_object *poMeta = nullptr;
    2018          13 :     json_object *poGeomFields = nullptr;
    2019          13 :     json_object *poFields = nullptr;
    2020          13 :     if (!m_osFID.empty())
    2021             :     {
    2022           5 :         poMeta = json_object_new_object();
    2023           5 :         json_object_object_add(poMeta, "fid",
    2024             :                                json_object_new_string(m_osFID.c_str()));
    2025             :     }
    2026          31 :     for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
    2027             :     {
    2028             :         OGRGeomFieldDefn *poGeomFieldDefn =
    2029          18 :             m_poFeatureDefn->GetGeomFieldDefn(i);
    2030          18 :         if (!m_abIsGeoPoint[i] && poGeomFieldDefn->GetType() != wkbUnknown)
    2031             :         {
    2032           1 :             if (poMeta == nullptr)
    2033           1 :                 poMeta = json_object_new_object();
    2034           1 :             if (poGeomFields == nullptr)
    2035             :             {
    2036           1 :                 poGeomFields = json_object_new_object();
    2037           1 :                 json_object_object_add(poMeta, "geomfields", poGeomFields);
    2038             :             }
    2039           1 :             json_object_object_add(poGeomFields, poGeomFieldDefn->GetNameRef(),
    2040             :                                    json_object_new_string(OGRToOGCGeomType(
    2041             :                                        poGeomFieldDefn->GetType())));
    2042             :         }
    2043             :     }
    2044          47 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    2045             :     {
    2046          34 :         OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    2047          34 :         OGRFieldType eType = poFieldDefn->GetType();
    2048          34 :         if (eType == OFTIntegerList || eType == OFTInteger64List ||
    2049          31 :             eType == OFTRealList || eType == OFTStringList)
    2050             :         {
    2051           4 :             if (poMeta == nullptr)
    2052           1 :                 poMeta = json_object_new_object();
    2053           4 :             if (poFields == nullptr)
    2054             :             {
    2055           1 :                 poFields = json_object_new_object();
    2056           1 :                 json_object_object_add(poMeta, "fields", poFields);
    2057             :             }
    2058           4 :             json_object_object_add(
    2059             :                 poFields, poFieldDefn->GetNameRef(),
    2060             :                 json_object_new_string(OGR_GetFieldTypeName(eType)));
    2061             :         }
    2062             :     }
    2063          13 :     if (poMeta)
    2064           7 :         json_object_object_add(poMapping, "_meta", poMeta);
    2065             : 
    2066          13 :     CPLString jsonMap(json_object_to_json_string(map));
    2067          13 :     json_object_put(map);
    2068             : 
    2069             :     // Got personally caught by that...
    2070          13 :     if (CSLCount(m_papszStoredFields) == 1 &&
    2071           1 :         (EQUAL(m_papszStoredFields[0], "YES") ||
    2072          14 :          EQUAL(m_papszStoredFields[0], "TRUE")) &&
    2073           0 :         m_poFeatureDefn->GetFieldIndex(m_papszStoredFields[0]) < 0)
    2074             :     {
    2075           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    2076             :                  "STORED_FIELDS=%s was specified. Perhaps you meant "
    2077             :                  "STORE_FIELDS=%s instead?",
    2078           0 :                  m_papszStoredFields[0], m_papszStoredFields[0]);
    2079             :     }
    2080             : 
    2081          26 :     return jsonMap;
    2082             : }
    2083             : 
    2084             : /************************************************************************/
    2085             : /*                       BuildGeoJSONGeometry()                         */
    2086             : /************************************************************************/
    2087             : 
    2088          19 : static void BuildGeoJSONGeometry(json_object *geometry,
    2089             :                                  const OGRGeometry *poGeom)
    2090             : {
    2091          19 :     const int nPrecision = 10;
    2092          19 :     double dfEps = pow(10.0, -(double)nPrecision);
    2093          19 :     const char *pszGeomType = "";
    2094          19 :     switch (wkbFlatten(poGeom->getGeometryType()))
    2095             :     {
    2096           6 :         case wkbPoint:
    2097           6 :             pszGeomType = "point";
    2098           6 :             break;
    2099           3 :         case wkbLineString:
    2100           3 :             pszGeomType = "linestring";
    2101           3 :             break;
    2102           2 :         case wkbPolygon:
    2103           2 :             pszGeomType = "polygon";
    2104           2 :             break;
    2105           2 :         case wkbMultiPoint:
    2106           2 :             pszGeomType = "multipoint";
    2107           2 :             break;
    2108           2 :         case wkbMultiLineString:
    2109           2 :             pszGeomType = "multilinestring";
    2110           2 :             break;
    2111           2 :         case wkbMultiPolygon:
    2112           2 :             pszGeomType = "multipolygon";
    2113           2 :             break;
    2114           2 :         case wkbGeometryCollection:
    2115           2 :             pszGeomType = "geometrycollection";
    2116           2 :             break;
    2117           0 :         default:
    2118           0 :             break;
    2119             :     }
    2120          19 :     json_object_object_add(geometry, "type",
    2121             :                            json_object_new_string(pszGeomType));
    2122             : 
    2123          19 :     switch (wkbFlatten(poGeom->getGeometryType()))
    2124             :     {
    2125           6 :         case wkbPoint:
    2126             :         {
    2127           6 :             const OGRPoint *poPoint = poGeom->toPoint();
    2128           6 :             json_object *coordinates = json_object_new_array();
    2129           6 :             json_object_object_add(geometry, "coordinates", coordinates);
    2130           6 :             json_object_array_add(coordinates,
    2131             :                                   json_object_new_double_with_precision(
    2132             :                                       poPoint->getX(), nPrecision));
    2133           6 :             json_object_array_add(coordinates,
    2134             :                                   json_object_new_double_with_precision(
    2135             :                                       poPoint->getY(), nPrecision));
    2136           6 :             break;
    2137             :         }
    2138             : 
    2139           3 :         case wkbLineString:
    2140             :         {
    2141           3 :             const OGRLineString *poLS = poGeom->toLineString();
    2142           3 :             json_object *coordinates = json_object_new_array();
    2143           3 :             json_object_object_add(geometry, "coordinates", coordinates);
    2144           9 :             for (int i = 0; i < poLS->getNumPoints(); i++)
    2145             :             {
    2146           6 :                 json_object *point = json_object_new_array();
    2147           6 :                 json_object_array_add(coordinates, point);
    2148           6 :                 json_object_array_add(
    2149             :                     point, json_object_new_double_with_precision(poLS->getX(i),
    2150             :                                                                  nPrecision));
    2151           6 :                 json_object_array_add(
    2152             :                     point, json_object_new_double_with_precision(poLS->getY(i),
    2153             :                                                                  nPrecision));
    2154             :             }
    2155           3 :             break;
    2156             :         }
    2157             : 
    2158           2 :         case wkbPolygon:
    2159             :         {
    2160           2 :             const OGRPolygon *poPoly = poGeom->toPolygon();
    2161           2 :             json_object *coordinates = json_object_new_array();
    2162           2 :             json_object_object_add(geometry, "coordinates", coordinates);
    2163           6 :             for (auto &&poLS : *poPoly)
    2164             :             {
    2165           4 :                 json_object *ring = json_object_new_array();
    2166           4 :                 json_object_array_add(coordinates, ring);
    2167          20 :                 for (int j = 0; j < poLS->getNumPoints(); j++)
    2168             :                 {
    2169          28 :                     if (j > 0 &&
    2170          20 :                         fabs(poLS->getX(j) - poLS->getX(j - 1)) < dfEps &&
    2171           4 :                         fabs(poLS->getY(j) - poLS->getY(j - 1)) < dfEps)
    2172           0 :                         continue;
    2173          16 :                     json_object *point = json_object_new_array();
    2174          16 :                     json_object_array_add(ring, point);
    2175          16 :                     json_object_array_add(point,
    2176             :                                           json_object_new_double_with_precision(
    2177          16 :                                               poLS->getX(j), nPrecision));
    2178          16 :                     json_object_array_add(point,
    2179             :                                           json_object_new_double_with_precision(
    2180          16 :                                               poLS->getY(j), nPrecision));
    2181             :                 }
    2182             :             }
    2183           2 :             break;
    2184             :         }
    2185             : 
    2186           2 :         case wkbMultiPoint:
    2187             :         {
    2188           2 :             const OGRMultiPoint *poMP = poGeom->toMultiPoint();
    2189           2 :             json_object *coordinates = json_object_new_array();
    2190           2 :             json_object_object_add(geometry, "coordinates", coordinates);
    2191           6 :             for (auto &&poPoint : *poMP)
    2192             :             {
    2193           4 :                 json_object *point = json_object_new_array();
    2194           4 :                 json_object_array_add(coordinates, point);
    2195           4 :                 json_object_array_add(point,
    2196             :                                       json_object_new_double_with_precision(
    2197             :                                           poPoint->getX(), nPrecision));
    2198           4 :                 json_object_array_add(point,
    2199             :                                       json_object_new_double_with_precision(
    2200             :                                           poPoint->getY(), nPrecision));
    2201             :             }
    2202           2 :             break;
    2203             :         }
    2204             : 
    2205           2 :         case wkbMultiLineString:
    2206             :         {
    2207           2 :             const OGRMultiLineString *poMLS = poGeom->toMultiLineString();
    2208           2 :             json_object *coordinates = json_object_new_array();
    2209           2 :             json_object_object_add(geometry, "coordinates", coordinates);
    2210           6 :             for (auto &&poLS : *poMLS)
    2211             :             {
    2212           4 :                 json_object *ls = json_object_new_array();
    2213           4 :                 json_object_array_add(coordinates, ls);
    2214          12 :                 for (auto &&oPoint : *poLS)
    2215             :                 {
    2216           8 :                     json_object *point = json_object_new_array();
    2217           8 :                     json_object_array_add(ls, point);
    2218           8 :                     json_object_array_add(point,
    2219             :                                           json_object_new_double_with_precision(
    2220             :                                               oPoint.getX(), nPrecision));
    2221           8 :                     json_object_array_add(point,
    2222             :                                           json_object_new_double_with_precision(
    2223             :                                               oPoint.getY(), nPrecision));
    2224             :                 }
    2225             :             }
    2226           2 :             break;
    2227             :         }
    2228             : 
    2229           2 :         case wkbMultiPolygon:
    2230             :         {
    2231           2 :             const OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
    2232           2 :             json_object *coordinates = json_object_new_array();
    2233           2 :             json_object_object_add(geometry, "coordinates", coordinates);
    2234           6 :             for (auto &&poPoly : *poMP)
    2235             :             {
    2236           4 :                 json_object *poly = json_object_new_array();
    2237           4 :                 json_object_array_add(coordinates, poly);
    2238          10 :                 for (auto &&poLS : *poPoly)
    2239             :                 {
    2240           6 :                     json_object *ring = json_object_new_array();
    2241           6 :                     json_object_array_add(poly, ring);
    2242          30 :                     for (int k = 0; k < poLS->getNumPoints(); k++)
    2243             :                     {
    2244          42 :                         if (k > 0 &&
    2245          30 :                             fabs(poLS->getX(k) - poLS->getX(k - 1)) < dfEps &&
    2246           6 :                             fabs(poLS->getY(k) - poLS->getY(k - 1)) < dfEps)
    2247           0 :                             continue;
    2248          24 :                         json_object *point = json_object_new_array();
    2249          24 :                         json_object_array_add(ring, point);
    2250          24 :                         json_object_array_add(
    2251             :                             point, json_object_new_double_with_precision(
    2252          24 :                                        poLS->getX(k), nPrecision));
    2253          24 :                         json_object_array_add(
    2254             :                             point, json_object_new_double_with_precision(
    2255          24 :                                        poLS->getY(k), nPrecision));
    2256             :                     }
    2257             :                 }
    2258             :             }
    2259           2 :             break;
    2260             :         }
    2261             : 
    2262           2 :         case wkbGeometryCollection:
    2263             :         {
    2264           2 :             const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    2265           2 :             json_object *geometries = json_object_new_array();
    2266           2 :             json_object_object_add(geometry, "geometries", geometries);
    2267          14 :             for (auto &&poSubGeom : *poGC)
    2268             :             {
    2269          12 :                 json_object *subgeom = json_object_new_object();
    2270          12 :                 json_object_array_add(geometries, subgeom);
    2271          12 :                 BuildGeoJSONGeometry(subgeom, poSubGeom);
    2272             :             }
    2273           2 :             break;
    2274             :         }
    2275             : 
    2276           0 :         default:
    2277           0 :             break;
    2278             :     }
    2279          19 : }
    2280             : 
    2281             : /************************************************************************/
    2282             : /*                       WriteMapIfNecessary()                          */
    2283             : /************************************************************************/
    2284             : 
    2285          86 : OGRErr OGRElasticLayer::WriteMapIfNecessary()
    2286             : {
    2287          86 :     if (m_bManualMapping)
    2288          11 :         return OGRERR_NONE;
    2289             : 
    2290             :     // Check to see if the user has elected to only write out the mapping file
    2291             :     // This method will only write out one layer from the vector file in cases
    2292             :     // where there are multiple layers
    2293          75 :     if (!m_osWriteMapFilename.empty())
    2294             :     {
    2295           4 :         if (m_bSerializeMapping)
    2296             :         {
    2297           2 :             m_bSerializeMapping = false;
    2298           4 :             CPLString map = BuildMap();
    2299             : 
    2300             :             // Write the map to a file
    2301           2 :             VSILFILE *f = VSIFOpenL(m_osWriteMapFilename, "wb");
    2302           2 :             if (f)
    2303             :             {
    2304           2 :                 VSIFWriteL(map.c_str(), 1, map.length(), f);
    2305           2 :                 VSIFCloseL(f);
    2306             :             }
    2307             :         }
    2308           4 :         return OGRERR_NONE;
    2309             :     }
    2310             : 
    2311             :     // Check to see if we have any fields to upload to this index
    2312          71 :     if (m_osWriteMapFilename.empty() && m_bSerializeMapping)
    2313             :     {
    2314          11 :         m_bSerializeMapping = false;
    2315          11 :         CPLString osURL = BuildMappingURL(true);
    2316          11 :         if (!m_poDS->UploadFile(osURL.c_str(), BuildMap()))
    2317             :         {
    2318           1 :             return OGRERR_FAILURE;
    2319             :         }
    2320             :     }
    2321             : 
    2322          70 :     return OGRERR_NONE;
    2323             : }
    2324             : 
    2325             : /************************************************************************/
    2326             : /*                      GetContainerForFeature()                        */
    2327             : /************************************************************************/
    2328             : 
    2329             : static json_object *
    2330          66 : GetContainerForFeature(json_object *poContainer,
    2331             :                        const std::vector<CPLString> &aosPath,
    2332             :                        std::map<std::vector<CPLString>, json_object *> &oMap)
    2333             : {
    2334          66 :     std::vector<CPLString> aosSubPath;
    2335         121 :     for (int j = 0; j < (int)aosPath.size() - 1; j++)
    2336             :     {
    2337          55 :         aosSubPath.push_back(aosPath[j]);
    2338             :         std::map<std::vector<CPLString>, json_object *>::iterator oIter =
    2339          55 :             oMap.find(aosSubPath);
    2340          55 :         if (oIter == oMap.end())
    2341             :         {
    2342          22 :             json_object *poNewContainer = json_object_new_object();
    2343          22 :             json_object_object_add(poContainer, aosPath[j], poNewContainer);
    2344          22 :             oMap[aosSubPath] = poNewContainer;
    2345          22 :             poContainer = poNewContainer;
    2346             :         }
    2347             :         else
    2348             :         {
    2349          33 :             poContainer = oIter->second;
    2350             :         }
    2351             :     }
    2352         132 :     return poContainer;
    2353             : }
    2354             : 
    2355             : /************************************************************************/
    2356             : /*                        BuildMappingURL()                             */
    2357             : /************************************************************************/
    2358          90 : CPLString OGRElasticLayer::BuildMappingURL(bool bMappingApi)
    2359             : {
    2360             :     CPLString osURL =
    2361          90 :         CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str());
    2362          90 :     if (bMappingApi)
    2363          11 :         osURL += "/_mapping";
    2364          90 :     if (m_poDS->m_nMajorVersion < 7)
    2365          85 :         osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    2366          90 :     return osURL;
    2367             : }
    2368             : 
    2369             : /************************************************************************/
    2370             : /*                        BuildJSonFromFeature()                        */
    2371             : /************************************************************************/
    2372             : 
    2373          24 : CPLString OGRElasticLayer::BuildJSonFromFeature(OGRFeature *poFeature)
    2374             : {
    2375             : 
    2376          24 :     CPLString fields;
    2377          24 :     int nJSonFieldIndex = m_poFeatureDefn->GetFieldIndex("_json");
    2378          25 :     if (nJSonFieldIndex >= 0 &&
    2379           1 :         poFeature->IsFieldSetAndNotNull(nJSonFieldIndex))
    2380             :     {
    2381           1 :         fields = poFeature->GetFieldAsString(nJSonFieldIndex);
    2382             :     }
    2383             :     else
    2384             :     {
    2385          23 :         json_object *fieldObject = json_object_new_object();
    2386             : 
    2387          23 :         if (poFeature->GetFID() >= 0 && !m_osFID.empty())
    2388             :         {
    2389           9 :             json_object_object_add(fieldObject, m_osFID.c_str(),
    2390           9 :                                    json_object_new_int64(poFeature->GetFID()));
    2391             :         }
    2392             : 
    2393          46 :         std::map<std::vector<CPLString>, json_object *> oMap;
    2394             : 
    2395          47 :         for (int i = 0; i < poFeature->GetGeomFieldCount(); i++)
    2396             :         {
    2397          24 :             OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
    2398          24 :             if (poGeom != nullptr && !poGeom->IsEmpty())
    2399             :             {
    2400          13 :                 OGREnvelope env;
    2401          13 :                 poGeom->getEnvelope(&env);
    2402             : 
    2403          13 :                 if (m_apoCT[i] != nullptr)
    2404           1 :                     poGeom->transform(m_apoCT[i]);
    2405          12 :                 else if (env.MinX < -180 || env.MinY < -90 || env.MaxX > 180 ||
    2406          11 :                          env.MaxY > 90)
    2407             :                 {
    2408             :                     static bool bHasWarned = false;
    2409           1 :                     if (!bHasWarned)
    2410             :                     {
    2411           1 :                         bHasWarned = true;
    2412           1 :                         CPLError(
    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             : 
    2420          26 :                 std::vector<CPLString> aosPath = m_aaosGeomFieldPaths[i];
    2421          13 :                 bool bAddGeoJSONType = false;
    2422          17 :                 if (m_abIsGeoPoint[i] && aosPath.size() >= 2 &&
    2423           4 :                     aosPath.back() == "coordinates")
    2424             :                 {
    2425           4 :                     bAddGeoJSONType = true;
    2426           4 :                     aosPath.resize((int)aosPath.size() - 1);
    2427             :                 }
    2428             : 
    2429             :                 json_object *poContainer =
    2430          13 :                     GetContainerForFeature(fieldObject, aosPath, oMap);
    2431          13 :                 const char *pszLastComponent = aosPath.back();
    2432             : 
    2433          13 :                 if (m_abIsGeoPoint[i])
    2434             :                 {
    2435           5 :                     json_object *coordinates = json_object_new_array();
    2436           5 :                     const int nPrecision = 10;
    2437           5 :                     json_object_array_add(
    2438             :                         coordinates,
    2439             :                         json_object_new_double_with_precision(
    2440           5 :                             (env.MaxX + env.MinX) * 0.5, nPrecision));
    2441           5 :                     json_object_array_add(
    2442             :                         coordinates,
    2443             :                         json_object_new_double_with_precision(
    2444           5 :                             (env.MaxY + env.MinY) * 0.5, nPrecision));
    2445             : 
    2446           5 :                     if (bAddGeoJSONType)
    2447             :                     {
    2448           4 :                         json_object *geometry = json_object_new_object();
    2449           4 :                         json_object_object_add(poContainer, pszLastComponent,
    2450             :                                                geometry);
    2451           4 :                         json_object_object_add(geometry, "type",
    2452             :                                                json_object_new_string("Point"));
    2453           4 :                         json_object_object_add(geometry, "coordinates",
    2454             :                                                coordinates);
    2455             :                     }
    2456             :                     else
    2457             :                     {
    2458           1 :                         json_object_object_add(poContainer, pszLastComponent,
    2459             :                                                coordinates);
    2460             :                     }
    2461             :                 }
    2462             :                 else
    2463             :                 {
    2464           8 :                     if (m_bGeoShapeAsGeoJSON)
    2465             :                     {
    2466           7 :                         json_object *geometry = json_object_new_object();
    2467           7 :                         json_object_object_add(poContainer, pszLastComponent,
    2468             :                                                geometry);
    2469           7 :                         BuildGeoJSONGeometry(geometry, poGeom);
    2470             :                     }
    2471             :                     else
    2472             :                     {
    2473           1 :                         char *pszWKT = nullptr;
    2474           1 :                         poGeom->exportToWkt(&pszWKT);
    2475           1 :                         json_object_object_add(poContainer, pszLastComponent,
    2476             :                                                json_object_new_string(pszWKT));
    2477           1 :                         CPLFree(pszWKT);
    2478             :                     }
    2479             :                 }
    2480             :             }
    2481             :         }
    2482             : 
    2483          23 :         if (m_osMappingName == "FeatureCollection")
    2484             :         {
    2485          37 :             if (poFeature->GetGeomFieldCount() == 1 &&
    2486          17 :                 poFeature->GetGeomFieldRef(0))
    2487             :             {
    2488           7 :                 json_object_object_add(fieldObject, "type",
    2489             :                                        json_object_new_string("Feature"));
    2490             :             }
    2491             : 
    2492          40 :             std::vector<CPLString> aosPath;
    2493          20 :             aosPath.push_back("properties");
    2494          20 :             aosPath.push_back("dummy");
    2495          20 :             GetContainerForFeature(fieldObject, aosPath, oMap);
    2496             :         }
    2497             : 
    2498             :         // For every field (except _id)
    2499          23 :         int fieldCount = m_poFeatureDefn->GetFieldCount();
    2500         115 :         for (int i = 1; i < fieldCount; i++)
    2501             :         {
    2502          92 :             if (!poFeature->IsFieldSet(i))
    2503             :             {
    2504          59 :                 continue;
    2505             :             }
    2506             : 
    2507             :             json_object *poContainer =
    2508          33 :                 GetContainerForFeature(fieldObject, m_aaosFieldPaths[i], oMap);
    2509          33 :             const char *pszLastComponent = m_aaosFieldPaths[i].back();
    2510             : 
    2511          33 :             if (poFeature->IsFieldNull(i))
    2512             :             {
    2513           1 :                 json_object_object_add(poContainer, pszLastComponent, nullptr);
    2514           1 :                 continue;
    2515             :             }
    2516             : 
    2517          32 :             switch (m_poFeatureDefn->GetFieldDefn(i)->GetType())
    2518             :             {
    2519           5 :                 case OFTInteger:
    2520           5 :                     if (m_poFeatureDefn->GetFieldDefn(i)->GetSubType() ==
    2521             :                         OFSTBoolean)
    2522           2 :                         json_object_object_add(
    2523             :                             poContainer, pszLastComponent,
    2524             :                             json_object_new_boolean(
    2525             :                                 poFeature->GetFieldAsInteger(i)));
    2526             :                     else
    2527           3 :                         json_object_object_add(
    2528             :                             poContainer, pszLastComponent,
    2529             :                             json_object_new_int(
    2530             :                                 poFeature->GetFieldAsInteger(i)));
    2531           5 :                     break;
    2532           2 :                 case OFTInteger64:
    2533           2 :                     json_object_object_add(
    2534             :                         poContainer, pszLastComponent,
    2535             :                         json_object_new_int64(
    2536           2 :                             poFeature->GetFieldAsInteger64(i)));
    2537           2 :                     break;
    2538           2 :                 case OFTReal:
    2539           2 :                     json_object_object_add(
    2540             :                         poContainer, pszLastComponent,
    2541             :                         json_object_new_double_with_significant_figures(
    2542             :                             poFeature->GetFieldAsDouble(i), -1));
    2543           2 :                     break;
    2544           2 :                 case OFTIntegerList:
    2545             :                 {
    2546           2 :                     int nCount = 0;
    2547             :                     const int *panValues =
    2548           2 :                         poFeature->GetFieldAsIntegerList(i, &nCount);
    2549           2 :                     json_object *poArray = json_object_new_array();
    2550           6 :                     for (int j = 0; j < nCount; j++)
    2551           4 :                         json_object_array_add(
    2552           4 :                             poArray, json_object_new_int(panValues[j]));
    2553           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2554             :                                            poArray);
    2555           2 :                     break;
    2556             :                 }
    2557           2 :                 case OFTInteger64List:
    2558             :                 {
    2559           2 :                     int nCount = 0;
    2560             :                     const GIntBig *panValues =
    2561           2 :                         poFeature->GetFieldAsInteger64List(i, &nCount);
    2562           2 :                     json_object *poArray = json_object_new_array();
    2563           6 :                     for (int j = 0; j < nCount; j++)
    2564           4 :                         json_object_array_add(
    2565           4 :                             poArray, json_object_new_int64(panValues[j]));
    2566           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2567             :                                            poArray);
    2568           2 :                     break;
    2569             :                 }
    2570           2 :                 case OFTRealList:
    2571             :                 {
    2572           2 :                     int nCount = 0;
    2573             :                     const double *padfValues =
    2574           2 :                         poFeature->GetFieldAsDoubleList(i, &nCount);
    2575           2 :                     json_object *poArray = json_object_new_array();
    2576           6 :                     for (int j = 0; j < nCount; j++)
    2577           4 :                         json_object_array_add(
    2578             :                             poArray,
    2579             :                             json_object_new_double_with_significant_figures(
    2580           4 :                                 padfValues[j], -1));
    2581           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2582             :                                            poArray);
    2583           2 :                     break;
    2584             :                 }
    2585           2 :                 case OFTStringList:
    2586             :                 {
    2587           2 :                     char **papszValues = poFeature->GetFieldAsStringList(i);
    2588           2 :                     json_object *poArray = json_object_new_array();
    2589           6 :                     for (int j = 0; papszValues[j] != nullptr; j++)
    2590           4 :                         json_object_array_add(
    2591           4 :                             poArray, json_object_new_string(papszValues[j]));
    2592           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2593             :                                            poArray);
    2594           2 :                     break;
    2595             :                 }
    2596           2 :                 case OFTBinary:
    2597             :                 {
    2598           2 :                     int nCount = 0;
    2599           2 :                     GByte *pabyVal = poFeature->GetFieldAsBinary(i, &nCount);
    2600           2 :                     char *pszVal = CPLBase64Encode(nCount, pabyVal);
    2601           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2602             :                                            json_object_new_string(pszVal));
    2603           2 :                     CPLFree(pszVal);
    2604           2 :                     break;
    2605             :                 }
    2606           3 :                 case OFTDateTime:
    2607             :                 {
    2608           3 :                     int nYear = 0;
    2609           3 :                     int nMonth = 0;
    2610           3 :                     int nDay = 0;
    2611           3 :                     int nHour = 0;
    2612           3 :                     int nMin = 0;
    2613           3 :                     int nTZ = 0;
    2614           3 :                     float fSec = 0.0f;
    2615           3 :                     poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay,
    2616             :                                                   &nHour, &nMin, &fSec, &nTZ);
    2617           3 :                     if (nTZ == 0)
    2618             :                     {
    2619           2 :                         json_object_object_add(
    2620             :                             poContainer, pszLastComponent,
    2621             :                             json_object_new_string(CPLSPrintf(
    2622             :                                 "%04d/%02d/%02d %02d:%02d:%06.3f", nYear,
    2623             :                                 nMonth, nDay, nHour, nMin, fSec)));
    2624             :                     }
    2625             :                     else
    2626             :                     {
    2627           1 :                         const int TZOffset = std::abs(nTZ - 100) * 15;
    2628           1 :                         const int TZHour = TZOffset / 60;
    2629           1 :                         const int TZMinute = TZOffset - TZHour * 60;
    2630           1 :                         json_object_object_add(
    2631             :                             poContainer, pszLastComponent,
    2632             :                             json_object_new_string(CPLSPrintf(
    2633             :                                 "%04d/%02d/%02d %02d:%02d:%06.3f%c%02d:%02d",
    2634             :                                 nYear, nMonth, nDay, nHour, nMin, fSec,
    2635           1 :                                 (nTZ >= 100) ? '+' : '-', TZHour, TZMinute)));
    2636             :                     }
    2637           3 :                     break;
    2638             :                 }
    2639          10 :                 default:
    2640             :                 {
    2641          10 :                     const char *pszVal = poFeature->GetFieldAsString(i);
    2642          10 :                     json_object_object_add(poContainer, pszLastComponent,
    2643             :                                            json_object_new_string(pszVal));
    2644             :                 }
    2645             :             }
    2646             :         }
    2647             : 
    2648             :         // Build the field string
    2649          23 :         fields = json_object_to_json_string(fieldObject);
    2650          23 :         json_object_put(fieldObject);
    2651             :     }
    2652             : 
    2653          24 :     return fields;
    2654             : }
    2655             : 
    2656             : /************************************************************************/
    2657             : /*                          ICreateFeature()                            */
    2658             : /************************************************************************/
    2659             : 
    2660          20 : OGRErr OGRElasticLayer::ICreateFeature(OGRFeature *poFeature)
    2661             : {
    2662          20 :     if (m_poDS->GetAccess() != GA_Update)
    2663             :     {
    2664           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2665             :                  "Dataset opened in read-only mode");
    2666           1 :         return OGRERR_FAILURE;
    2667             :     }
    2668             : 
    2669          19 :     FinalizeFeatureDefn();
    2670             : 
    2671          19 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    2672           1 :         return OGRERR_FAILURE;
    2673             : 
    2674          18 :     if (!m_osWriteMapFilename.empty())
    2675           2 :         return OGRERR_NONE;
    2676             : 
    2677          16 :     if (poFeature->GetFID() < 0)
    2678             :     {
    2679          14 :         if (m_nNextFID < 0)
    2680           1 :             m_nNextFID = GetFeatureCount(FALSE);
    2681          14 :         poFeature->SetFID(++m_nNextFID);
    2682             :     }
    2683             : 
    2684          32 :     CPLString osFields(BuildJSonFromFeature(poFeature));
    2685             : 
    2686          16 :     const char *pszId = nullptr;
    2687          16 :     if (poFeature->IsFieldSetAndNotNull(0) && !m_bIgnoreSourceID)
    2688           1 :         pszId = poFeature->GetFieldAsString(0);
    2689             : 
    2690             :     // Check to see if we're using bulk uploading
    2691          16 :     if (m_nBulkUpload > 0)
    2692             :     {
    2693             :         m_osBulkContent +=
    2694           7 :             CPLSPrintf("{\"index\" :{\"_index\":\"%s\"", m_osIndexName.c_str());
    2695           7 :         if (m_poDS->m_nMajorVersion < 7)
    2696             :             m_osBulkContent +=
    2697           6 :                 CPLSPrintf(", \"_type\":\"%s\"", m_osMappingName.c_str());
    2698           7 :         if (pszId)
    2699           0 :             m_osBulkContent += CPLSPrintf(",\"_id\":\"%s\"", pszId);
    2700           7 :         m_osBulkContent += "}}\n" + osFields + "\n\n";
    2701             : 
    2702             :         // Only push the data if we are over our bulk upload limit
    2703           7 :         if ((int)m_osBulkContent.length() > m_nBulkUpload)
    2704             :         {
    2705           0 :             if (!PushIndex())
    2706             :             {
    2707           0 :                 return OGRERR_FAILURE;
    2708             :             }
    2709             :         }
    2710             :     }
    2711             :     else
    2712             :     {
    2713             :         // Fall back to using single item upload for every feature.
    2714           9 :         CPLString osURL(BuildMappingURL(false));
    2715           9 :         if (pszId)
    2716           1 :             osURL += CPLSPrintf("/%s", pszId);
    2717           9 :         json_object *poRes = m_poDS->RunRequest(osURL, osFields);
    2718           9 :         if (poRes == nullptr)
    2719             :         {
    2720           1 :             return OGRERR_FAILURE;
    2721             :         }
    2722           8 :         if (pszId == nullptr)
    2723             :         {
    2724           7 :             json_object *poId = CPL_json_object_object_get(poRes, "_id");
    2725           8 :             if (poId != nullptr &&
    2726           1 :                 json_object_get_type(poId) == json_type_string)
    2727             :             {
    2728           1 :                 pszId = json_object_get_string(poId);
    2729           1 :                 poFeature->SetField(0, pszId);
    2730             :             }
    2731             :         }
    2732           8 :         json_object_put(poRes);
    2733             :     }
    2734             : 
    2735          15 :     return OGRERR_NONE;
    2736             : }
    2737             : 
    2738             : /************************************************************************/
    2739             : /*                           ISetFeature()                              */
    2740             : /************************************************************************/
    2741             : 
    2742           6 : OGRErr OGRElasticLayer::ISetFeature(OGRFeature *poFeature)
    2743             : {
    2744           6 :     if (m_poDS->GetAccess() != GA_Update)
    2745             :     {
    2746           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2747             :                  "Dataset opened in read-only mode");
    2748           1 :         return OGRERR_FAILURE;
    2749             :     }
    2750             : 
    2751           5 :     FinalizeFeatureDefn();
    2752             : 
    2753           5 :     if (!poFeature->IsFieldSetAndNotNull(0))
    2754             :     {
    2755           1 :         CPLError(CE_Failure, CPLE_AppDefined, "_id field not set");
    2756           1 :         return OGRERR_FAILURE;
    2757             :     }
    2758           4 :     if (poFeature->GetFID() < 0 && !m_osFID.empty())
    2759             :     {
    2760           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid FID");
    2761           0 :         return OGRERR_FAILURE;
    2762             :     }
    2763             : 
    2764           4 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    2765           0 :         return OGRERR_FAILURE;
    2766           4 :     PushIndex();
    2767             : 
    2768           8 :     CPLString osFields(BuildJSonFromFeature(poFeature));
    2769             : 
    2770             :     // TODO? we should theoretically detect if the provided _id doesn't exist
    2771             :     CPLString osURL(
    2772           8 :         CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str()));
    2773           4 :     if (m_poDS->m_nMajorVersion < 7)
    2774           4 :         osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    2775           4 :     osURL += CPLSPrintf("/%s", poFeature->GetFieldAsString(0));
    2776           4 :     json_object *poRes = m_poDS->RunRequest(osURL, osFields);
    2777           4 :     if (poRes == nullptr)
    2778             :     {
    2779           1 :         return OGRERR_FAILURE;
    2780             :     }
    2781             :     // CPLDebug("ES", "SetFeature(): %s", json_object_to_json_string(poRes));
    2782           3 :     json_object_put(poRes);
    2783             : 
    2784           3 :     return OGRERR_NONE;
    2785             : }
    2786             : 
    2787             : /************************************************************************/
    2788             : /*                          IUpsertFeature()                            */
    2789             : /************************************************************************/
    2790             : 
    2791           5 : OGRErr OGRElasticLayer::IUpsertFeature(OGRFeature *poFeature)
    2792             : {
    2793           5 :     if (m_poDS->GetAccess() != GA_Update)
    2794             :     {
    2795           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2796             :                  "Dataset opened in read-only mode");
    2797           1 :         return OGRERR_FAILURE;
    2798             :     }
    2799             : 
    2800           4 :     FinalizeFeatureDefn();
    2801             : 
    2802           4 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    2803           0 :         return OGRERR_FAILURE;
    2804             : 
    2805           4 :     if (!m_osWriteMapFilename.empty())
    2806           0 :         return OGRERR_NONE;
    2807             : 
    2808           4 :     if (poFeature->GetFID() < 0)
    2809             :     {
    2810           1 :         if (m_nNextFID < 0)
    2811           0 :             m_nNextFID = GetFeatureCount(FALSE);
    2812           1 :         poFeature->SetFID(++m_nNextFID);
    2813             :     }
    2814             : 
    2815           8 :     CPLString osFields(BuildJSonFromFeature(poFeature));
    2816             : 
    2817           4 :     const char *pszId = nullptr;
    2818           4 :     if (poFeature->IsFieldSetAndNotNull(0))
    2819             :     {
    2820           4 :         pszId = poFeature->GetFieldAsString(0);
    2821             :     }
    2822             :     else
    2823             :     {
    2824           0 :         return OGRERR_FAILURE;
    2825             :     }
    2826             : 
    2827             :     // Check to see if we're using bulk uploading
    2828           4 :     if (m_nBulkUpload > 0)
    2829             :     {
    2830             :         m_osBulkContent +=
    2831             :             CPLSPrintf("{\"update\":{\"_index\":\"%s\",\"_id\":\"%s\"",
    2832           2 :                        m_osIndexName.c_str(), pszId);
    2833           2 :         if (m_poDS->m_nMajorVersion < 7)
    2834             :         {
    2835             :             m_osBulkContent +=
    2836           2 :                 CPLSPrintf(", \"_type\":\"%s\"", m_osMappingName.c_str());
    2837             :         }
    2838             :         m_osBulkContent +=
    2839           2 :             "}}\n{\"doc\":" + osFields + ",\"doc_as_upsert\":true}\n\n";
    2840             : 
    2841             :         // Only push the data if we are over our bulk upload limit
    2842           2 :         if (m_osBulkContent.length() > static_cast<size_t>(m_nBulkUpload))
    2843             :         {
    2844           0 :             if (!PushIndex())
    2845             :             {
    2846           0 :                 return OGRERR_FAILURE;
    2847             :             }
    2848             :         }
    2849             :     }
    2850             :     else
    2851             :     {
    2852             :         // Fall back to using single item upload for every feature.
    2853           2 :         CPLString osURL(BuildMappingURL(false));
    2854           2 :         if (m_poDS->m_nMajorVersion < 7)
    2855             :         {
    2856           2 :             osURL += CPLSPrintf("/%s/_update", pszId);
    2857             :         }
    2858             :         else
    2859             :         {
    2860           0 :             osURL += CPLSPrintf("/_update/%s", pszId);
    2861             :         }
    2862             : 
    2863             :         const CPLString osUpdate =
    2864           2 :             CPLSPrintf("{\"doc\":%s,\"doc_as_upsert\":true}", osFields.c_str());
    2865           2 :         const CPLString osMethod = "POST";
    2866           2 :         if (!m_poDS->UploadFile(osURL, osUpdate, osMethod))
    2867             :         {
    2868           0 :             return OGRERR_FAILURE;
    2869             :         }
    2870             :     }
    2871             : 
    2872           4 :     return OGRERR_NONE;
    2873             : }
    2874             : 
    2875             : /************************************************************************/
    2876             : /*                             PushIndex()                              */
    2877             : /************************************************************************/
    2878             : 
    2879          63 : bool OGRElasticLayer::PushIndex()
    2880             : {
    2881          63 :     if (m_osBulkContent.empty())
    2882             :     {
    2883          56 :         return true;
    2884             :     }
    2885             : 
    2886          14 :     const bool bRet = m_poDS->UploadFile(
    2887           7 :         CPLSPrintf("%s/_bulk", m_poDS->GetURL()), m_osBulkContent);
    2888           7 :     m_osBulkContent.clear();
    2889             : 
    2890           7 :     return bRet;
    2891             : }
    2892             : 
    2893             : /************************************************************************/
    2894             : /*                            CreateField()                             */
    2895             : /************************************************************************/
    2896             : 
    2897          17 : OGRErr OGRElasticLayer::CreateField(const OGRFieldDefn *poFieldDefn,
    2898             :                                     int /*bApproxOK*/)
    2899             : {
    2900          17 :     if (m_poDS->GetAccess() != GA_Update)
    2901             :     {
    2902           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2903             :                  "Dataset opened in read-only mode");
    2904           1 :         return OGRERR_FAILURE;
    2905             :     }
    2906             : 
    2907          16 :     FinalizeFeatureDefn();
    2908          16 :     ResetReading();
    2909             : 
    2910          16 :     if (m_poFeatureDefn->GetFieldIndex(poFieldDefn->GetNameRef()) >= 0)
    2911             :     {
    2912           0 :         if (!EQUAL(poFieldDefn->GetNameRef(), "_id") &&
    2913           0 :             !EQUAL(poFieldDefn->GetNameRef(), "_json"))
    2914             :         {
    2915           0 :             CPLError(
    2916             :                 CE_Failure, CPLE_AppDefined,
    2917             :                 "CreateField() called with an already existing field name: %s",
    2918             :                 poFieldDefn->GetNameRef());
    2919             :         }
    2920           0 :         return OGRERR_FAILURE;
    2921             :     }
    2922             : 
    2923          16 :     std::vector<CPLString> aosPath;
    2924          16 :     if (m_osMappingName == "FeatureCollection")
    2925          14 :         aosPath.push_back("properties");
    2926             : 
    2927          16 :     if (m_bDotAsNestedField)
    2928             :     {
    2929             :         char **papszTokens =
    2930          16 :             CSLTokenizeString2(poFieldDefn->GetNameRef(), ".", 0);
    2931          33 :         for (int i = 0; papszTokens[i]; i++)
    2932          17 :             aosPath.push_back(papszTokens[i]);
    2933          16 :         CSLDestroy(papszTokens);
    2934             :     }
    2935             :     else
    2936           0 :         aosPath.push_back(poFieldDefn->GetNameRef());
    2937             : 
    2938          16 :     AddFieldDefn(poFieldDefn->GetNameRef(), poFieldDefn->GetType(), aosPath,
    2939             :                  poFieldDefn->GetSubType());
    2940             : 
    2941          16 :     m_bSerializeMapping = true;
    2942             : 
    2943          16 :     return OGRERR_NONE;
    2944             : }
    2945             : 
    2946             : /************************************************************************/
    2947             : /*                           CreateGeomField()                          */
    2948             : /************************************************************************/
    2949             : 
    2950          20 : OGRErr OGRElasticLayer::CreateGeomField(const OGRGeomFieldDefn *poFieldIn,
    2951             :                                         int /*bApproxOK*/)
    2952             : 
    2953             : {
    2954          20 :     if (m_poDS->GetAccess() != GA_Update)
    2955             :     {
    2956           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2957             :                  "Dataset opened in read-only mode");
    2958           1 :         return OGRERR_FAILURE;
    2959             :     }
    2960             : 
    2961          19 :     FinalizeFeatureDefn();
    2962          19 :     ResetReading();
    2963             : 
    2964          19 :     if (m_poFeatureDefn->GetGeomFieldIndex(poFieldIn->GetNameRef()) >= 0)
    2965             :     {
    2966           0 :         CPLError(
    2967             :             CE_Failure, CPLE_AppDefined,
    2968             :             "CreateGeomField() called with an already existing field name: %s",
    2969             :             poFieldIn->GetNameRef());
    2970           0 :         return OGRERR_FAILURE;
    2971             :     }
    2972             : 
    2973          38 :     OGRGeomFieldDefn oFieldDefn(poFieldIn);
    2974          19 :     auto poSRSOri = poFieldIn->GetSpatialRef();
    2975          19 :     if (poSRSOri)
    2976             :     {
    2977          17 :         auto poSRS = poSRSOri->Clone();
    2978          17 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2979          17 :         oFieldDefn.SetSpatialRef(poSRS);
    2980          17 :         poSRS->Release();
    2981             :     }
    2982          19 :     if (EQUAL(oFieldDefn.GetNameRef(), ""))
    2983           0 :         oFieldDefn.SetName("geometry");
    2984             : 
    2985          19 :     std::vector<CPLString> aosPath;
    2986          19 :     if (m_bDotAsNestedField)
    2987             :     {
    2988             :         char **papszTokens =
    2989          19 :             CSLTokenizeString2(oFieldDefn.GetNameRef(), ".", 0);
    2990          39 :         for (int i = 0; papszTokens[i]; i++)
    2991          20 :             aosPath.push_back(papszTokens[i]);
    2992          19 :         CSLDestroy(papszTokens);
    2993             :     }
    2994             :     else
    2995           0 :         aosPath.push_back(oFieldDefn.GetNameRef());
    2996             : 
    2997          37 :     if (m_eGeomTypeMapping == ES_GEOMTYPE_GEO_SHAPE ||
    2998          35 :         (m_eGeomTypeMapping == ES_GEOMTYPE_AUTO &&
    2999          17 :          poFieldIn->GetType() != wkbPoint))
    3000             :     {
    3001          16 :         m_abIsGeoPoint.push_back(FALSE);
    3002             :     }
    3003             :     else
    3004             :     {
    3005           3 :         m_abIsGeoPoint.push_back(TRUE);
    3006           3 :         aosPath.push_back("coordinates");
    3007             :     }
    3008             : 
    3009          19 :     m_aaosGeomFieldPaths.push_back(aosPath);
    3010             : 
    3011          19 :     m_aosMapToGeomFieldIndex[BuildPathFromArray(aosPath)] =
    3012          19 :         m_poFeatureDefn->GetGeomFieldCount();
    3013             : 
    3014          19 :     m_poFeatureDefn->AddGeomFieldDefn(&oFieldDefn);
    3015             : 
    3016          19 :     OGRCoordinateTransformation *poCT = nullptr;
    3017          19 :     if (oFieldDefn.GetSpatialRef() != nullptr)
    3018             :     {
    3019          34 :         OGRSpatialReference oSRS_WGS84;
    3020          17 :         oSRS_WGS84.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
    3021          17 :         oSRS_WGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    3022          17 :         if (!oSRS_WGS84.IsSame(oFieldDefn.GetSpatialRef()))
    3023             :         {
    3024           1 :             poCT = OGRCreateCoordinateTransformation(oFieldDefn.GetSpatialRef(),
    3025             :                                                      &oSRS_WGS84);
    3026           1 :             if (poCT == nullptr)
    3027             :             {
    3028           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3029             :                          "On-the-fly reprojection to WGS84 long/lat would be "
    3030             :                          "needed, but instantiation of transformer failed");
    3031             :             }
    3032             :         }
    3033             :     }
    3034             :     else
    3035             :     {
    3036           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    3037             :                  "No SRS given for geometry column %s. SRS is assumed to "
    3038             :                  "be EPSG:4326 (WGS84 long/lat)",
    3039             :                  oFieldDefn.GetNameRef());
    3040             :     }
    3041             : 
    3042          19 :     m_apoCT.push_back(poCT);
    3043             : 
    3044          19 :     m_bSerializeMapping = true;
    3045             : 
    3046          19 :     return OGRERR_NONE;
    3047             : }
    3048             : 
    3049             : /************************************************************************/
    3050             : /*                           TestCapability()                           */
    3051             : /************************************************************************/
    3052             : 
    3053          67 : int OGRElasticLayer::TestCapability(const char *pszCap)
    3054             : {
    3055          67 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    3056           1 :         return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr;
    3057             : 
    3058          66 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    3059          27 :         return TRUE;
    3060             : 
    3061          39 :     else if (EQUAL(pszCap, OLCSequentialWrite) ||
    3062          38 :              EQUAL(pszCap, OLCUpsertFeature) || EQUAL(pszCap, OLCRandomWrite))
    3063           1 :         return m_poDS->GetAccess() == GA_Update;
    3064          38 :     else if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCCreateGeomField))
    3065           2 :         return m_poDS->GetAccess() == GA_Update;
    3066             :     else
    3067          36 :         return FALSE;
    3068             : }
    3069             : 
    3070             : /************************************************************************/
    3071             : /*                   AddTimeoutTerminateAfterToURL()                    */
    3072             : /************************************************************************/
    3073             : 
    3074          16 : void OGRElasticLayer::AddTimeoutTerminateAfterToURL(CPLString &osURL)
    3075             : {
    3076          16 :     if (!m_osSingleQueryTimeout.empty())
    3077           5 :         osURL += "&timeout=" + m_osSingleQueryTimeout;
    3078          16 :     if (!m_osSingleQueryTerminateAfter.empty())
    3079           5 :         osURL += "&terminate_after=" + m_osSingleQueryTerminateAfter;
    3080          16 : }
    3081             : 
    3082             : /************************************************************************/
    3083             : /*                          GetFeatureCount()                           */
    3084             : /************************************************************************/
    3085             : 
    3086          12 : GIntBig OGRElasticLayer::GetFeatureCount(int bForce)
    3087             : {
    3088          12 :     if (m_bFilterMustBeClientSideEvaluated)
    3089             :     {
    3090           0 :         m_bUseSingleQueryParams = true;
    3091           0 :         const auto nRet = OGRLayer::GetFeatureCount(bForce);
    3092           0 :         m_bUseSingleQueryParams = false;
    3093           0 :         return nRet;
    3094             :     }
    3095             : 
    3096          12 :     json_object *poResponse = nullptr;
    3097          24 :     CPLString osURL(CPLSPrintf("%s", m_poDS->GetURL()));
    3098          24 :     CPLString osFilter = "";
    3099          12 :     if (!m_osESSearch.empty())
    3100             :     {
    3101           1 :         if (m_osESSearch[0] != '{')
    3102           0 :             return OGRLayer::GetFeatureCount(bForce);
    3103           1 :         osURL += "/_search?pretty";
    3104           1 :         osFilter = "{ \"size\": 0 ";
    3105           1 :         if (m_osESSearch == "{}")
    3106           0 :             osFilter += '}';
    3107             :         else
    3108           1 :             osFilter += ", " + m_osESSearch.substr(1);
    3109             :     }
    3110          11 :     else if ((m_poSpatialFilter && m_osJSONFilter.empty()) || m_poJSONFilter)
    3111             :     {
    3112           3 :         osFilter = BuildQuery(true);
    3113           3 :         osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
    3114           3 :         if (m_poDS->m_nMajorVersion < 7)
    3115           2 :             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3116           3 :         if (m_poDS->m_nMajorVersion >= 5 && m_osSingleQueryTimeout.empty())
    3117             :         {
    3118           1 :             osURL += "/_count?pretty";
    3119             :         }
    3120             :         else
    3121             :         {
    3122           2 :             osURL += "/_search?pretty";
    3123             :         }
    3124             :     }
    3125           8 :     else if (!m_osJSONFilter.empty())
    3126             :     {
    3127           2 :         osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
    3128           2 :         if (m_poDS->m_nMajorVersion < 7)
    3129           1 :             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3130           2 :         osURL += "/_search?pretty";
    3131           2 :         osFilter = ("{ \"size\": 0, " + m_osJSONFilter.substr(1));
    3132             :     }
    3133             :     else
    3134             :     {
    3135           6 :         osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
    3136           6 :         if (m_poDS->m_nMajorVersion < 7)
    3137           5 :             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3138           6 :         if (m_osSingleQueryTimeout.empty())
    3139             :         {
    3140           5 :             osURL += "/_count?pretty";
    3141             :         }
    3142             :         else
    3143             :         {
    3144           1 :             osFilter = "{ \"size\": 0 }";
    3145           1 :             osURL += CPLSPrintf("/_search?pretty");
    3146             :         }
    3147             :     }
    3148          12 :     AddTimeoutTerminateAfterToURL(osURL);
    3149             : 
    3150          12 :     poResponse = m_poDS->RunRequest(osURL.c_str(), osFilter.c_str());
    3151             : 
    3152          12 :     json_object *poCount = json_ex_get_object_by_path(poResponse, "hits.count");
    3153          12 :     if (poCount == nullptr)
    3154             :     {
    3155             :         // For _search request
    3156          10 :         poCount = json_ex_get_object_by_path(poResponse, "hits.total");
    3157          10 :         if (poCount && json_object_get_type(poCount) == json_type_object)
    3158             :         {
    3159             :             // Since ES 7.0
    3160           4 :             poCount = json_ex_get_object_by_path(poCount, "value");
    3161             :         }
    3162             :     }
    3163          12 :     if (poCount == nullptr)
    3164             :     {
    3165             :         // For _count request
    3166           4 :         poCount = json_ex_get_object_by_path(poResponse, "count");
    3167             :     }
    3168          12 :     if (poCount == nullptr || json_object_get_type(poCount) != json_type_int)
    3169             :     {
    3170           3 :         json_object_put(poResponse);
    3171           3 :         CPLDebug("ES", "Cannot find hits in GetFeatureCount() response. "
    3172             :                        "Falling back to slow implementation");
    3173           3 :         m_bUseSingleQueryParams = true;
    3174           3 :         const auto nRet = OGRLayer::GetFeatureCount(bForce);
    3175           3 :         m_bUseSingleQueryParams = false;
    3176           3 :         return nRet;
    3177             :     }
    3178             : 
    3179           9 :     GIntBig nCount = json_object_get_int64(poCount);
    3180           9 :     json_object_put(poResponse);
    3181           9 :     return nCount;
    3182             : }
    3183             : 
    3184             : /************************************************************************/
    3185             : /*                            GetValue()                                */
    3186             : /************************************************************************/
    3187             : 
    3188          28 : json_object *OGRElasticLayer::GetValue(int nFieldIdx, swq_expr_node *poValNode)
    3189             : {
    3190          28 :     json_object *poVal = nullptr;
    3191          28 :     if (poValNode->field_type == SWQ_FLOAT)
    3192           1 :         poVal = json_object_new_double(poValNode->float_value);
    3193          27 :     else if (poValNode->field_type == SWQ_INTEGER ||
    3194          26 :              poValNode->field_type == SWQ_INTEGER64)
    3195           2 :         poVal = json_object_new_int64(poValNode->int_value);
    3196          25 :     else if (poValNode->field_type == SWQ_STRING)
    3197          23 :         poVal = json_object_new_string(poValNode->string_value);
    3198           2 :     else if (poValNode->field_type == SWQ_TIMESTAMP)
    3199             :     {
    3200           2 :         int nYear = 0;
    3201           2 :         int nMonth = 0;
    3202           2 :         int nDay = 0;
    3203           2 :         int nHour = 0;
    3204           2 :         int nMinute = 0;
    3205           2 :         float fSecond = 0;
    3206           4 :         if (sscanf(poValNode->string_value, "%04d/%02d/%02d %02d:%02d:%f",
    3207           2 :                    &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) >= 3 ||
    3208           0 :             sscanf(poValNode->string_value, "%04d-%02d-%02dT%02d:%02d:%f",
    3209             :                    &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) >= 3)
    3210             :         {
    3211             :             OGRFieldType eType(
    3212           2 :                 m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType());
    3213           2 :             if (eType == OFTDateTime)
    3214           2 :                 poVal = json_object_new_string(
    3215             :                     CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02.03f", nYear,
    3216             :                                nMonth, nDay, nHour, nMinute, fSecond));
    3217           0 :             else if (eType == OFTDate)
    3218           0 :                 poVal = json_object_new_string(
    3219             :                     CPLSPrintf("%04d/%02d/%02d", nYear, nMonth, nDay));
    3220             :             else
    3221           0 :                 poVal = json_object_new_string(
    3222             :                     CPLSPrintf("%02d:%02d:%02.03f", nHour, nMinute, fSecond));
    3223             :         }
    3224             :         else
    3225             :         {
    3226           0 :             return nullptr;
    3227             :         }
    3228             :     }
    3229             :     else
    3230             :     {
    3231           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unhandled type: %d",
    3232           0 :                  poValNode->field_type);
    3233             :     }
    3234          28 :     return poVal;
    3235             : }
    3236             : 
    3237             : /************************************************************************/
    3238             : /*                      OGRESGetFieldIndexFromSQL()                     */
    3239             : /************************************************************************/
    3240             : 
    3241          31 : static int OGRESGetFieldIndexFromSQL(const swq_expr_node *poNode)
    3242             : {
    3243          31 :     if (poNode->eNodeType == SNT_COLUMN)
    3244          27 :         return poNode->field_index;
    3245             : 
    3246           4 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_CAST &&
    3247           1 :         poNode->nSubExprCount >= 1 &&
    3248           1 :         poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN)
    3249           1 :         return poNode->papoSubExpr[0]->field_index;
    3250             : 
    3251           3 :     return -1;
    3252             : }
    3253             : 
    3254             : /************************************************************************/
    3255             : /*                        TranslateSQLToFilter()                        */
    3256             : /************************************************************************/
    3257             : 
    3258          40 : json_object *OGRElasticLayer::TranslateSQLToFilter(swq_expr_node *poNode)
    3259             : {
    3260          40 :     if (poNode->eNodeType == SNT_OPERATION)
    3261             :     {
    3262          40 :         int nFieldIdx = 0;
    3263          40 :         CPL_IGNORE_RET_VAL(nFieldIdx);  // to make cppcheck happy
    3264          40 :         if (poNode->nOperation == SWQ_AND && poNode->nSubExprCount == 2)
    3265             :         {
    3266             :             // For AND, we can deal with a failure in one of the branch
    3267             :             // since client-side will do that extra filtering
    3268             :             json_object *poFilter1 =
    3269           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[0]);
    3270             :             json_object *poFilter2 =
    3271           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[1]);
    3272           3 :             if (poFilter1 && poFilter2)
    3273             :             {
    3274           1 :                 json_object *poRet = json_object_new_object();
    3275           1 :                 json_object *poBool = json_object_new_object();
    3276           1 :                 json_object_object_add(poRet, "bool", poBool);
    3277           1 :                 json_object *poMust = json_object_new_array();
    3278           1 :                 json_object_object_add(poBool, "must", poMust);
    3279           1 :                 json_object_array_add(poMust, poFilter1);
    3280           1 :                 json_object_array_add(poMust, poFilter2);
    3281          35 :                 return poRet;
    3282             :             }
    3283           2 :             else if (poFilter1)
    3284           1 :                 return poFilter1;
    3285             :             else
    3286           1 :                 return poFilter2;
    3287             :         }
    3288          37 :         else if (poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
    3289             :         {
    3290             :             json_object *poFilter1 =
    3291           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[0]);
    3292             :             json_object *poFilter2 =
    3293           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[1]);
    3294           3 :             if (poFilter1 && poFilter2)
    3295             :             {
    3296           2 :                 json_object *poRet = json_object_new_object();
    3297           2 :                 json_object *poBool = json_object_new_object();
    3298           2 :                 json_object_object_add(poRet, "bool", poBool);
    3299           2 :                 json_object *poShould = json_object_new_array();
    3300           2 :                 json_object_object_add(poBool, "should", poShould);
    3301           2 :                 json_object_array_add(poShould, poFilter1);
    3302           2 :                 json_object_array_add(poShould, poFilter2);
    3303           2 :                 return poRet;
    3304             :             }
    3305             :             else
    3306             :             {
    3307           1 :                 json_object_put(poFilter1);
    3308           1 :                 json_object_put(poFilter2);
    3309           1 :                 return nullptr;
    3310             :             }
    3311             :         }
    3312          34 :         else if (poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
    3313             :         {
    3314           6 :             if (poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
    3315           2 :                 poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
    3316           1 :                 poNode->papoSubExpr[0]->nSubExprCount == 1 &&
    3317           5 :                 poNode->papoSubExpr[0]->papoSubExpr[0]->field_index != 0 &&
    3318           1 :                 poNode->papoSubExpr[0]->papoSubExpr[0]->field_index <
    3319           1 :                     m_poFeatureDefn->GetFieldCount())
    3320             :             {
    3321           1 :                 json_object *poRet = json_object_new_object();
    3322           1 :                 json_object *poExists = json_object_new_object();
    3323             :                 CPLString osFieldName(BuildPathFromArray(
    3324             :                     m_aaosFieldPaths
    3325           1 :                         [poNode->papoSubExpr[0]->papoSubExpr[0]->field_index]));
    3326           1 :                 json_object_object_add(poExists, "field",
    3327             :                                        json_object_new_string(osFieldName));
    3328           1 :                 json_object_object_add(poRet, "exists", poExists);
    3329           1 :                 return poRet;
    3330             :             }
    3331             :             else
    3332             :             {
    3333             :                 json_object *poFilter =
    3334           1 :                     TranslateSQLToFilter(poNode->papoSubExpr[0]);
    3335           1 :                 if (poFilter)
    3336             :                 {
    3337           1 :                     json_object *poRet = json_object_new_object();
    3338           1 :                     json_object *poBool = json_object_new_object();
    3339           1 :                     json_object_object_add(poRet, "bool", poBool);
    3340           1 :                     json_object_object_add(poBool, "must_not", poFilter);
    3341           1 :                     return poRet;
    3342             :                 }
    3343             :                 else
    3344             :                 {
    3345           0 :                     return nullptr;
    3346             :                 }
    3347             :             }
    3348             :         }
    3349          65 :         else if (poNode->nOperation == SWQ_ISNULL &&
    3350           1 :                  poNode->nSubExprCount == 1 &&
    3351           1 :                  (nFieldIdx =
    3352          34 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3353           1 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3354             :         {
    3355           1 :             json_object *poRet = json_object_new_object();
    3356           1 :             json_object *poExists = json_object_new_object();
    3357             :             CPLString osFieldName(
    3358           1 :                 BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3359           1 :             json_object_object_add(poExists, "field",
    3360             :                                    json_object_new_string(osFieldName));
    3361           1 :             json_object *poBool = json_object_new_object();
    3362           1 :             json_object_object_add(poRet, "bool", poBool);
    3363           1 :             json_object *poMustNot = json_object_new_object();
    3364           1 :             json_object_object_add(poMustNot, "exists", poExists);
    3365           1 :             json_object_object_add(poBool, "must_not", poMustNot);
    3366           1 :             return poRet;
    3367             :         }
    3368          31 :         else if (poNode->nOperation == SWQ_NE)
    3369             :         {
    3370           1 :             poNode->nOperation = SWQ_EQ;
    3371           1 :             json_object *poFilter = TranslateSQLToFilter(poNode);
    3372           1 :             poNode->nOperation = SWQ_NE;
    3373           1 :             if (poFilter)
    3374             :             {
    3375           1 :                 json_object *poRet = json_object_new_object();
    3376           1 :                 json_object *poBool = json_object_new_object();
    3377           1 :                 json_object_object_add(poRet, "bool", poBool);
    3378           1 :                 json_object_object_add(poBool, "must_not", poFilter);
    3379           1 :                 return poRet;
    3380             :             }
    3381             :             else
    3382             :             {
    3383           0 :                 return nullptr;
    3384             :             }
    3385             :         }
    3386          16 :         else if (poNode->nOperation == SWQ_EQ && poNode->nSubExprCount == 2 &&
    3387          16 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
    3388          16 :                  (nFieldIdx =
    3389          62 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) >= 0 &&
    3390          13 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3391             :         {
    3392          13 :             json_object *poVal = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
    3393          13 :             if (poVal == nullptr)
    3394             :             {
    3395           0 :                 return nullptr;
    3396             :             }
    3397          13 :             json_object *poRet = json_object_new_object();
    3398          13 :             if (nFieldIdx == 0)
    3399             :             {
    3400           1 :                 json_object *poIds = json_object_new_object();
    3401           1 :                 json_object *poValues = json_object_new_array();
    3402           1 :                 json_object_object_add(poIds, "values", poValues);
    3403           1 :                 json_object_array_add(poValues, poVal);
    3404           1 :                 json_object_object_add(poRet, "ids", poIds);
    3405             :             }
    3406             :             else
    3407             :             {
    3408          12 :                 json_object *poTerm = json_object_new_object();
    3409             :                 CPLString osPath(
    3410          24 :                     BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3411          12 :                 bool bNotAnalyzed = true;
    3412          12 :                 if (poNode->papoSubExpr[1]->field_type == SWQ_STRING)
    3413             :                 {
    3414             :                     const char *pszFieldName =
    3415          12 :                         m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
    3416          12 :                     bNotAnalyzed = CSLFindString(m_papszNotAnalyzedFields,
    3417             :                                                  pszFieldName) >= 0;
    3418          12 :                     if (!bNotAnalyzed)
    3419             :                     {
    3420           3 :                         if (CSLFindString(m_papszFieldsWithRawValue,
    3421           3 :                                           pszFieldName) >= 0)
    3422             :                         {
    3423           1 :                             osPath += ".raw";
    3424           1 :                             bNotAnalyzed = true;
    3425             :                         }
    3426           2 :                         else if (!m_bFilterMustBeClientSideEvaluated)
    3427             :                         {
    3428           2 :                             m_bFilterMustBeClientSideEvaluated = true;
    3429           2 :                             CPLDebug("ES",
    3430             :                                      "Part or full filter will have to be "
    3431             :                                      "evaluated on "
    3432             :                                      "client side (equality test on a analyzed "
    3433             :                                      "field).");
    3434             :                         }
    3435             :                     }
    3436             :                 }
    3437          12 :                 json_object_object_add(poRet, bNotAnalyzed ? "term" : "match",
    3438             :                                        poTerm);
    3439          12 :                 json_object_object_add(poTerm, osPath, poVal);
    3440             : 
    3441          12 :                 if (!bNotAnalyzed && m_poDS->m_nMajorVersion < 2)
    3442             :                 {
    3443           0 :                     json_object *poNewRet = json_object_new_object();
    3444           0 :                     json_object_object_add(poNewRet, "query", poRet);
    3445           0 :                     poRet = poNewRet;
    3446             :                 }
    3447             :             }
    3448          13 :             return poRet;
    3449             :         }
    3450          50 :         else if ((poNode->nOperation == SWQ_LT ||
    3451          16 :                   poNode->nOperation == SWQ_LE ||
    3452          15 :                   poNode->nOperation == SWQ_GT ||
    3453          14 :                   poNode->nOperation == SWQ_GE) &&
    3454           5 :                  poNode->nSubExprCount == 2 &&
    3455           5 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
    3456           5 :                  (nFieldIdx =
    3457          39 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3458           5 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3459             :         {
    3460           5 :             json_object *poVal = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
    3461           5 :             if (poVal == nullptr)
    3462             :             {
    3463           0 :                 return nullptr;
    3464             :             }
    3465           5 :             json_object *poRet = json_object_new_object();
    3466           5 :             json_object *poRange = json_object_new_object();
    3467           5 :             json_object_object_add(poRet, "range", poRange);
    3468           5 :             json_object *poFieldConstraint = json_object_new_object();
    3469             :             CPLString osFieldName(
    3470           5 :                 BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3471           5 :             json_object_object_add(poRange, osFieldName, poFieldConstraint);
    3472           9 :             const char *pszOp = (poNode->nOperation == SWQ_LT)   ? "lt"
    3473           7 :                                 : (poNode->nOperation == SWQ_LE) ? "lte"
    3474           3 :                                 : (poNode->nOperation == SWQ_GT)
    3475           3 :                                     ? "gt"
    3476             :                                     :
    3477             :                                     /*(poNode->nOperation == SWQ_GE) ?*/ "gte";
    3478           5 :             json_object_object_add(poFieldConstraint, pszOp, poVal);
    3479           5 :             return poRet;
    3480             :         }
    3481          25 :         else if (poNode->nOperation == SWQ_BETWEEN &&
    3482           1 :                  poNode->nSubExprCount == 3 &&
    3483           1 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
    3484           1 :                  poNode->papoSubExpr[2]->eNodeType == SNT_CONSTANT &&
    3485           1 :                  (nFieldIdx =
    3486          14 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3487           1 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3488             :         {
    3489           1 :             json_object *poVal1 = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
    3490           1 :             if (poVal1 == nullptr)
    3491             :             {
    3492           0 :                 return nullptr;
    3493             :             }
    3494           1 :             json_object *poVal2 = GetValue(nFieldIdx, poNode->papoSubExpr[2]);
    3495           1 :             if (poVal2 == nullptr)
    3496             :             {
    3497           0 :                 json_object_put(poVal1);
    3498           0 :                 return nullptr;
    3499             :             }
    3500             : 
    3501           1 :             json_object *poRet = json_object_new_object();
    3502           1 :             json_object *poRange = json_object_new_object();
    3503           1 :             json_object_object_add(poRet, "range", poRange);
    3504           1 :             json_object *poFieldConstraint = json_object_new_object();
    3505             :             CPLString osFieldName(
    3506           1 :                 BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3507           1 :             json_object_object_add(poRange, osFieldName, poFieldConstraint);
    3508           1 :             json_object_object_add(poFieldConstraint, "gte", poVal1);
    3509           1 :             json_object_object_add(poFieldConstraint, "lte", poVal2);
    3510           1 :             return poRet;
    3511             :         }
    3512           4 :         else if (poNode->nOperation == SWQ_IN && poNode->nSubExprCount > 1 &&
    3513           4 :                  (nFieldIdx =
    3514          19 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) >= 0 &&
    3515           4 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3516             :         {
    3517           4 :             bool bAllConstant = true;
    3518          12 :             for (int i = 1; i < poNode->nSubExprCount; i++)
    3519             :             {
    3520           8 :                 if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT)
    3521             :                 {
    3522           0 :                     bAllConstant = false;
    3523           0 :                     break;
    3524             :                 }
    3525             :             }
    3526           4 :             if (bAllConstant)
    3527             :             {
    3528           4 :                 json_object *poRet = json_object_new_object();
    3529           4 :                 if (nFieldIdx == 0)
    3530             :                 {
    3531           1 :                     json_object *poIds = json_object_new_object();
    3532           1 :                     json_object *poValues = json_object_new_array();
    3533           1 :                     json_object_object_add(poIds, "values", poValues);
    3534           1 :                     json_object_object_add(poRet, "ids", poIds);
    3535           3 :                     for (int i = 1; i < poNode->nSubExprCount; i++)
    3536             :                     {
    3537             :                         json_object *poVal =
    3538           2 :                             GetValue(nFieldIdx, poNode->papoSubExpr[i]);
    3539           2 :                         if (poVal == nullptr)
    3540             :                         {
    3541           0 :                             json_object_put(poRet);
    3542           0 :                             return nullptr;
    3543             :                         }
    3544           2 :                         json_object_array_add(poValues, poVal);
    3545             :                     }
    3546             :                 }
    3547             :                 else
    3548             :                 {
    3549           3 :                     bool bNotAnalyzed = true;
    3550             :                     CPLString osPath(
    3551           3 :                         BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3552           3 :                     if (poNode->papoSubExpr[1]->field_type == SWQ_STRING)
    3553             :                     {
    3554             :                         const char *pszFieldName =
    3555           3 :                             m_poFeatureDefn->GetFieldDefn(nFieldIdx)
    3556           3 :                                 ->GetNameRef();
    3557           3 :                         bNotAnalyzed = CSLFindString(m_papszNotAnalyzedFields,
    3558             :                                                      pszFieldName) >= 0;
    3559           5 :                         if (!bNotAnalyzed &&
    3560           2 :                             CSLFindString(m_papszFieldsWithRawValue,
    3561             :                                           pszFieldName) >= 0)
    3562             :                         {
    3563           1 :                             osPath += ".raw";
    3564           1 :                             bNotAnalyzed = true;
    3565             :                         }
    3566             : 
    3567           3 :                         if (!bNotAnalyzed &&
    3568           1 :                             !m_bFilterMustBeClientSideEvaluated)
    3569             :                         {
    3570           1 :                             m_bFilterMustBeClientSideEvaluated = true;
    3571           1 :                             CPLDebug("ES",
    3572             :                                      "Part or full filter will have to be "
    3573             :                                      "evaluated on client side (IN test on a "
    3574             :                                      "analyzed field).");
    3575             :                         }
    3576             :                     }
    3577             : 
    3578           3 :                     if (bNotAnalyzed)
    3579             :                     {
    3580           2 :                         json_object *poTerms = json_object_new_object();
    3581           2 :                         json_object_object_add(poRet, "terms", poTerms);
    3582           2 :                         json_object *poTermsValues = json_object_new_array();
    3583           2 :                         json_object_object_add(poTerms, osPath, poTermsValues);
    3584           6 :                         for (int i = 1; i < poNode->nSubExprCount; i++)
    3585             :                         {
    3586             :                             json_object *poVal =
    3587           4 :                                 GetValue(nFieldIdx, poNode->papoSubExpr[i]);
    3588           4 :                             if (poVal == nullptr)
    3589             :                             {
    3590           0 :                                 json_object_put(poRet);
    3591           0 :                                 return nullptr;
    3592             :                             }
    3593           4 :                             json_object_array_add(poTermsValues, poVal);
    3594             :                         }
    3595             :                     }
    3596             :                     else
    3597             :                     {
    3598           1 :                         json_object *poBool = json_object_new_object();
    3599           1 :                         json_object_object_add(poRet, "bool", poBool);
    3600           1 :                         json_object *poShould = json_object_new_array();
    3601           1 :                         json_object_object_add(poBool, "should", poShould);
    3602           3 :                         for (int i = 1; i < poNode->nSubExprCount; i++)
    3603             :                         {
    3604             :                             json_object *poVal =
    3605           2 :                                 GetValue(nFieldIdx, poNode->papoSubExpr[i]);
    3606           2 :                             if (poVal == nullptr)
    3607             :                             {
    3608           0 :                                 json_object_put(poRet);
    3609           0 :                                 return nullptr;
    3610             :                             }
    3611           2 :                             json_object *poShouldElt = json_object_new_object();
    3612           2 :                             json_object *poMatch = json_object_new_object();
    3613           2 :                             json_object_object_add(poShouldElt, "match",
    3614             :                                                    poMatch);
    3615           2 :                             json_object_object_add(poMatch, osPath, poVal);
    3616             : 
    3617           2 :                             if (m_poDS->m_nMajorVersion < 2)
    3618             :                             {
    3619             :                                 json_object *poNewShouldElt =
    3620           0 :                                     json_object_new_object();
    3621           0 :                                 json_object_object_add(poNewShouldElt, "query",
    3622             :                                                        poShouldElt);
    3623           0 :                                 poShouldElt = poNewShouldElt;
    3624             :                             }
    3625           2 :                             json_object_array_add(poShould, poShouldElt);
    3626             :                         }
    3627             :                     }
    3628             :                 }
    3629           4 :                 return poRet;
    3630             :             }
    3631             :         }
    3632          17 :         else if ((poNode->nOperation == SWQ_LIKE ||
    3633           3 :                   poNode->nOperation ==
    3634           4 :                       SWQ_ILIKE) &&  // ES actual semantics doesn't match
    3635             :                                      // exactly either...
    3636           4 :                  poNode->nSubExprCount >= 2 &&
    3637           4 :                  (nFieldIdx =
    3638          18 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3639           4 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3640             :         {
    3641           4 :             char chEscape = '\0';
    3642           4 :             if (poNode->nSubExprCount == 3)
    3643           1 :                 chEscape = poNode->papoSubExpr[2]->string_value[0];
    3644           4 :             const char *pszPattern = poNode->papoSubExpr[1]->string_value;
    3645             :             const char *pszFieldName =
    3646           4 :                 m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
    3647             :             bool bNotAnalyzed =
    3648           4 :                 CSLFindString(m_papszNotAnalyzedFields, pszFieldName) >= 0;
    3649           4 :             CPLString osPath(BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3650           7 :             if (!bNotAnalyzed &&
    3651           3 :                 CSLFindString(m_papszFieldsWithRawValue, pszFieldName) >= 0)
    3652             :             {
    3653           1 :                 osPath += ".raw";
    3654           1 :                 bNotAnalyzed = true;
    3655             :             }
    3656             : 
    3657           4 :             if (strchr(pszPattern, '*') || strchr(pszPattern, '?'))
    3658             :             {
    3659           1 :                 CPLDebug("ES", "Cannot handle * or ? in LIKE pattern");
    3660             :             }
    3661           3 :             else if (!bNotAnalyzed)
    3662             :             {
    3663           1 :                 if (!m_bFilterMustBeClientSideEvaluated)
    3664             :                 {
    3665           1 :                     m_bFilterMustBeClientSideEvaluated = true;
    3666           1 :                     CPLDebug(
    3667             :                         "ES",
    3668             :                         "Part or full filter will have to be evaluated on "
    3669             :                         "client side (wildcard test on a analyzed field).");
    3670             :                 }
    3671             :             }
    3672             :             else
    3673             :             {
    3674           2 :                 CPLString osUnescaped;
    3675           8 :                 for (int i = 0; pszPattern[i] != '\0'; ++i)
    3676             :                 {
    3677           6 :                     if (chEscape == pszPattern[i])
    3678             :                     {
    3679           1 :                         if (pszPattern[i + 1] == '\0')
    3680           0 :                             break;
    3681           1 :                         osUnescaped += pszPattern[i + 1];
    3682           1 :                         i++;
    3683             :                     }
    3684           5 :                     else if (pszPattern[i] == '%')
    3685             :                     {
    3686           2 :                         osUnescaped += '*';
    3687             :                     }
    3688           3 :                     else if (pszPattern[i] == '_')
    3689             :                     {
    3690           2 :                         osUnescaped += '?';
    3691             :                     }
    3692             :                     else
    3693             :                     {
    3694           1 :                         osUnescaped += pszPattern[i];
    3695             :                     }
    3696             :                 }
    3697           2 :                 json_object *poRet = json_object_new_object();
    3698           2 :                 json_object *poWildcard = json_object_new_object();
    3699           2 :                 json_object_object_add(poRet, "wildcard", poWildcard);
    3700           2 :                 json_object_object_add(poWildcard, osPath,
    3701             :                                        json_object_new_string(osUnescaped));
    3702           2 :                 return poRet;
    3703             :             }
    3704             :         }
    3705             :     }
    3706             : 
    3707           5 :     if (!m_bFilterMustBeClientSideEvaluated)
    3708             :     {
    3709           4 :         m_bFilterMustBeClientSideEvaluated = true;
    3710           4 :         CPLDebug("ES", "Part or full filter will have to be evaluated on "
    3711             :                        "client side.");
    3712             :     }
    3713           5 :     return nullptr;
    3714             : }
    3715             : 
    3716             : /************************************************************************/
    3717             : /*                          SetAttributeFilter()                        */
    3718             : /************************************************************************/
    3719             : 
    3720          37 : OGRErr OGRElasticLayer::SetAttributeFilter(const char *pszFilter)
    3721             : {
    3722          37 :     m_bFilterMustBeClientSideEvaluated = false;
    3723          37 :     if (pszFilter != nullptr && pszFilter[0] == '{')
    3724             :     {
    3725           2 :         if (!m_osESSearch.empty())
    3726             :         {
    3727           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3728             :                      "Setting an Elasticsearch filter on a resulting layer "
    3729             :                      "is not supported");
    3730           0 :             return OGRERR_FAILURE;
    3731             :         }
    3732           2 :         OGRLayer::SetAttributeFilter(nullptr);
    3733           2 :         m_osJSONFilter = pszFilter;
    3734           2 :         return OGRERR_NONE;
    3735             :     }
    3736             :     else
    3737             :     {
    3738          35 :         m_osJSONFilter.clear();
    3739          35 :         json_object_put(m_poJSONFilter);
    3740          35 :         m_poJSONFilter = nullptr;
    3741          35 :         OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
    3742          35 :         if (eErr == OGRERR_NONE && m_poAttrQuery != nullptr)
    3743             :         {
    3744             :             swq_expr_node *poNode =
    3745          26 :                 reinterpret_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
    3746          26 :             m_poJSONFilter = TranslateSQLToFilter(poNode);
    3747             :         }
    3748          35 :         return eErr;
    3749             :     }
    3750             : }
    3751             : 
    3752             : /************************************************************************/
    3753             : /*                          ClampEnvelope()                             */
    3754             : /************************************************************************/
    3755             : 
    3756           7 : void OGRElasticLayer::ClampEnvelope(OGREnvelope &sEnvelope)
    3757             : {
    3758           7 :     if (sEnvelope.MinX < -180)
    3759           1 :         sEnvelope.MinX = -180;
    3760           7 :     if (sEnvelope.MinX > 180)
    3761           0 :         sEnvelope.MinX = 180;
    3762             : 
    3763           7 :     if (sEnvelope.MinY < -90)
    3764           1 :         sEnvelope.MinY = -90;
    3765           7 :     if (sEnvelope.MinY > 90)
    3766           0 :         sEnvelope.MinY = 90;
    3767             : 
    3768           7 :     if (sEnvelope.MaxX > 180)
    3769           1 :         sEnvelope.MaxX = 180;
    3770           7 :     if (sEnvelope.MaxX < -180)
    3771           0 :         sEnvelope.MaxX = -180;
    3772             : 
    3773           7 :     if (sEnvelope.MaxY > 90)
    3774           1 :         sEnvelope.MaxY = 90;
    3775           7 :     if (sEnvelope.MaxY < -90)
    3776           0 :         sEnvelope.MaxY = -90;
    3777           7 : }
    3778             : 
    3779             : /************************************************************************/
    3780             : /*                          SetSpatialFilter()                          */
    3781             : /************************************************************************/
    3782             : 
    3783          16 : void OGRElasticLayer::SetSpatialFilter(int iGeomField, OGRGeometry *poGeomIn)
    3784             : 
    3785             : {
    3786          16 :     FinalizeFeatureDefn();
    3787             : 
    3788          30 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
    3789          14 :         GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone)
    3790             :     {
    3791           2 :         if (iGeomField != 0)
    3792             :         {
    3793           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    3794             :                      "Invalid geometry field index : %d", iGeomField);
    3795             :         }
    3796          12 :         return;
    3797             :     }
    3798          14 :     m_iGeomFieldFilter = iGeomField;
    3799             : 
    3800          14 :     InstallFilter(poGeomIn);
    3801             : 
    3802          14 :     json_object_put(m_poSpatialFilter);
    3803          14 :     m_poSpatialFilter = nullptr;
    3804             : 
    3805          14 :     if (poGeomIn == nullptr)
    3806           9 :         return;
    3807             : 
    3808           5 :     if (!m_osESSearch.empty())
    3809             :     {
    3810           0 :         CPLError(
    3811             :             CE_Failure, CPLE_AppDefined,
    3812             :             "Setting a spatial filter on a resulting layer is not supported");
    3813           0 :         return;
    3814             :     }
    3815             : 
    3816           5 :     OGREnvelope sEnvelope;
    3817           5 :     poGeomIn->getEnvelope(&sEnvelope);
    3818           5 :     ClampEnvelope(sEnvelope);
    3819             : 
    3820           5 :     if (sEnvelope.MinX == -180 && sEnvelope.MinY == -90 &&
    3821           1 :         sEnvelope.MaxX == 180 && sEnvelope.MaxY == 90)
    3822             :     {
    3823           1 :         return;
    3824             :     }
    3825             : 
    3826           4 :     m_poSpatialFilter = json_object_new_object();
    3827             : 
    3828           4 :     if (m_abIsGeoPoint[iGeomField])
    3829             :     {
    3830           1 :         json_object *geo_bounding_box = json_object_new_object();
    3831           1 :         json_object_object_add(m_poSpatialFilter, "geo_bounding_box",
    3832             :                                geo_bounding_box);
    3833             : 
    3834           2 :         CPLString osPath = BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]);
    3835             : 
    3836           1 :         json_object *field = json_object_new_object();
    3837           1 :         json_object_object_add(geo_bounding_box, osPath.c_str(), field);
    3838             : 
    3839           1 :         json_object *top_left = json_object_new_object();
    3840           1 :         json_object_object_add(field, "top_left", top_left);
    3841           1 :         json_object_object_add(
    3842             :             top_left, "lat",
    3843             :             json_object_new_double_with_precision(sEnvelope.MaxY, 6));
    3844           1 :         json_object_object_add(
    3845             :             top_left, "lon",
    3846             :             json_object_new_double_with_precision(sEnvelope.MinX, 6));
    3847             : 
    3848           1 :         json_object *bottom_right = json_object_new_object();
    3849           1 :         json_object_object_add(field, "bottom_right", bottom_right);
    3850           1 :         json_object_object_add(
    3851             :             bottom_right, "lat",
    3852             :             json_object_new_double_with_precision(sEnvelope.MinY, 6));
    3853           1 :         json_object_object_add(
    3854             :             bottom_right, "lon",
    3855             :             json_object_new_double_with_precision(sEnvelope.MaxX, 6));
    3856             :     }
    3857             :     else
    3858             :     {
    3859           3 :         json_object *geo_shape = json_object_new_object();
    3860           3 :         json_object_object_add(m_poSpatialFilter, "geo_shape", geo_shape);
    3861             : 
    3862           6 :         CPLString osPath = BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]);
    3863             : 
    3864           3 :         json_object *field = json_object_new_object();
    3865           3 :         json_object_object_add(geo_shape, osPath.c_str(), field);
    3866             : 
    3867           3 :         json_object *shape = json_object_new_object();
    3868           3 :         json_object_object_add(field, "shape", shape);
    3869             : 
    3870           3 :         json_object_object_add(shape, "type",
    3871             :                                json_object_new_string("envelope"));
    3872             : 
    3873           3 :         json_object *coordinates = json_object_new_array();
    3874           3 :         json_object_object_add(shape, "coordinates", coordinates);
    3875             : 
    3876           3 :         json_object *top_left = json_object_new_array();
    3877           3 :         json_object_array_add(
    3878             :             top_left, json_object_new_double_with_precision(sEnvelope.MinX, 6));
    3879           3 :         json_object_array_add(
    3880             :             top_left, json_object_new_double_with_precision(sEnvelope.MaxY, 6));
    3881           3 :         json_object_array_add(coordinates, top_left);
    3882             : 
    3883           3 :         json_object *bottom_right = json_object_new_array();
    3884           3 :         json_object_array_add(
    3885             :             bottom_right,
    3886             :             json_object_new_double_with_precision(sEnvelope.MaxX, 6));
    3887           3 :         json_object_array_add(
    3888             :             bottom_right,
    3889             :             json_object_new_double_with_precision(sEnvelope.MinY, 6));
    3890           3 :         json_object_array_add(coordinates, bottom_right);
    3891             :     }
    3892             : }
    3893             : 
    3894             : /************************************************************************/
    3895             : /*                            GetExtent()                                */
    3896             : /************************************************************************/
    3897             : 
    3898           7 : OGRErr OGRElasticLayer::GetExtent(int iGeomField, OGREnvelope *psExtent,
    3899             :                                   int bForce)
    3900             : {
    3901           7 :     FinalizeFeatureDefn();
    3902             : 
    3903           7 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount())
    3904             :     {
    3905           2 :         if (iGeomField != 0)
    3906             :         {
    3907           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    3908             :                      "Invalid geometry field index : %d", iGeomField);
    3909             :         }
    3910           2 :         return OGRERR_FAILURE;
    3911             :     }
    3912             : 
    3913             :     // geo_shape aggregation is only available since ES 7.8, but only with XPack
    3914             :     // for now
    3915           6 :     if (!m_abIsGeoPoint[iGeomField] &&
    3916           1 :         !(m_poDS->m_nMajorVersion > 7 ||
    3917           1 :           (m_poDS->m_nMajorVersion == 7 && m_poDS->m_nMinorVersion >= 8)))
    3918             :     {
    3919           1 :         m_bUseSingleQueryParams = true;
    3920             :         const auto eRet =
    3921           1 :             OGRLayer::GetExtentInternal(iGeomField, psExtent, bForce);
    3922           1 :         m_bUseSingleQueryParams = false;
    3923           1 :         return eRet;
    3924             :     }
    3925             : 
    3926             :     CPLString osFilter = CPLSPrintf(
    3927             :         "{ \"size\": 0, \"aggs\" : { \"bbox\" : { \"geo_bounds\" : { \"field\" "
    3928             :         ": \"%s\" } } } }",
    3929           8 :         BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]).c_str());
    3930             :     CPLString osURL =
    3931           8 :         CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str());
    3932           4 :     if (m_poDS->m_nMajorVersion < 7)
    3933           3 :         osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3934           4 :     osURL += "/_search?pretty";
    3935           4 :     AddTimeoutTerminateAfterToURL(osURL);
    3936             : 
    3937           4 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    3938             :     json_object *poResponse =
    3939           4 :         m_poDS->RunRequest(osURL.c_str(), osFilter.c_str());
    3940           4 :     CPLPopErrorHandler();
    3941           4 :     if (poResponse == nullptr)
    3942             :     {
    3943           2 :         const char *pszLastErrorMsg = CPLGetLastErrorMsg();
    3944           2 :         if (!m_abIsGeoPoint[iGeomField] &&
    3945           0 :             strstr(pszLastErrorMsg, "Fielddata is not supported on field") !=
    3946             :                 nullptr)
    3947             :         {
    3948           0 :             CPLDebug("ES",
    3949             :                      "geo_bounds aggregation failed, likely because of lack "
    3950             :                      "of XPack. Using client-side method");
    3951           0 :             CPLErrorReset();
    3952             :         }
    3953             :         else
    3954             :         {
    3955           2 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", pszLastErrorMsg);
    3956             :         }
    3957             :     }
    3958             : 
    3959             :     json_object *poBounds =
    3960           4 :         json_ex_get_object_by_path(poResponse, "aggregations.bbox.bounds");
    3961           4 :     json_object *poTopLeft = json_ex_get_object_by_path(poBounds, "top_left");
    3962             :     json_object *poBottomRight =
    3963           4 :         json_ex_get_object_by_path(poBounds, "bottom_right");
    3964           4 :     json_object *poTopLeftLon = json_ex_get_object_by_path(poTopLeft, "lon");
    3965           4 :     json_object *poTopLeftLat = json_ex_get_object_by_path(poTopLeft, "lat");
    3966             :     json_object *poBottomRightLon =
    3967           4 :         json_ex_get_object_by_path(poBottomRight, "lon");
    3968             :     json_object *poBottomRightLat =
    3969           4 :         json_ex_get_object_by_path(poBottomRight, "lat");
    3970             : 
    3971             :     OGRErr eErr;
    3972           4 :     if (poTopLeftLon == nullptr || poTopLeftLat == nullptr ||
    3973           2 :         poBottomRightLon == nullptr || poBottomRightLat == nullptr)
    3974             :     {
    3975           2 :         m_bUseSingleQueryParams = true;
    3976             :         const auto eRet =
    3977           2 :             OGRLayer::GetExtentInternal(iGeomField, psExtent, bForce);
    3978           2 :         m_bUseSingleQueryParams = false;
    3979           2 :         return eRet;
    3980             :     }
    3981             :     else
    3982             :     {
    3983           2 :         double dfMinX = json_object_get_double(poTopLeftLon);
    3984           2 :         double dfMaxY = json_object_get_double(poTopLeftLat);
    3985           2 :         double dfMaxX = json_object_get_double(poBottomRightLon);
    3986           2 :         double dfMinY = json_object_get_double(poBottomRightLat);
    3987             : 
    3988           2 :         psExtent->MinX = dfMinX;
    3989           2 :         psExtent->MaxY = dfMaxY;
    3990           2 :         psExtent->MaxX = dfMaxX;
    3991           2 :         psExtent->MinY = dfMinY;
    3992             : 
    3993           2 :         eErr = OGRERR_NONE;
    3994             :     }
    3995           2 :     json_object_put(poResponse);
    3996             : 
    3997           2 :     return eErr;
    3998             : }
    3999             : 
    4000             : /************************************************************************/
    4001             : /*                             GetDataset()                             */
    4002             : /************************************************************************/
    4003             : 
    4004           1 : GDALDataset *OGRElasticLayer::GetDataset()
    4005             : {
    4006           1 :     return m_poDS;
    4007             : }

Generated by: LCOV version 1.14