Coverage Report

Created: 2026-03-30 09:00

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