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