LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/elastic - ogrelasticlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1990 2119 93.9 %
Date: 2025-01-18 12:42:00 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           1 :                     CPLErrorOnce(
    2409             :                         CE_Warning, CPLE_AppDefined,
    2410             :                         "At least one geometry has a bounding box outside "
    2411             :                         "of [-180,180] longitude range and/or [-90,90] "
    2412             :                         "latitude range. Undefined behavior");
    2413             :                 }
    2414             : 
    2415          26 :                 std::vector<CPLString> aosPath = m_aaosGeomFieldPaths[i];
    2416          13 :                 bool bAddGeoJSONType = false;
    2417          17 :                 if (m_abIsGeoPoint[i] && aosPath.size() >= 2 &&
    2418           4 :                     aosPath.back() == "coordinates")
    2419             :                 {
    2420           4 :                     bAddGeoJSONType = true;
    2421           4 :                     aosPath.resize((int)aosPath.size() - 1);
    2422             :                 }
    2423             : 
    2424             :                 json_object *poContainer =
    2425          13 :                     GetContainerForFeature(fieldObject, aosPath, oMap);
    2426          13 :                 const char *pszLastComponent = aosPath.back();
    2427             : 
    2428          13 :                 if (m_abIsGeoPoint[i])
    2429             :                 {
    2430           5 :                     json_object *coordinates = json_object_new_array();
    2431           5 :                     const int nPrecision = 10;
    2432           5 :                     json_object_array_add(
    2433             :                         coordinates,
    2434             :                         json_object_new_double_with_precision(
    2435           5 :                             (env.MaxX + env.MinX) * 0.5, nPrecision));
    2436           5 :                     json_object_array_add(
    2437             :                         coordinates,
    2438             :                         json_object_new_double_with_precision(
    2439           5 :                             (env.MaxY + env.MinY) * 0.5, nPrecision));
    2440             : 
    2441           5 :                     if (bAddGeoJSONType)
    2442             :                     {
    2443           4 :                         json_object *geometry = json_object_new_object();
    2444           4 :                         json_object_object_add(poContainer, pszLastComponent,
    2445             :                                                geometry);
    2446           4 :                         json_object_object_add(geometry, "type",
    2447             :                                                json_object_new_string("Point"));
    2448           4 :                         json_object_object_add(geometry, "coordinates",
    2449             :                                                coordinates);
    2450             :                     }
    2451             :                     else
    2452             :                     {
    2453           1 :                         json_object_object_add(poContainer, pszLastComponent,
    2454             :                                                coordinates);
    2455             :                     }
    2456             :                 }
    2457             :                 else
    2458             :                 {
    2459           8 :                     if (m_bGeoShapeAsGeoJSON)
    2460             :                     {
    2461           7 :                         json_object *geometry = json_object_new_object();
    2462           7 :                         json_object_object_add(poContainer, pszLastComponent,
    2463             :                                                geometry);
    2464           7 :                         BuildGeoJSONGeometry(geometry, poGeom);
    2465             :                     }
    2466             :                     else
    2467             :                     {
    2468           1 :                         char *pszWKT = nullptr;
    2469           1 :                         poGeom->exportToWkt(&pszWKT);
    2470           1 :                         json_object_object_add(poContainer, pszLastComponent,
    2471             :                                                json_object_new_string(pszWKT));
    2472           1 :                         CPLFree(pszWKT);
    2473             :                     }
    2474             :                 }
    2475             :             }
    2476             :         }
    2477             : 
    2478          23 :         if (m_osMappingName == "FeatureCollection")
    2479             :         {
    2480          37 :             if (poFeature->GetGeomFieldCount() == 1 &&
    2481          17 :                 poFeature->GetGeomFieldRef(0))
    2482             :             {
    2483           7 :                 json_object_object_add(fieldObject, "type",
    2484             :                                        json_object_new_string("Feature"));
    2485             :             }
    2486             : 
    2487          40 :             std::vector<CPLString> aosPath;
    2488          20 :             aosPath.push_back("properties");
    2489          20 :             aosPath.push_back("dummy");
    2490          20 :             GetContainerForFeature(fieldObject, aosPath, oMap);
    2491             :         }
    2492             : 
    2493             :         // For every field (except _id)
    2494          23 :         int fieldCount = m_poFeatureDefn->GetFieldCount();
    2495         115 :         for (int i = 1; i < fieldCount; i++)
    2496             :         {
    2497          92 :             if (!poFeature->IsFieldSet(i))
    2498             :             {
    2499          59 :                 continue;
    2500             :             }
    2501             : 
    2502             :             json_object *poContainer =
    2503          33 :                 GetContainerForFeature(fieldObject, m_aaosFieldPaths[i], oMap);
    2504          33 :             const char *pszLastComponent = m_aaosFieldPaths[i].back();
    2505             : 
    2506          33 :             if (poFeature->IsFieldNull(i))
    2507             :             {
    2508           1 :                 json_object_object_add(poContainer, pszLastComponent, nullptr);
    2509           1 :                 continue;
    2510             :             }
    2511             : 
    2512          32 :             switch (m_poFeatureDefn->GetFieldDefn(i)->GetType())
    2513             :             {
    2514           5 :                 case OFTInteger:
    2515           5 :                     if (m_poFeatureDefn->GetFieldDefn(i)->GetSubType() ==
    2516             :                         OFSTBoolean)
    2517           2 :                         json_object_object_add(
    2518             :                             poContainer, pszLastComponent,
    2519             :                             json_object_new_boolean(
    2520             :                                 poFeature->GetFieldAsInteger(i)));
    2521             :                     else
    2522           3 :                         json_object_object_add(
    2523             :                             poContainer, pszLastComponent,
    2524             :                             json_object_new_int(
    2525             :                                 poFeature->GetFieldAsInteger(i)));
    2526           5 :                     break;
    2527           2 :                 case OFTInteger64:
    2528           2 :                     json_object_object_add(
    2529             :                         poContainer, pszLastComponent,
    2530             :                         json_object_new_int64(
    2531           2 :                             poFeature->GetFieldAsInteger64(i)));
    2532           2 :                     break;
    2533           2 :                 case OFTReal:
    2534           2 :                     json_object_object_add(
    2535             :                         poContainer, pszLastComponent,
    2536             :                         json_object_new_double_with_significant_figures(
    2537             :                             poFeature->GetFieldAsDouble(i), -1));
    2538           2 :                     break;
    2539           2 :                 case OFTIntegerList:
    2540             :                 {
    2541           2 :                     int nCount = 0;
    2542             :                     const int *panValues =
    2543           2 :                         poFeature->GetFieldAsIntegerList(i, &nCount);
    2544           2 :                     json_object *poArray = json_object_new_array();
    2545           6 :                     for (int j = 0; j < nCount; j++)
    2546           4 :                         json_object_array_add(
    2547           4 :                             poArray, json_object_new_int(panValues[j]));
    2548           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2549             :                                            poArray);
    2550           2 :                     break;
    2551             :                 }
    2552           2 :                 case OFTInteger64List:
    2553             :                 {
    2554           2 :                     int nCount = 0;
    2555             :                     const GIntBig *panValues =
    2556           2 :                         poFeature->GetFieldAsInteger64List(i, &nCount);
    2557           2 :                     json_object *poArray = json_object_new_array();
    2558           6 :                     for (int j = 0; j < nCount; j++)
    2559           4 :                         json_object_array_add(
    2560           4 :                             poArray, json_object_new_int64(panValues[j]));
    2561           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2562             :                                            poArray);
    2563           2 :                     break;
    2564             :                 }
    2565           2 :                 case OFTRealList:
    2566             :                 {
    2567           2 :                     int nCount = 0;
    2568             :                     const double *padfValues =
    2569           2 :                         poFeature->GetFieldAsDoubleList(i, &nCount);
    2570           2 :                     json_object *poArray = json_object_new_array();
    2571           6 :                     for (int j = 0; j < nCount; j++)
    2572           4 :                         json_object_array_add(
    2573             :                             poArray,
    2574             :                             json_object_new_double_with_significant_figures(
    2575           4 :                                 padfValues[j], -1));
    2576           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2577             :                                            poArray);
    2578           2 :                     break;
    2579             :                 }
    2580           2 :                 case OFTStringList:
    2581             :                 {
    2582           2 :                     char **papszValues = poFeature->GetFieldAsStringList(i);
    2583           2 :                     json_object *poArray = json_object_new_array();
    2584           6 :                     for (int j = 0; papszValues[j] != nullptr; j++)
    2585           4 :                         json_object_array_add(
    2586           4 :                             poArray, json_object_new_string(papszValues[j]));
    2587           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2588             :                                            poArray);
    2589           2 :                     break;
    2590             :                 }
    2591           2 :                 case OFTBinary:
    2592             :                 {
    2593           2 :                     int nCount = 0;
    2594           2 :                     GByte *pabyVal = poFeature->GetFieldAsBinary(i, &nCount);
    2595           2 :                     char *pszVal = CPLBase64Encode(nCount, pabyVal);
    2596           2 :                     json_object_object_add(poContainer, pszLastComponent,
    2597             :                                            json_object_new_string(pszVal));
    2598           2 :                     CPLFree(pszVal);
    2599           2 :                     break;
    2600             :                 }
    2601           3 :                 case OFTDateTime:
    2602             :                 {
    2603           3 :                     int nYear = 0;
    2604           3 :                     int nMonth = 0;
    2605           3 :                     int nDay = 0;
    2606           3 :                     int nHour = 0;
    2607           3 :                     int nMin = 0;
    2608           3 :                     int nTZ = 0;
    2609           3 :                     float fSec = 0.0f;
    2610           3 :                     poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay,
    2611             :                                                   &nHour, &nMin, &fSec, &nTZ);
    2612           3 :                     if (nTZ == 0)
    2613             :                     {
    2614           2 :                         json_object_object_add(
    2615             :                             poContainer, pszLastComponent,
    2616             :                             json_object_new_string(CPLSPrintf(
    2617             :                                 "%04d/%02d/%02d %02d:%02d:%06.3f", nYear,
    2618             :                                 nMonth, nDay, nHour, nMin, fSec)));
    2619             :                     }
    2620             :                     else
    2621             :                     {
    2622           1 :                         const int TZOffset = std::abs(nTZ - 100) * 15;
    2623           1 :                         const int TZHour = TZOffset / 60;
    2624           1 :                         const int TZMinute = TZOffset - TZHour * 60;
    2625           1 :                         json_object_object_add(
    2626             :                             poContainer, pszLastComponent,
    2627             :                             json_object_new_string(CPLSPrintf(
    2628             :                                 "%04d/%02d/%02d %02d:%02d:%06.3f%c%02d:%02d",
    2629             :                                 nYear, nMonth, nDay, nHour, nMin, fSec,
    2630           1 :                                 (nTZ >= 100) ? '+' : '-', TZHour, TZMinute)));
    2631             :                     }
    2632           3 :                     break;
    2633             :                 }
    2634          10 :                 default:
    2635             :                 {
    2636          10 :                     const char *pszVal = poFeature->GetFieldAsString(i);
    2637          10 :                     json_object_object_add(poContainer, pszLastComponent,
    2638             :                                            json_object_new_string(pszVal));
    2639             :                 }
    2640             :             }
    2641             :         }
    2642             : 
    2643             :         // Build the field string
    2644          23 :         fields = json_object_to_json_string(fieldObject);
    2645          23 :         json_object_put(fieldObject);
    2646             :     }
    2647             : 
    2648          24 :     return fields;
    2649             : }
    2650             : 
    2651             : /************************************************************************/
    2652             : /*                          ICreateFeature()                            */
    2653             : /************************************************************************/
    2654             : 
    2655          20 : OGRErr OGRElasticLayer::ICreateFeature(OGRFeature *poFeature)
    2656             : {
    2657          20 :     if (m_poDS->GetAccess() != GA_Update)
    2658             :     {
    2659           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2660             :                  "Dataset opened in read-only mode");
    2661           1 :         return OGRERR_FAILURE;
    2662             :     }
    2663             : 
    2664          19 :     FinalizeFeatureDefn();
    2665             : 
    2666          19 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    2667           1 :         return OGRERR_FAILURE;
    2668             : 
    2669          18 :     if (!m_osWriteMapFilename.empty())
    2670           2 :         return OGRERR_NONE;
    2671             : 
    2672          16 :     if (poFeature->GetFID() < 0)
    2673             :     {
    2674          14 :         if (m_nNextFID < 0)
    2675           1 :             m_nNextFID = GetFeatureCount(FALSE);
    2676          14 :         poFeature->SetFID(++m_nNextFID);
    2677             :     }
    2678             : 
    2679          32 :     CPLString osFields(BuildJSonFromFeature(poFeature));
    2680             : 
    2681          16 :     const char *pszId = nullptr;
    2682          16 :     if (poFeature->IsFieldSetAndNotNull(0) && !m_bIgnoreSourceID)
    2683           1 :         pszId = poFeature->GetFieldAsString(0);
    2684             : 
    2685             :     // Check to see if we're using bulk uploading
    2686          16 :     if (m_nBulkUpload > 0)
    2687             :     {
    2688             :         m_osBulkContent +=
    2689           7 :             CPLSPrintf("{\"index\" :{\"_index\":\"%s\"", m_osIndexName.c_str());
    2690           7 :         if (m_poDS->m_nMajorVersion < 7)
    2691             :             m_osBulkContent +=
    2692           6 :                 CPLSPrintf(", \"_type\":\"%s\"", m_osMappingName.c_str());
    2693           7 :         if (pszId)
    2694           0 :             m_osBulkContent += CPLSPrintf(",\"_id\":\"%s\"", pszId);
    2695           7 :         m_osBulkContent += "}}\n" + osFields + "\n\n";
    2696             : 
    2697             :         // Only push the data if we are over our bulk upload limit
    2698           7 :         if ((int)m_osBulkContent.length() > m_nBulkUpload)
    2699             :         {
    2700           0 :             if (!PushIndex())
    2701             :             {
    2702           0 :                 return OGRERR_FAILURE;
    2703             :             }
    2704             :         }
    2705             :     }
    2706             :     else
    2707             :     {
    2708             :         // Fall back to using single item upload for every feature.
    2709           9 :         CPLString osURL(BuildMappingURL(false));
    2710           9 :         if (pszId)
    2711           1 :             osURL += CPLSPrintf("/%s", pszId);
    2712           9 :         json_object *poRes = m_poDS->RunRequest(osURL, osFields);
    2713           9 :         if (poRes == nullptr)
    2714             :         {
    2715           1 :             return OGRERR_FAILURE;
    2716             :         }
    2717           8 :         if (pszId == nullptr)
    2718             :         {
    2719           7 :             json_object *poId = CPL_json_object_object_get(poRes, "_id");
    2720           8 :             if (poId != nullptr &&
    2721           1 :                 json_object_get_type(poId) == json_type_string)
    2722             :             {
    2723           1 :                 pszId = json_object_get_string(poId);
    2724           1 :                 poFeature->SetField(0, pszId);
    2725             :             }
    2726             :         }
    2727           8 :         json_object_put(poRes);
    2728             :     }
    2729             : 
    2730          15 :     return OGRERR_NONE;
    2731             : }
    2732             : 
    2733             : /************************************************************************/
    2734             : /*                           ISetFeature()                              */
    2735             : /************************************************************************/
    2736             : 
    2737           6 : OGRErr OGRElasticLayer::ISetFeature(OGRFeature *poFeature)
    2738             : {
    2739           6 :     if (m_poDS->GetAccess() != GA_Update)
    2740             :     {
    2741           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2742             :                  "Dataset opened in read-only mode");
    2743           1 :         return OGRERR_FAILURE;
    2744             :     }
    2745             : 
    2746           5 :     FinalizeFeatureDefn();
    2747             : 
    2748           5 :     if (!poFeature->IsFieldSetAndNotNull(0))
    2749             :     {
    2750           1 :         CPLError(CE_Failure, CPLE_AppDefined, "_id field not set");
    2751           1 :         return OGRERR_FAILURE;
    2752             :     }
    2753           4 :     if (poFeature->GetFID() < 0 && !m_osFID.empty())
    2754             :     {
    2755           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid FID");
    2756           0 :         return OGRERR_FAILURE;
    2757             :     }
    2758             : 
    2759           4 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    2760           0 :         return OGRERR_FAILURE;
    2761           4 :     PushIndex();
    2762             : 
    2763           8 :     CPLString osFields(BuildJSonFromFeature(poFeature));
    2764             : 
    2765             :     // TODO? we should theoretically detect if the provided _id doesn't exist
    2766             :     CPLString osURL(
    2767           8 :         CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str()));
    2768           4 :     if (m_poDS->m_nMajorVersion < 7)
    2769           4 :         osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    2770           4 :     osURL += CPLSPrintf("/%s", poFeature->GetFieldAsString(0));
    2771           4 :     json_object *poRes = m_poDS->RunRequest(osURL, osFields);
    2772           4 :     if (poRes == nullptr)
    2773             :     {
    2774           1 :         return OGRERR_FAILURE;
    2775             :     }
    2776             :     // CPLDebug("ES", "SetFeature(): %s", json_object_to_json_string(poRes));
    2777           3 :     json_object_put(poRes);
    2778             : 
    2779           3 :     return OGRERR_NONE;
    2780             : }
    2781             : 
    2782             : /************************************************************************/
    2783             : /*                          IUpsertFeature()                            */
    2784             : /************************************************************************/
    2785             : 
    2786           5 : OGRErr OGRElasticLayer::IUpsertFeature(OGRFeature *poFeature)
    2787             : {
    2788           5 :     if (m_poDS->GetAccess() != GA_Update)
    2789             :     {
    2790           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2791             :                  "Dataset opened in read-only mode");
    2792           1 :         return OGRERR_FAILURE;
    2793             :     }
    2794             : 
    2795           4 :     FinalizeFeatureDefn();
    2796             : 
    2797           4 :     if (WriteMapIfNecessary() != OGRERR_NONE)
    2798           0 :         return OGRERR_FAILURE;
    2799             : 
    2800           4 :     if (!m_osWriteMapFilename.empty())
    2801           0 :         return OGRERR_NONE;
    2802             : 
    2803           4 :     if (poFeature->GetFID() < 0)
    2804             :     {
    2805           1 :         if (m_nNextFID < 0)
    2806           0 :             m_nNextFID = GetFeatureCount(FALSE);
    2807           1 :         poFeature->SetFID(++m_nNextFID);
    2808             :     }
    2809             : 
    2810           8 :     CPLString osFields(BuildJSonFromFeature(poFeature));
    2811             : 
    2812           4 :     const char *pszId = nullptr;
    2813           4 :     if (poFeature->IsFieldSetAndNotNull(0))
    2814             :     {
    2815           4 :         pszId = poFeature->GetFieldAsString(0);
    2816             :     }
    2817             :     else
    2818             :     {
    2819           0 :         return OGRERR_FAILURE;
    2820             :     }
    2821             : 
    2822             :     // Check to see if we're using bulk uploading
    2823           4 :     if (m_nBulkUpload > 0)
    2824             :     {
    2825             :         m_osBulkContent +=
    2826             :             CPLSPrintf("{\"update\":{\"_index\":\"%s\",\"_id\":\"%s\"",
    2827           2 :                        m_osIndexName.c_str(), pszId);
    2828           2 :         if (m_poDS->m_nMajorVersion < 7)
    2829             :         {
    2830             :             m_osBulkContent +=
    2831           2 :                 CPLSPrintf(", \"_type\":\"%s\"", m_osMappingName.c_str());
    2832             :         }
    2833             :         m_osBulkContent +=
    2834           2 :             "}}\n{\"doc\":" + osFields + ",\"doc_as_upsert\":true}\n\n";
    2835             : 
    2836             :         // Only push the data if we are over our bulk upload limit
    2837           2 :         if (m_osBulkContent.length() > static_cast<size_t>(m_nBulkUpload))
    2838             :         {
    2839           0 :             if (!PushIndex())
    2840             :             {
    2841           0 :                 return OGRERR_FAILURE;
    2842             :             }
    2843             :         }
    2844             :     }
    2845             :     else
    2846             :     {
    2847             :         // Fall back to using single item upload for every feature.
    2848           2 :         CPLString osURL(BuildMappingURL(false));
    2849           2 :         if (m_poDS->m_nMajorVersion < 7)
    2850             :         {
    2851           2 :             osURL += CPLSPrintf("/%s/_update", pszId);
    2852             :         }
    2853             :         else
    2854             :         {
    2855           0 :             osURL += CPLSPrintf("/_update/%s", pszId);
    2856             :         }
    2857             : 
    2858             :         const CPLString osUpdate =
    2859           2 :             CPLSPrintf("{\"doc\":%s,\"doc_as_upsert\":true}", osFields.c_str());
    2860           2 :         const CPLString osMethod = "POST";
    2861           2 :         if (!m_poDS->UploadFile(osURL, osUpdate, osMethod))
    2862             :         {
    2863           0 :             return OGRERR_FAILURE;
    2864             :         }
    2865             :     }
    2866             : 
    2867           4 :     return OGRERR_NONE;
    2868             : }
    2869             : 
    2870             : /************************************************************************/
    2871             : /*                             PushIndex()                              */
    2872             : /************************************************************************/
    2873             : 
    2874          63 : bool OGRElasticLayer::PushIndex()
    2875             : {
    2876          63 :     if (m_osBulkContent.empty())
    2877             :     {
    2878          56 :         return true;
    2879             :     }
    2880             : 
    2881          14 :     const bool bRet = m_poDS->UploadFile(
    2882           7 :         CPLSPrintf("%s/_bulk", m_poDS->GetURL()), m_osBulkContent);
    2883           7 :     m_osBulkContent.clear();
    2884             : 
    2885           7 :     return bRet;
    2886             : }
    2887             : 
    2888             : /************************************************************************/
    2889             : /*                            CreateField()                             */
    2890             : /************************************************************************/
    2891             : 
    2892          17 : OGRErr OGRElasticLayer::CreateField(const OGRFieldDefn *poFieldDefn,
    2893             :                                     int /*bApproxOK*/)
    2894             : {
    2895          17 :     if (m_poDS->GetAccess() != GA_Update)
    2896             :     {
    2897           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2898             :                  "Dataset opened in read-only mode");
    2899           1 :         return OGRERR_FAILURE;
    2900             :     }
    2901             : 
    2902          16 :     FinalizeFeatureDefn();
    2903          16 :     ResetReading();
    2904             : 
    2905          16 :     if (m_poFeatureDefn->GetFieldIndex(poFieldDefn->GetNameRef()) >= 0)
    2906             :     {
    2907           0 :         if (!EQUAL(poFieldDefn->GetNameRef(), "_id") &&
    2908           0 :             !EQUAL(poFieldDefn->GetNameRef(), "_json"))
    2909             :         {
    2910           0 :             CPLError(
    2911             :                 CE_Failure, CPLE_AppDefined,
    2912             :                 "CreateField() called with an already existing field name: %s",
    2913             :                 poFieldDefn->GetNameRef());
    2914             :         }
    2915           0 :         return OGRERR_FAILURE;
    2916             :     }
    2917             : 
    2918          16 :     std::vector<CPLString> aosPath;
    2919          16 :     if (m_osMappingName == "FeatureCollection")
    2920          14 :         aosPath.push_back("properties");
    2921             : 
    2922          16 :     if (m_bDotAsNestedField)
    2923             :     {
    2924             :         char **papszTokens =
    2925          16 :             CSLTokenizeString2(poFieldDefn->GetNameRef(), ".", 0);
    2926          33 :         for (int i = 0; papszTokens[i]; i++)
    2927          17 :             aosPath.push_back(papszTokens[i]);
    2928          16 :         CSLDestroy(papszTokens);
    2929             :     }
    2930             :     else
    2931           0 :         aosPath.push_back(poFieldDefn->GetNameRef());
    2932             : 
    2933          16 :     AddFieldDefn(poFieldDefn->GetNameRef(), poFieldDefn->GetType(), aosPath,
    2934             :                  poFieldDefn->GetSubType());
    2935             : 
    2936          16 :     m_bSerializeMapping = true;
    2937             : 
    2938          16 :     return OGRERR_NONE;
    2939             : }
    2940             : 
    2941             : /************************************************************************/
    2942             : /*                           CreateGeomField()                          */
    2943             : /************************************************************************/
    2944             : 
    2945          20 : OGRErr OGRElasticLayer::CreateGeomField(const OGRGeomFieldDefn *poFieldIn,
    2946             :                                         int /*bApproxOK*/)
    2947             : 
    2948             : {
    2949          20 :     if (m_poDS->GetAccess() != GA_Update)
    2950             :     {
    2951           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2952             :                  "Dataset opened in read-only mode");
    2953           1 :         return OGRERR_FAILURE;
    2954             :     }
    2955             : 
    2956          19 :     FinalizeFeatureDefn();
    2957          19 :     ResetReading();
    2958             : 
    2959          19 :     if (m_poFeatureDefn->GetGeomFieldIndex(poFieldIn->GetNameRef()) >= 0)
    2960             :     {
    2961           0 :         CPLError(
    2962             :             CE_Failure, CPLE_AppDefined,
    2963             :             "CreateGeomField() called with an already existing field name: %s",
    2964             :             poFieldIn->GetNameRef());
    2965           0 :         return OGRERR_FAILURE;
    2966             :     }
    2967             : 
    2968          38 :     OGRGeomFieldDefn oFieldDefn(poFieldIn);
    2969          19 :     auto poSRSOri = poFieldIn->GetSpatialRef();
    2970          19 :     if (poSRSOri)
    2971             :     {
    2972          17 :         auto poSRS = poSRSOri->Clone();
    2973          17 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2974          17 :         oFieldDefn.SetSpatialRef(poSRS);
    2975          17 :         poSRS->Release();
    2976             :     }
    2977          19 :     if (EQUAL(oFieldDefn.GetNameRef(), ""))
    2978           0 :         oFieldDefn.SetName("geometry");
    2979             : 
    2980          19 :     std::vector<CPLString> aosPath;
    2981          19 :     if (m_bDotAsNestedField)
    2982             :     {
    2983             :         char **papszTokens =
    2984          19 :             CSLTokenizeString2(oFieldDefn.GetNameRef(), ".", 0);
    2985          39 :         for (int i = 0; papszTokens[i]; i++)
    2986          20 :             aosPath.push_back(papszTokens[i]);
    2987          19 :         CSLDestroy(papszTokens);
    2988             :     }
    2989             :     else
    2990           0 :         aosPath.push_back(oFieldDefn.GetNameRef());
    2991             : 
    2992          37 :     if (m_eGeomTypeMapping == ES_GEOMTYPE_GEO_SHAPE ||
    2993          35 :         (m_eGeomTypeMapping == ES_GEOMTYPE_AUTO &&
    2994          17 :          poFieldIn->GetType() != wkbPoint))
    2995             :     {
    2996          16 :         m_abIsGeoPoint.push_back(FALSE);
    2997             :     }
    2998             :     else
    2999             :     {
    3000           3 :         m_abIsGeoPoint.push_back(TRUE);
    3001           3 :         aosPath.push_back("coordinates");
    3002             :     }
    3003             : 
    3004          19 :     m_aaosGeomFieldPaths.push_back(aosPath);
    3005             : 
    3006          19 :     m_aosMapToGeomFieldIndex[BuildPathFromArray(aosPath)] =
    3007          19 :         m_poFeatureDefn->GetGeomFieldCount();
    3008             : 
    3009          19 :     m_poFeatureDefn->AddGeomFieldDefn(&oFieldDefn);
    3010             : 
    3011          19 :     OGRCoordinateTransformation *poCT = nullptr;
    3012          19 :     if (oFieldDefn.GetSpatialRef() != nullptr)
    3013             :     {
    3014          34 :         OGRSpatialReference oSRS_WGS84;
    3015          17 :         oSRS_WGS84.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
    3016          17 :         oSRS_WGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    3017          17 :         if (!oSRS_WGS84.IsSame(oFieldDefn.GetSpatialRef()))
    3018             :         {
    3019           1 :             poCT = OGRCreateCoordinateTransformation(oFieldDefn.GetSpatialRef(),
    3020             :                                                      &oSRS_WGS84);
    3021           1 :             if (poCT == nullptr)
    3022             :             {
    3023           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3024             :                          "On-the-fly reprojection to WGS84 long/lat would be "
    3025             :                          "needed, but instantiation of transformer failed");
    3026             :             }
    3027             :         }
    3028             :     }
    3029             :     else
    3030             :     {
    3031           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    3032             :                  "No SRS given for geometry column %s. SRS is assumed to "
    3033             :                  "be EPSG:4326 (WGS84 long/lat)",
    3034             :                  oFieldDefn.GetNameRef());
    3035             :     }
    3036             : 
    3037          19 :     m_apoCT.push_back(poCT);
    3038             : 
    3039          19 :     m_bSerializeMapping = true;
    3040             : 
    3041          19 :     return OGRERR_NONE;
    3042             : }
    3043             : 
    3044             : /************************************************************************/
    3045             : /*                           TestCapability()                           */
    3046             : /************************************************************************/
    3047             : 
    3048          67 : int OGRElasticLayer::TestCapability(const char *pszCap)
    3049             : {
    3050          67 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    3051           1 :         return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr;
    3052             : 
    3053          66 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    3054          27 :         return TRUE;
    3055             : 
    3056          39 :     else if (EQUAL(pszCap, OLCSequentialWrite) ||
    3057          38 :              EQUAL(pszCap, OLCUpsertFeature) || EQUAL(pszCap, OLCRandomWrite))
    3058           1 :         return m_poDS->GetAccess() == GA_Update;
    3059          38 :     else if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCCreateGeomField))
    3060           2 :         return m_poDS->GetAccess() == GA_Update;
    3061             :     else
    3062          36 :         return FALSE;
    3063             : }
    3064             : 
    3065             : /************************************************************************/
    3066             : /*                   AddTimeoutTerminateAfterToURL()                    */
    3067             : /************************************************************************/
    3068             : 
    3069          16 : void OGRElasticLayer::AddTimeoutTerminateAfterToURL(CPLString &osURL)
    3070             : {
    3071          16 :     if (!m_osSingleQueryTimeout.empty())
    3072           5 :         osURL += "&timeout=" + m_osSingleQueryTimeout;
    3073          16 :     if (!m_osSingleQueryTerminateAfter.empty())
    3074           5 :         osURL += "&terminate_after=" + m_osSingleQueryTerminateAfter;
    3075          16 : }
    3076             : 
    3077             : /************************************************************************/
    3078             : /*                          GetFeatureCount()                           */
    3079             : /************************************************************************/
    3080             : 
    3081          12 : GIntBig OGRElasticLayer::GetFeatureCount(int bForce)
    3082             : {
    3083          12 :     if (m_bFilterMustBeClientSideEvaluated)
    3084             :     {
    3085           0 :         m_bUseSingleQueryParams = true;
    3086           0 :         const auto nRet = OGRLayer::GetFeatureCount(bForce);
    3087           0 :         m_bUseSingleQueryParams = false;
    3088           0 :         return nRet;
    3089             :     }
    3090             : 
    3091          12 :     json_object *poResponse = nullptr;
    3092          24 :     CPLString osURL(CPLSPrintf("%s", m_poDS->GetURL()));
    3093          24 :     CPLString osFilter = "";
    3094          12 :     if (!m_osESSearch.empty())
    3095             :     {
    3096           1 :         if (m_osESSearch[0] != '{')
    3097           0 :             return OGRLayer::GetFeatureCount(bForce);
    3098           1 :         osURL += "/_search?pretty";
    3099           1 :         osFilter = "{ \"size\": 0 ";
    3100           1 :         if (m_osESSearch == "{}")
    3101           0 :             osFilter += '}';
    3102             :         else
    3103           1 :             osFilter += ", " + m_osESSearch.substr(1);
    3104             :     }
    3105          11 :     else if ((m_poSpatialFilter && m_osJSONFilter.empty()) || m_poJSONFilter)
    3106             :     {
    3107           3 :         osFilter = BuildQuery(true);
    3108           3 :         osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
    3109           3 :         if (m_poDS->m_nMajorVersion < 7)
    3110           2 :             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3111           3 :         if (m_poDS->m_nMajorVersion >= 5 && m_osSingleQueryTimeout.empty())
    3112             :         {
    3113           1 :             osURL += "/_count?pretty";
    3114             :         }
    3115             :         else
    3116             :         {
    3117           2 :             osURL += "/_search?pretty";
    3118             :         }
    3119             :     }
    3120           8 :     else if (!m_osJSONFilter.empty())
    3121             :     {
    3122           2 :         osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
    3123           2 :         if (m_poDS->m_nMajorVersion < 7)
    3124           1 :             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3125           2 :         osURL += "/_search?pretty";
    3126           2 :         osFilter = ("{ \"size\": 0, " + m_osJSONFilter.substr(1));
    3127             :     }
    3128             :     else
    3129             :     {
    3130           6 :         osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
    3131           6 :         if (m_poDS->m_nMajorVersion < 7)
    3132           5 :             osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3133           6 :         if (m_osSingleQueryTimeout.empty())
    3134             :         {
    3135           5 :             osURL += "/_count?pretty";
    3136             :         }
    3137             :         else
    3138             :         {
    3139           1 :             osFilter = "{ \"size\": 0 }";
    3140           1 :             osURL += CPLSPrintf("/_search?pretty");
    3141             :         }
    3142             :     }
    3143          12 :     AddTimeoutTerminateAfterToURL(osURL);
    3144             : 
    3145          12 :     poResponse = m_poDS->RunRequest(osURL.c_str(), osFilter.c_str());
    3146             : 
    3147          12 :     json_object *poCount = json_ex_get_object_by_path(poResponse, "hits.count");
    3148          12 :     if (poCount == nullptr)
    3149             :     {
    3150             :         // For _search request
    3151          10 :         poCount = json_ex_get_object_by_path(poResponse, "hits.total");
    3152          10 :         if (poCount && json_object_get_type(poCount) == json_type_object)
    3153             :         {
    3154             :             // Since ES 7.0
    3155           4 :             poCount = json_ex_get_object_by_path(poCount, "value");
    3156             :         }
    3157             :     }
    3158          12 :     if (poCount == nullptr)
    3159             :     {
    3160             :         // For _count request
    3161           4 :         poCount = json_ex_get_object_by_path(poResponse, "count");
    3162             :     }
    3163          12 :     if (poCount == nullptr || json_object_get_type(poCount) != json_type_int)
    3164             :     {
    3165           3 :         json_object_put(poResponse);
    3166           3 :         CPLDebug("ES", "Cannot find hits in GetFeatureCount() response. "
    3167             :                        "Falling back to slow implementation");
    3168           3 :         m_bUseSingleQueryParams = true;
    3169           3 :         const auto nRet = OGRLayer::GetFeatureCount(bForce);
    3170           3 :         m_bUseSingleQueryParams = false;
    3171           3 :         return nRet;
    3172             :     }
    3173             : 
    3174           9 :     GIntBig nCount = json_object_get_int64(poCount);
    3175           9 :     json_object_put(poResponse);
    3176           9 :     return nCount;
    3177             : }
    3178             : 
    3179             : /************************************************************************/
    3180             : /*                            GetValue()                                */
    3181             : /************************************************************************/
    3182             : 
    3183          28 : json_object *OGRElasticLayer::GetValue(int nFieldIdx, swq_expr_node *poValNode)
    3184             : {
    3185          28 :     json_object *poVal = nullptr;
    3186          28 :     if (poValNode->field_type == SWQ_FLOAT)
    3187           1 :         poVal = json_object_new_double(poValNode->float_value);
    3188          27 :     else if (poValNode->field_type == SWQ_INTEGER ||
    3189          26 :              poValNode->field_type == SWQ_INTEGER64)
    3190           2 :         poVal = json_object_new_int64(poValNode->int_value);
    3191          25 :     else if (poValNode->field_type == SWQ_STRING)
    3192          23 :         poVal = json_object_new_string(poValNode->string_value);
    3193           2 :     else if (poValNode->field_type == SWQ_TIMESTAMP)
    3194             :     {
    3195           2 :         int nYear = 0;
    3196           2 :         int nMonth = 0;
    3197           2 :         int nDay = 0;
    3198           2 :         int nHour = 0;
    3199           2 :         int nMinute = 0;
    3200           2 :         float fSecond = 0;
    3201           4 :         if (sscanf(poValNode->string_value, "%04d/%02d/%02d %02d:%02d:%f",
    3202           2 :                    &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) >= 3 ||
    3203           0 :             sscanf(poValNode->string_value, "%04d-%02d-%02dT%02d:%02d:%f",
    3204             :                    &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) >= 3)
    3205             :         {
    3206             :             OGRFieldType eType(
    3207           2 :                 m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType());
    3208           2 :             if (eType == OFTDateTime)
    3209           2 :                 poVal = json_object_new_string(
    3210             :                     CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02.03f", nYear,
    3211             :                                nMonth, nDay, nHour, nMinute, fSecond));
    3212           0 :             else if (eType == OFTDate)
    3213           0 :                 poVal = json_object_new_string(
    3214             :                     CPLSPrintf("%04d/%02d/%02d", nYear, nMonth, nDay));
    3215             :             else
    3216           0 :                 poVal = json_object_new_string(
    3217             :                     CPLSPrintf("%02d:%02d:%02.03f", nHour, nMinute, fSecond));
    3218             :         }
    3219             :         else
    3220             :         {
    3221           0 :             return nullptr;
    3222             :         }
    3223             :     }
    3224             :     else
    3225             :     {
    3226           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unhandled type: %d",
    3227           0 :                  poValNode->field_type);
    3228             :     }
    3229          28 :     return poVal;
    3230             : }
    3231             : 
    3232             : /************************************************************************/
    3233             : /*                      OGRESGetFieldIndexFromSQL()                     */
    3234             : /************************************************************************/
    3235             : 
    3236          31 : static int OGRESGetFieldIndexFromSQL(const swq_expr_node *poNode)
    3237             : {
    3238          31 :     if (poNode->eNodeType == SNT_COLUMN)
    3239          27 :         return poNode->field_index;
    3240             : 
    3241           4 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_CAST &&
    3242           1 :         poNode->nSubExprCount >= 1 &&
    3243           1 :         poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN)
    3244           1 :         return poNode->papoSubExpr[0]->field_index;
    3245             : 
    3246           3 :     return -1;
    3247             : }
    3248             : 
    3249             : /************************************************************************/
    3250             : /*                        TranslateSQLToFilter()                        */
    3251             : /************************************************************************/
    3252             : 
    3253          40 : json_object *OGRElasticLayer::TranslateSQLToFilter(swq_expr_node *poNode)
    3254             : {
    3255          40 :     if (poNode->eNodeType == SNT_OPERATION)
    3256             :     {
    3257          40 :         int nFieldIdx = 0;
    3258          40 :         CPL_IGNORE_RET_VAL(nFieldIdx);  // to make cppcheck happy
    3259          40 :         if (poNode->nOperation == SWQ_AND && poNode->nSubExprCount == 2)
    3260             :         {
    3261             :             // For AND, we can deal with a failure in one of the branch
    3262             :             // since client-side will do that extra filtering
    3263             :             json_object *poFilter1 =
    3264           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[0]);
    3265             :             json_object *poFilter2 =
    3266           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[1]);
    3267           3 :             if (poFilter1 && poFilter2)
    3268             :             {
    3269           1 :                 json_object *poRet = json_object_new_object();
    3270           1 :                 json_object *poBool = json_object_new_object();
    3271           1 :                 json_object_object_add(poRet, "bool", poBool);
    3272           1 :                 json_object *poMust = json_object_new_array();
    3273           1 :                 json_object_object_add(poBool, "must", poMust);
    3274           1 :                 json_object_array_add(poMust, poFilter1);
    3275           1 :                 json_object_array_add(poMust, poFilter2);
    3276          35 :                 return poRet;
    3277             :             }
    3278           2 :             else if (poFilter1)
    3279           1 :                 return poFilter1;
    3280             :             else
    3281           1 :                 return poFilter2;
    3282             :         }
    3283          37 :         else if (poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
    3284             :         {
    3285             :             json_object *poFilter1 =
    3286           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[0]);
    3287             :             json_object *poFilter2 =
    3288           3 :                 TranslateSQLToFilter(poNode->papoSubExpr[1]);
    3289           3 :             if (poFilter1 && poFilter2)
    3290             :             {
    3291           2 :                 json_object *poRet = json_object_new_object();
    3292           2 :                 json_object *poBool = json_object_new_object();
    3293           2 :                 json_object_object_add(poRet, "bool", poBool);
    3294           2 :                 json_object *poShould = json_object_new_array();
    3295           2 :                 json_object_object_add(poBool, "should", poShould);
    3296           2 :                 json_object_array_add(poShould, poFilter1);
    3297           2 :                 json_object_array_add(poShould, poFilter2);
    3298           2 :                 return poRet;
    3299             :             }
    3300             :             else
    3301             :             {
    3302           1 :                 json_object_put(poFilter1);
    3303           1 :                 json_object_put(poFilter2);
    3304           1 :                 return nullptr;
    3305             :             }
    3306             :         }
    3307          34 :         else if (poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
    3308             :         {
    3309           6 :             if (poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
    3310           2 :                 poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
    3311           1 :                 poNode->papoSubExpr[0]->nSubExprCount == 1 &&
    3312           5 :                 poNode->papoSubExpr[0]->papoSubExpr[0]->field_index != 0 &&
    3313           1 :                 poNode->papoSubExpr[0]->papoSubExpr[0]->field_index <
    3314           1 :                     m_poFeatureDefn->GetFieldCount())
    3315             :             {
    3316           1 :                 json_object *poRet = json_object_new_object();
    3317           1 :                 json_object *poExists = json_object_new_object();
    3318             :                 CPLString osFieldName(BuildPathFromArray(
    3319             :                     m_aaosFieldPaths
    3320           1 :                         [poNode->papoSubExpr[0]->papoSubExpr[0]->field_index]));
    3321           1 :                 json_object_object_add(poExists, "field",
    3322             :                                        json_object_new_string(osFieldName));
    3323           1 :                 json_object_object_add(poRet, "exists", poExists);
    3324           1 :                 return poRet;
    3325             :             }
    3326             :             else
    3327             :             {
    3328             :                 json_object *poFilter =
    3329           1 :                     TranslateSQLToFilter(poNode->papoSubExpr[0]);
    3330           1 :                 if (poFilter)
    3331             :                 {
    3332           1 :                     json_object *poRet = json_object_new_object();
    3333           1 :                     json_object *poBool = json_object_new_object();
    3334           1 :                     json_object_object_add(poRet, "bool", poBool);
    3335           1 :                     json_object_object_add(poBool, "must_not", poFilter);
    3336           1 :                     return poRet;
    3337             :                 }
    3338             :                 else
    3339             :                 {
    3340           0 :                     return nullptr;
    3341             :                 }
    3342             :             }
    3343             :         }
    3344          65 :         else if (poNode->nOperation == SWQ_ISNULL &&
    3345           1 :                  poNode->nSubExprCount == 1 &&
    3346           1 :                  (nFieldIdx =
    3347          34 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3348           1 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3349             :         {
    3350           1 :             json_object *poRet = json_object_new_object();
    3351           1 :             json_object *poExists = json_object_new_object();
    3352             :             CPLString osFieldName(
    3353           1 :                 BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3354           1 :             json_object_object_add(poExists, "field",
    3355             :                                    json_object_new_string(osFieldName));
    3356           1 :             json_object *poBool = json_object_new_object();
    3357           1 :             json_object_object_add(poRet, "bool", poBool);
    3358           1 :             json_object *poMustNot = json_object_new_object();
    3359           1 :             json_object_object_add(poMustNot, "exists", poExists);
    3360           1 :             json_object_object_add(poBool, "must_not", poMustNot);
    3361           1 :             return poRet;
    3362             :         }
    3363          31 :         else if (poNode->nOperation == SWQ_NE)
    3364             :         {
    3365           1 :             poNode->nOperation = SWQ_EQ;
    3366           1 :             json_object *poFilter = TranslateSQLToFilter(poNode);
    3367           1 :             poNode->nOperation = SWQ_NE;
    3368           1 :             if (poFilter)
    3369             :             {
    3370           1 :                 json_object *poRet = json_object_new_object();
    3371           1 :                 json_object *poBool = json_object_new_object();
    3372           1 :                 json_object_object_add(poRet, "bool", poBool);
    3373           1 :                 json_object_object_add(poBool, "must_not", poFilter);
    3374           1 :                 return poRet;
    3375             :             }
    3376             :             else
    3377             :             {
    3378           0 :                 return nullptr;
    3379             :             }
    3380             :         }
    3381          16 :         else if (poNode->nOperation == SWQ_EQ && poNode->nSubExprCount == 2 &&
    3382          16 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
    3383          16 :                  (nFieldIdx =
    3384          62 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) >= 0 &&
    3385          13 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3386             :         {
    3387          13 :             json_object *poVal = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
    3388          13 :             if (poVal == nullptr)
    3389             :             {
    3390           0 :                 return nullptr;
    3391             :             }
    3392          13 :             json_object *poRet = json_object_new_object();
    3393          13 :             if (nFieldIdx == 0)
    3394             :             {
    3395           1 :                 json_object *poIds = json_object_new_object();
    3396           1 :                 json_object *poValues = json_object_new_array();
    3397           1 :                 json_object_object_add(poIds, "values", poValues);
    3398           1 :                 json_object_array_add(poValues, poVal);
    3399           1 :                 json_object_object_add(poRet, "ids", poIds);
    3400             :             }
    3401             :             else
    3402             :             {
    3403          12 :                 json_object *poTerm = json_object_new_object();
    3404             :                 CPLString osPath(
    3405          24 :                     BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3406          12 :                 bool bNotAnalyzed = true;
    3407          12 :                 if (poNode->papoSubExpr[1]->field_type == SWQ_STRING)
    3408             :                 {
    3409             :                     const char *pszFieldName =
    3410          12 :                         m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
    3411          12 :                     bNotAnalyzed = CSLFindString(m_papszNotAnalyzedFields,
    3412             :                                                  pszFieldName) >= 0;
    3413          12 :                     if (!bNotAnalyzed)
    3414             :                     {
    3415           3 :                         if (CSLFindString(m_papszFieldsWithRawValue,
    3416           3 :                                           pszFieldName) >= 0)
    3417             :                         {
    3418           1 :                             osPath += ".raw";
    3419           1 :                             bNotAnalyzed = true;
    3420             :                         }
    3421           2 :                         else if (!m_bFilterMustBeClientSideEvaluated)
    3422             :                         {
    3423           2 :                             m_bFilterMustBeClientSideEvaluated = true;
    3424           2 :                             CPLDebug("ES",
    3425             :                                      "Part or full filter will have to be "
    3426             :                                      "evaluated on "
    3427             :                                      "client side (equality test on a analyzed "
    3428             :                                      "field).");
    3429             :                         }
    3430             :                     }
    3431             :                 }
    3432          12 :                 json_object_object_add(poRet, bNotAnalyzed ? "term" : "match",
    3433             :                                        poTerm);
    3434          12 :                 json_object_object_add(poTerm, osPath, poVal);
    3435             : 
    3436          12 :                 if (!bNotAnalyzed && m_poDS->m_nMajorVersion < 2)
    3437             :                 {
    3438           0 :                     json_object *poNewRet = json_object_new_object();
    3439           0 :                     json_object_object_add(poNewRet, "query", poRet);
    3440           0 :                     poRet = poNewRet;
    3441             :                 }
    3442             :             }
    3443          13 :             return poRet;
    3444             :         }
    3445          50 :         else if ((poNode->nOperation == SWQ_LT ||
    3446          16 :                   poNode->nOperation == SWQ_LE ||
    3447          15 :                   poNode->nOperation == SWQ_GT ||
    3448          14 :                   poNode->nOperation == SWQ_GE) &&
    3449           5 :                  poNode->nSubExprCount == 2 &&
    3450           5 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
    3451           5 :                  (nFieldIdx =
    3452          39 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3453           5 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3454             :         {
    3455           5 :             json_object *poVal = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
    3456           5 :             if (poVal == nullptr)
    3457             :             {
    3458           0 :                 return nullptr;
    3459             :             }
    3460           5 :             json_object *poRet = json_object_new_object();
    3461           5 :             json_object *poRange = json_object_new_object();
    3462           5 :             json_object_object_add(poRet, "range", poRange);
    3463           5 :             json_object *poFieldConstraint = json_object_new_object();
    3464             :             CPLString osFieldName(
    3465           5 :                 BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3466           5 :             json_object_object_add(poRange, osFieldName, poFieldConstraint);
    3467           9 :             const char *pszOp = (poNode->nOperation == SWQ_LT)   ? "lt"
    3468           7 :                                 : (poNode->nOperation == SWQ_LE) ? "lte"
    3469           3 :                                 : (poNode->nOperation == SWQ_GT)
    3470           3 :                                     ? "gt"
    3471             :                                     :
    3472             :                                     /*(poNode->nOperation == SWQ_GE) ?*/ "gte";
    3473           5 :             json_object_object_add(poFieldConstraint, pszOp, poVal);
    3474           5 :             return poRet;
    3475             :         }
    3476          25 :         else if (poNode->nOperation == SWQ_BETWEEN &&
    3477           1 :                  poNode->nSubExprCount == 3 &&
    3478           1 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
    3479           1 :                  poNode->papoSubExpr[2]->eNodeType == SNT_CONSTANT &&
    3480           1 :                  (nFieldIdx =
    3481          14 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3482           1 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3483             :         {
    3484           1 :             json_object *poVal1 = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
    3485           1 :             if (poVal1 == nullptr)
    3486             :             {
    3487           0 :                 return nullptr;
    3488             :             }
    3489           1 :             json_object *poVal2 = GetValue(nFieldIdx, poNode->papoSubExpr[2]);
    3490           1 :             if (poVal2 == nullptr)
    3491             :             {
    3492           0 :                 json_object_put(poVal1);
    3493           0 :                 return nullptr;
    3494             :             }
    3495             : 
    3496           1 :             json_object *poRet = json_object_new_object();
    3497           1 :             json_object *poRange = json_object_new_object();
    3498           1 :             json_object_object_add(poRet, "range", poRange);
    3499           1 :             json_object *poFieldConstraint = json_object_new_object();
    3500             :             CPLString osFieldName(
    3501           1 :                 BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3502           1 :             json_object_object_add(poRange, osFieldName, poFieldConstraint);
    3503           1 :             json_object_object_add(poFieldConstraint, "gte", poVal1);
    3504           1 :             json_object_object_add(poFieldConstraint, "lte", poVal2);
    3505           1 :             return poRet;
    3506             :         }
    3507           4 :         else if (poNode->nOperation == SWQ_IN && poNode->nSubExprCount > 1 &&
    3508           4 :                  (nFieldIdx =
    3509          19 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) >= 0 &&
    3510           4 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3511             :         {
    3512           4 :             bool bAllConstant = true;
    3513          12 :             for (int i = 1; i < poNode->nSubExprCount; i++)
    3514             :             {
    3515           8 :                 if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT)
    3516             :                 {
    3517           0 :                     bAllConstant = false;
    3518           0 :                     break;
    3519             :                 }
    3520             :             }
    3521           4 :             if (bAllConstant)
    3522             :             {
    3523           4 :                 json_object *poRet = json_object_new_object();
    3524           4 :                 if (nFieldIdx == 0)
    3525             :                 {
    3526           1 :                     json_object *poIds = json_object_new_object();
    3527           1 :                     json_object *poValues = json_object_new_array();
    3528           1 :                     json_object_object_add(poIds, "values", poValues);
    3529           1 :                     json_object_object_add(poRet, "ids", poIds);
    3530           3 :                     for (int i = 1; i < poNode->nSubExprCount; i++)
    3531             :                     {
    3532             :                         json_object *poVal =
    3533           2 :                             GetValue(nFieldIdx, poNode->papoSubExpr[i]);
    3534           2 :                         if (poVal == nullptr)
    3535             :                         {
    3536           0 :                             json_object_put(poRet);
    3537           0 :                             return nullptr;
    3538             :                         }
    3539           2 :                         json_object_array_add(poValues, poVal);
    3540             :                     }
    3541             :                 }
    3542             :                 else
    3543             :                 {
    3544           3 :                     bool bNotAnalyzed = true;
    3545             :                     CPLString osPath(
    3546           3 :                         BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3547           3 :                     if (poNode->papoSubExpr[1]->field_type == SWQ_STRING)
    3548             :                     {
    3549             :                         const char *pszFieldName =
    3550           3 :                             m_poFeatureDefn->GetFieldDefn(nFieldIdx)
    3551           3 :                                 ->GetNameRef();
    3552           3 :                         bNotAnalyzed = CSLFindString(m_papszNotAnalyzedFields,
    3553             :                                                      pszFieldName) >= 0;
    3554           5 :                         if (!bNotAnalyzed &&
    3555           2 :                             CSLFindString(m_papszFieldsWithRawValue,
    3556             :                                           pszFieldName) >= 0)
    3557             :                         {
    3558           1 :                             osPath += ".raw";
    3559           1 :                             bNotAnalyzed = true;
    3560             :                         }
    3561             : 
    3562           3 :                         if (!bNotAnalyzed &&
    3563           1 :                             !m_bFilterMustBeClientSideEvaluated)
    3564             :                         {
    3565           1 :                             m_bFilterMustBeClientSideEvaluated = true;
    3566           1 :                             CPLDebug("ES",
    3567             :                                      "Part or full filter will have to be "
    3568             :                                      "evaluated on client side (IN test on a "
    3569             :                                      "analyzed field).");
    3570             :                         }
    3571             :                     }
    3572             : 
    3573           3 :                     if (bNotAnalyzed)
    3574             :                     {
    3575           2 :                         json_object *poTerms = json_object_new_object();
    3576           2 :                         json_object_object_add(poRet, "terms", poTerms);
    3577           2 :                         json_object *poTermsValues = json_object_new_array();
    3578           2 :                         json_object_object_add(poTerms, osPath, poTermsValues);
    3579           6 :                         for (int i = 1; i < poNode->nSubExprCount; i++)
    3580             :                         {
    3581             :                             json_object *poVal =
    3582           4 :                                 GetValue(nFieldIdx, poNode->papoSubExpr[i]);
    3583           4 :                             if (poVal == nullptr)
    3584             :                             {
    3585           0 :                                 json_object_put(poRet);
    3586           0 :                                 return nullptr;
    3587             :                             }
    3588           4 :                             json_object_array_add(poTermsValues, poVal);
    3589             :                         }
    3590             :                     }
    3591             :                     else
    3592             :                     {
    3593           1 :                         json_object *poBool = json_object_new_object();
    3594           1 :                         json_object_object_add(poRet, "bool", poBool);
    3595           1 :                         json_object *poShould = json_object_new_array();
    3596           1 :                         json_object_object_add(poBool, "should", poShould);
    3597           3 :                         for (int i = 1; i < poNode->nSubExprCount; i++)
    3598             :                         {
    3599             :                             json_object *poVal =
    3600           2 :                                 GetValue(nFieldIdx, poNode->papoSubExpr[i]);
    3601           2 :                             if (poVal == nullptr)
    3602             :                             {
    3603           0 :                                 json_object_put(poRet);
    3604           0 :                                 return nullptr;
    3605             :                             }
    3606           2 :                             json_object *poShouldElt = json_object_new_object();
    3607           2 :                             json_object *poMatch = json_object_new_object();
    3608           2 :                             json_object_object_add(poShouldElt, "match",
    3609             :                                                    poMatch);
    3610           2 :                             json_object_object_add(poMatch, osPath, poVal);
    3611             : 
    3612           2 :                             if (m_poDS->m_nMajorVersion < 2)
    3613             :                             {
    3614             :                                 json_object *poNewShouldElt =
    3615           0 :                                     json_object_new_object();
    3616           0 :                                 json_object_object_add(poNewShouldElt, "query",
    3617             :                                                        poShouldElt);
    3618           0 :                                 poShouldElt = poNewShouldElt;
    3619             :                             }
    3620           2 :                             json_object_array_add(poShould, poShouldElt);
    3621             :                         }
    3622             :                     }
    3623             :                 }
    3624           4 :                 return poRet;
    3625             :             }
    3626             :         }
    3627          17 :         else if ((poNode->nOperation == SWQ_LIKE ||
    3628           3 :                   poNode->nOperation ==
    3629           4 :                       SWQ_ILIKE) &&  // ES actual semantics doesn't match
    3630             :                                      // exactly either...
    3631           4 :                  poNode->nSubExprCount >= 2 &&
    3632           4 :                  (nFieldIdx =
    3633          18 :                       OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
    3634           4 :                  nFieldIdx < m_poFeatureDefn->GetFieldCount())
    3635             :         {
    3636           4 :             char chEscape = '\0';
    3637           4 :             if (poNode->nSubExprCount == 3)
    3638           1 :                 chEscape = poNode->papoSubExpr[2]->string_value[0];
    3639           4 :             const char *pszPattern = poNode->papoSubExpr[1]->string_value;
    3640             :             const char *pszFieldName =
    3641           4 :                 m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
    3642             :             bool bNotAnalyzed =
    3643           4 :                 CSLFindString(m_papszNotAnalyzedFields, pszFieldName) >= 0;
    3644           4 :             CPLString osPath(BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
    3645           7 :             if (!bNotAnalyzed &&
    3646           3 :                 CSLFindString(m_papszFieldsWithRawValue, pszFieldName) >= 0)
    3647             :             {
    3648           1 :                 osPath += ".raw";
    3649           1 :                 bNotAnalyzed = true;
    3650             :             }
    3651             : 
    3652           4 :             if (strchr(pszPattern, '*') || strchr(pszPattern, '?'))
    3653             :             {
    3654           1 :                 CPLDebug("ES", "Cannot handle * or ? in LIKE pattern");
    3655             :             }
    3656           3 :             else if (!bNotAnalyzed)
    3657             :             {
    3658           1 :                 if (!m_bFilterMustBeClientSideEvaluated)
    3659             :                 {
    3660           1 :                     m_bFilterMustBeClientSideEvaluated = true;
    3661           1 :                     CPLDebug(
    3662             :                         "ES",
    3663             :                         "Part or full filter will have to be evaluated on "
    3664             :                         "client side (wildcard test on a analyzed field).");
    3665             :                 }
    3666             :             }
    3667             :             else
    3668             :             {
    3669           2 :                 CPLString osUnescaped;
    3670           8 :                 for (int i = 0; pszPattern[i] != '\0'; ++i)
    3671             :                 {
    3672           6 :                     if (chEscape == pszPattern[i])
    3673             :                     {
    3674           1 :                         if (pszPattern[i + 1] == '\0')
    3675           0 :                             break;
    3676           1 :                         osUnescaped += pszPattern[i + 1];
    3677           1 :                         i++;
    3678             :                     }
    3679           5 :                     else if (pszPattern[i] == '%')
    3680             :                     {
    3681           2 :                         osUnescaped += '*';
    3682             :                     }
    3683           3 :                     else if (pszPattern[i] == '_')
    3684             :                     {
    3685           2 :                         osUnescaped += '?';
    3686             :                     }
    3687             :                     else
    3688             :                     {
    3689           1 :                         osUnescaped += pszPattern[i];
    3690             :                     }
    3691             :                 }
    3692           2 :                 json_object *poRet = json_object_new_object();
    3693           2 :                 json_object *poWildcard = json_object_new_object();
    3694           2 :                 json_object_object_add(poRet, "wildcard", poWildcard);
    3695           2 :                 json_object_object_add(poWildcard, osPath,
    3696             :                                        json_object_new_string(osUnescaped));
    3697           2 :                 return poRet;
    3698             :             }
    3699             :         }
    3700             :     }
    3701             : 
    3702           5 :     if (!m_bFilterMustBeClientSideEvaluated)
    3703             :     {
    3704           4 :         m_bFilterMustBeClientSideEvaluated = true;
    3705           4 :         CPLDebug("ES", "Part or full filter will have to be evaluated on "
    3706             :                        "client side.");
    3707             :     }
    3708           5 :     return nullptr;
    3709             : }
    3710             : 
    3711             : /************************************************************************/
    3712             : /*                          SetAttributeFilter()                        */
    3713             : /************************************************************************/
    3714             : 
    3715          37 : OGRErr OGRElasticLayer::SetAttributeFilter(const char *pszFilter)
    3716             : {
    3717          37 :     m_bFilterMustBeClientSideEvaluated = false;
    3718          37 :     if (pszFilter != nullptr && pszFilter[0] == '{')
    3719             :     {
    3720           2 :         if (!m_osESSearch.empty())
    3721             :         {
    3722           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3723             :                      "Setting an Elasticsearch filter on a resulting layer "
    3724             :                      "is not supported");
    3725           0 :             return OGRERR_FAILURE;
    3726             :         }
    3727           2 :         OGRLayer::SetAttributeFilter(nullptr);
    3728           2 :         m_osJSONFilter = pszFilter;
    3729           2 :         return OGRERR_NONE;
    3730             :     }
    3731             :     else
    3732             :     {
    3733          35 :         m_osJSONFilter.clear();
    3734          35 :         json_object_put(m_poJSONFilter);
    3735          35 :         m_poJSONFilter = nullptr;
    3736          35 :         OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
    3737          35 :         if (eErr == OGRERR_NONE && m_poAttrQuery != nullptr)
    3738             :         {
    3739             :             swq_expr_node *poNode =
    3740          26 :                 reinterpret_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
    3741          26 :             m_poJSONFilter = TranslateSQLToFilter(poNode);
    3742             :         }
    3743          35 :         return eErr;
    3744             :     }
    3745             : }
    3746             : 
    3747             : /************************************************************************/
    3748             : /*                          ClampEnvelope()                             */
    3749             : /************************************************************************/
    3750             : 
    3751           7 : void OGRElasticLayer::ClampEnvelope(OGREnvelope &sEnvelope)
    3752             : {
    3753           7 :     if (sEnvelope.MinX < -180)
    3754           1 :         sEnvelope.MinX = -180;
    3755           7 :     if (sEnvelope.MinX > 180)
    3756           0 :         sEnvelope.MinX = 180;
    3757             : 
    3758           7 :     if (sEnvelope.MinY < -90)
    3759           1 :         sEnvelope.MinY = -90;
    3760           7 :     if (sEnvelope.MinY > 90)
    3761           0 :         sEnvelope.MinY = 90;
    3762             : 
    3763           7 :     if (sEnvelope.MaxX > 180)
    3764           1 :         sEnvelope.MaxX = 180;
    3765           7 :     if (sEnvelope.MaxX < -180)
    3766           0 :         sEnvelope.MaxX = -180;
    3767             : 
    3768           7 :     if (sEnvelope.MaxY > 90)
    3769           1 :         sEnvelope.MaxY = 90;
    3770           7 :     if (sEnvelope.MaxY < -90)
    3771           0 :         sEnvelope.MaxY = -90;
    3772           7 : }
    3773             : 
    3774             : /************************************************************************/
    3775             : /*                          SetSpatialFilter()                          */
    3776             : /************************************************************************/
    3777             : 
    3778          16 : void OGRElasticLayer::SetSpatialFilter(int iGeomField, OGRGeometry *poGeomIn)
    3779             : 
    3780             : {
    3781          16 :     FinalizeFeatureDefn();
    3782             : 
    3783          30 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
    3784          14 :         GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone)
    3785             :     {
    3786           2 :         if (iGeomField != 0)
    3787             :         {
    3788           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    3789             :                      "Invalid geometry field index : %d", iGeomField);
    3790             :         }
    3791          12 :         return;
    3792             :     }
    3793          14 :     m_iGeomFieldFilter = iGeomField;
    3794             : 
    3795          14 :     InstallFilter(poGeomIn);
    3796             : 
    3797          14 :     json_object_put(m_poSpatialFilter);
    3798          14 :     m_poSpatialFilter = nullptr;
    3799             : 
    3800          14 :     if (poGeomIn == nullptr)
    3801           9 :         return;
    3802             : 
    3803           5 :     if (!m_osESSearch.empty())
    3804             :     {
    3805           0 :         CPLError(
    3806             :             CE_Failure, CPLE_AppDefined,
    3807             :             "Setting a spatial filter on a resulting layer is not supported");
    3808           0 :         return;
    3809             :     }
    3810             : 
    3811           5 :     OGREnvelope sEnvelope;
    3812           5 :     poGeomIn->getEnvelope(&sEnvelope);
    3813           5 :     ClampEnvelope(sEnvelope);
    3814             : 
    3815           5 :     if (sEnvelope.MinX == -180 && sEnvelope.MinY == -90 &&
    3816           1 :         sEnvelope.MaxX == 180 && sEnvelope.MaxY == 90)
    3817             :     {
    3818           1 :         return;
    3819             :     }
    3820             : 
    3821           4 :     m_poSpatialFilter = json_object_new_object();
    3822             : 
    3823           4 :     if (m_abIsGeoPoint[iGeomField])
    3824             :     {
    3825           1 :         json_object *geo_bounding_box = json_object_new_object();
    3826           1 :         json_object_object_add(m_poSpatialFilter, "geo_bounding_box",
    3827             :                                geo_bounding_box);
    3828             : 
    3829           2 :         CPLString osPath = BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]);
    3830             : 
    3831           1 :         json_object *field = json_object_new_object();
    3832           1 :         json_object_object_add(geo_bounding_box, osPath.c_str(), field);
    3833             : 
    3834           1 :         json_object *top_left = json_object_new_object();
    3835           1 :         json_object_object_add(field, "top_left", top_left);
    3836           1 :         json_object_object_add(
    3837             :             top_left, "lat",
    3838             :             json_object_new_double_with_precision(sEnvelope.MaxY, 6));
    3839           1 :         json_object_object_add(
    3840             :             top_left, "lon",
    3841             :             json_object_new_double_with_precision(sEnvelope.MinX, 6));
    3842             : 
    3843           1 :         json_object *bottom_right = json_object_new_object();
    3844           1 :         json_object_object_add(field, "bottom_right", bottom_right);
    3845           1 :         json_object_object_add(
    3846             :             bottom_right, "lat",
    3847             :             json_object_new_double_with_precision(sEnvelope.MinY, 6));
    3848           1 :         json_object_object_add(
    3849             :             bottom_right, "lon",
    3850             :             json_object_new_double_with_precision(sEnvelope.MaxX, 6));
    3851             :     }
    3852             :     else
    3853             :     {
    3854           3 :         json_object *geo_shape = json_object_new_object();
    3855           3 :         json_object_object_add(m_poSpatialFilter, "geo_shape", geo_shape);
    3856             : 
    3857           6 :         CPLString osPath = BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]);
    3858             : 
    3859           3 :         json_object *field = json_object_new_object();
    3860           3 :         json_object_object_add(geo_shape, osPath.c_str(), field);
    3861             : 
    3862           3 :         json_object *shape = json_object_new_object();
    3863           3 :         json_object_object_add(field, "shape", shape);
    3864             : 
    3865           3 :         json_object_object_add(shape, "type",
    3866             :                                json_object_new_string("envelope"));
    3867             : 
    3868           3 :         json_object *coordinates = json_object_new_array();
    3869           3 :         json_object_object_add(shape, "coordinates", coordinates);
    3870             : 
    3871           3 :         json_object *top_left = json_object_new_array();
    3872           3 :         json_object_array_add(
    3873             :             top_left, json_object_new_double_with_precision(sEnvelope.MinX, 6));
    3874           3 :         json_object_array_add(
    3875             :             top_left, json_object_new_double_with_precision(sEnvelope.MaxY, 6));
    3876           3 :         json_object_array_add(coordinates, top_left);
    3877             : 
    3878           3 :         json_object *bottom_right = json_object_new_array();
    3879           3 :         json_object_array_add(
    3880             :             bottom_right,
    3881             :             json_object_new_double_with_precision(sEnvelope.MaxX, 6));
    3882           3 :         json_object_array_add(
    3883             :             bottom_right,
    3884             :             json_object_new_double_with_precision(sEnvelope.MinY, 6));
    3885           3 :         json_object_array_add(coordinates, bottom_right);
    3886             :     }
    3887             : }
    3888             : 
    3889             : /************************************************************************/
    3890             : /*                            GetExtent()                                */
    3891             : /************************************************************************/
    3892             : 
    3893           7 : OGRErr OGRElasticLayer::GetExtent(int iGeomField, OGREnvelope *psExtent,
    3894             :                                   int bForce)
    3895             : {
    3896           7 :     FinalizeFeatureDefn();
    3897             : 
    3898           7 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount())
    3899             :     {
    3900           2 :         if (iGeomField != 0)
    3901             :         {
    3902           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    3903             :                      "Invalid geometry field index : %d", iGeomField);
    3904             :         }
    3905           2 :         return OGRERR_FAILURE;
    3906             :     }
    3907             : 
    3908             :     // geo_shape aggregation is only available since ES 7.8, but only with XPack
    3909             :     // for now
    3910           6 :     if (!m_abIsGeoPoint[iGeomField] &&
    3911           1 :         !(m_poDS->m_nMajorVersion > 7 ||
    3912           1 :           (m_poDS->m_nMajorVersion == 7 && m_poDS->m_nMinorVersion >= 8)))
    3913             :     {
    3914           1 :         m_bUseSingleQueryParams = true;
    3915             :         const auto eRet =
    3916           1 :             OGRLayer::GetExtentInternal(iGeomField, psExtent, bForce);
    3917           1 :         m_bUseSingleQueryParams = false;
    3918           1 :         return eRet;
    3919             :     }
    3920             : 
    3921             :     CPLString osFilter = CPLSPrintf(
    3922             :         "{ \"size\": 0, \"aggs\" : { \"bbox\" : { \"geo_bounds\" : { \"field\" "
    3923             :         ": \"%s\" } } } }",
    3924           8 :         BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]).c_str());
    3925             :     CPLString osURL =
    3926           8 :         CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str());
    3927           4 :     if (m_poDS->m_nMajorVersion < 7)
    3928           3 :         osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
    3929           4 :     osURL += "/_search?pretty";
    3930           4 :     AddTimeoutTerminateAfterToURL(osURL);
    3931             : 
    3932           4 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    3933             :     json_object *poResponse =
    3934           4 :         m_poDS->RunRequest(osURL.c_str(), osFilter.c_str());
    3935           4 :     CPLPopErrorHandler();
    3936           4 :     if (poResponse == nullptr)
    3937             :     {
    3938           2 :         const char *pszLastErrorMsg = CPLGetLastErrorMsg();
    3939           2 :         if (!m_abIsGeoPoint[iGeomField] &&
    3940           0 :             strstr(pszLastErrorMsg, "Fielddata is not supported on field") !=
    3941             :                 nullptr)
    3942             :         {
    3943           0 :             CPLDebug("ES",
    3944             :                      "geo_bounds aggregation failed, likely because of lack "
    3945             :                      "of XPack. Using client-side method");
    3946           0 :             CPLErrorReset();
    3947             :         }
    3948             :         else
    3949             :         {
    3950           2 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", pszLastErrorMsg);
    3951             :         }
    3952             :     }
    3953             : 
    3954             :     json_object *poBounds =
    3955           4 :         json_ex_get_object_by_path(poResponse, "aggregations.bbox.bounds");
    3956           4 :     json_object *poTopLeft = json_ex_get_object_by_path(poBounds, "top_left");
    3957             :     json_object *poBottomRight =
    3958           4 :         json_ex_get_object_by_path(poBounds, "bottom_right");
    3959           4 :     json_object *poTopLeftLon = json_ex_get_object_by_path(poTopLeft, "lon");
    3960           4 :     json_object *poTopLeftLat = json_ex_get_object_by_path(poTopLeft, "lat");
    3961             :     json_object *poBottomRightLon =
    3962           4 :         json_ex_get_object_by_path(poBottomRight, "lon");
    3963             :     json_object *poBottomRightLat =
    3964           4 :         json_ex_get_object_by_path(poBottomRight, "lat");
    3965             : 
    3966             :     OGRErr eErr;
    3967           4 :     if (poTopLeftLon == nullptr || poTopLeftLat == nullptr ||
    3968           2 :         poBottomRightLon == nullptr || poBottomRightLat == nullptr)
    3969             :     {
    3970           2 :         m_bUseSingleQueryParams = true;
    3971             :         const auto eRet =
    3972           2 :             OGRLayer::GetExtentInternal(iGeomField, psExtent, bForce);
    3973           2 :         m_bUseSingleQueryParams = false;
    3974           2 :         return eRet;
    3975             :     }
    3976             :     else
    3977             :     {
    3978           2 :         double dfMinX = json_object_get_double(poTopLeftLon);
    3979           2 :         double dfMaxY = json_object_get_double(poTopLeftLat);
    3980           2 :         double dfMaxX = json_object_get_double(poBottomRightLon);
    3981           2 :         double dfMinY = json_object_get_double(poBottomRightLat);
    3982             : 
    3983           2 :         psExtent->MinX = dfMinX;
    3984           2 :         psExtent->MaxY = dfMaxY;
    3985           2 :         psExtent->MaxX = dfMaxX;
    3986           2 :         psExtent->MinY = dfMinY;
    3987             : 
    3988           2 :         eErr = OGRERR_NONE;
    3989             :     }
    3990           2 :     json_object_put(poResponse);
    3991             : 
    3992           2 :     return eErr;
    3993             : }
    3994             : 
    3995             : /************************************************************************/
    3996             : /*                             GetDataset()                             */
    3997             : /************************************************************************/
    3998             : 
    3999           1 : GDALDataset *OGRElasticLayer::GetDataset()
    4000             : {
    4001           1 :     return m_poDS;
    4002             : }

Generated by: LCOV version 1.14