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