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 40 : poGeom = OGRGeoJSONReadGeometry(
1723 : it.val, /* bHasM = */ false,
1724 : /* OGRSpatialReference* = */ nullptr)
1725 20 : .release();
1726 : }
1727 : }
1728 1 : else if (json_object_get_type(it.val) == json_type_string)
1729 : {
1730 : // Assume this is WKT
1731 1 : OGRGeometryFactory::createFromWkt(
1732 : json_object_get_string(it.val), nullptr, &poGeom);
1733 : }
1734 :
1735 64 : if (poGeom != nullptr)
1736 : {
1737 64 : poGeom->assignSpatialReference(poSRS);
1738 64 : poFeature->SetGeomFieldDirectly(oIter->second, poGeom);
1739 : }
1740 : }
1741 221 : else if (json_object_get_type(it.val) == json_type_object &&
1742 73 : (m_poDS->m_bFlattenNestedAttributes ||
1743 2 : (osPath.empty() && m_osMappingName == "FeatureCollection" &&
1744 0 : strcmp(it.key, "properties") == 0)))
1745 : {
1746 71 : BuildFeature(poFeature, it.val, osCurPath);
1747 : }
1748 79 : else if (json_object_get_type(it.val) == json_type_object &&
1749 1 : !m_poDS->m_bFlattenNestedAttributes)
1750 : {
1751 1 : if (m_aosMapToGeomFieldIndex.find(osCurPath + ".coordinates") !=
1752 2 : m_aosMapToGeomFieldIndex.end())
1753 : {
1754 1 : BuildFeature(poFeature, it.val, osCurPath);
1755 : }
1756 : }
1757 : }
1758 155 : }
1759 :
1760 : /************************************************************************/
1761 : /* AppendGroup() */
1762 : /************************************************************************/
1763 :
1764 4 : static json_object *AppendGroup(json_object *parent, const CPLString &name)
1765 : {
1766 4 : json_object *obj = json_object_new_object();
1767 4 : json_object *properties = json_object_new_object();
1768 4 : json_object_object_add(parent, name, obj);
1769 4 : json_object_object_add(obj, "properties", properties);
1770 4 : return properties;
1771 : }
1772 :
1773 : /************************************************************************/
1774 : /* AddPropertyMap() */
1775 : /************************************************************************/
1776 :
1777 18 : static json_object *AddPropertyMap(const CPLString &type)
1778 : {
1779 18 : json_object *obj = json_object_new_object();
1780 18 : json_object_object_add(obj, "type", json_object_new_string(type.c_str()));
1781 18 : return obj;
1782 : }
1783 :
1784 : /************************************************************************/
1785 : /* GetContainerForMapping() */
1786 : /************************************************************************/
1787 :
1788 : static json_object *
1789 48 : GetContainerForMapping(json_object *poContainer,
1790 : const std::vector<CPLString> &aosPath,
1791 : std::map<std::vector<CPLString>, json_object *> &oMap)
1792 : {
1793 48 : std::vector<CPLString> aosSubPath;
1794 80 : for (int j = 0; j < (int)aosPath.size() - 1; j++)
1795 : {
1796 32 : aosSubPath.push_back(aosPath[j]);
1797 : std::map<std::vector<CPLString>, json_object *>::iterator oIter =
1798 32 : oMap.find(aosSubPath);
1799 32 : if (oIter == oMap.end())
1800 : {
1801 11 : json_object *poNewContainer = json_object_new_object();
1802 11 : json_object *poProperties = json_object_new_object();
1803 11 : json_object_object_add(poContainer, aosPath[j], poNewContainer);
1804 11 : json_object_object_add(poNewContainer, "properties", poProperties);
1805 11 : oMap[aosSubPath] = poProperties;
1806 11 : poContainer = poProperties;
1807 : }
1808 : else
1809 : {
1810 21 : poContainer = oIter->second;
1811 : }
1812 : }
1813 96 : return poContainer;
1814 : }
1815 :
1816 : /************************************************************************/
1817 : /* BuildMap() */
1818 : /************************************************************************/
1819 :
1820 13 : CPLString OGRElasticLayer::BuildMap()
1821 : {
1822 13 : json_object *map = json_object_new_object();
1823 :
1824 26 : std::map<std::vector<CPLString>, json_object *> oMap;
1825 :
1826 : json_object *poMapping;
1827 13 : json_object *poMappingProperties = json_object_new_object();
1828 13 : if (m_poDS->m_nMajorVersion < 7)
1829 : {
1830 11 : poMapping = json_object_new_object();
1831 11 : json_object_object_add(map, m_osMappingName, poMapping);
1832 : }
1833 : else
1834 : {
1835 2 : poMapping = map;
1836 : }
1837 13 : json_object_object_add(poMapping, "properties", poMappingProperties);
1838 :
1839 13 : if (m_poDS->m_nMajorVersion < 7 && m_osMappingName == "FeatureCollection")
1840 : {
1841 9 : json_object_object_add(
1842 : poMappingProperties, "type",
1843 9 : AddPropertyMap(m_poDS->m_nMajorVersion >= 5 ? "text" : "string"));
1844 :
1845 18 : std::vector<CPLString> aosPath;
1846 9 : aosPath.push_back("properties");
1847 9 : aosPath.push_back("dummy");
1848 9 : GetContainerForMapping(poMappingProperties, aosPath, oMap);
1849 : }
1850 :
1851 : /* skip _id field */
1852 34 : for (int i = 1; i < m_poFeatureDefn->GetFieldCount(); i++)
1853 : {
1854 21 : OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
1855 :
1856 21 : json_object *poContainer = GetContainerForMapping(
1857 21 : poMappingProperties, m_aaosFieldPaths[i], oMap);
1858 21 : const char *pszLastComponent = m_aaosFieldPaths[i].back();
1859 :
1860 21 : const char *pszType = "string";
1861 21 : const char *pszFormat = nullptr;
1862 :
1863 21 : switch (poFieldDefn->GetType())
1864 : {
1865 4 : case OFTInteger:
1866 : case OFTIntegerList:
1867 : {
1868 4 : if (poFieldDefn->GetSubType() == OFSTBoolean)
1869 1 : pszType = "boolean";
1870 : else
1871 3 : pszType = "integer";
1872 4 : break;
1873 : }
1874 2 : case OFTInteger64:
1875 : case OFTInteger64List:
1876 2 : pszType = "long";
1877 2 : break;
1878 3 : case OFTReal:
1879 : case OFTRealList:
1880 3 : pszType = "double";
1881 3 : break;
1882 2 : case OFTDateTime:
1883 : case OFTDate:
1884 2 : pszType = "date";
1885 2 : pszFormat = "yyyy/MM/dd HH:mm:ss.SSSZZ||yyyy/MM/dd "
1886 : "HH:mm:ss.SSS||yyyy/MM/dd";
1887 2 : break;
1888 1 : case OFTTime:
1889 1 : pszType = "date";
1890 1 : pszFormat = "HH:mm:ss.SSS";
1891 1 : break;
1892 1 : case OFTBinary:
1893 1 : pszType = "binary";
1894 1 : break;
1895 8 : default:
1896 8 : break;
1897 : }
1898 :
1899 21 : bool bAnalyzed = EQUAL(pszType, "string");
1900 21 : json_object *poPropertyMap = json_object_new_object();
1901 21 : if (m_poDS->m_nMajorVersion >= 5 && EQUAL(pszType, "string"))
1902 : {
1903 0 : if (CSLFindString(m_papszNotAnalyzedFields,
1904 0 : poFieldDefn->GetNameRef()) >= 0 ||
1905 0 : (CSLCount(m_papszNotAnalyzedFields) == 1 &&
1906 0 : EQUAL(m_papszNotAnalyzedFields[0], "{ALL}")))
1907 : {
1908 0 : bAnalyzed = false;
1909 0 : pszType = "keyword";
1910 : }
1911 : else
1912 0 : pszType = "text";
1913 : }
1914 21 : json_object_object_add(poPropertyMap, "type",
1915 : json_object_new_string(pszType));
1916 21 : if (pszFormat)
1917 3 : json_object_object_add(poPropertyMap, "format",
1918 : json_object_new_string(pszFormat));
1919 41 : if (m_bStoreFields ||
1920 20 : CSLFindString(m_papszStoredFields, poFieldDefn->GetNameRef()) >= 0)
1921 2 : json_object_object_add(poPropertyMap, "store",
1922 : json_object_new_string("yes"));
1923 42 : if (m_poDS->m_nMajorVersion < 5 &&
1924 21 : (CSLFindString(m_papszNotAnalyzedFields,
1925 20 : poFieldDefn->GetNameRef()) >= 0 ||
1926 20 : (CSLCount(m_papszNotAnalyzedFields) == 1 &&
1927 13 : EQUAL(m_papszNotAnalyzedFields[0], "{ALL}"))))
1928 : {
1929 1 : bAnalyzed = false;
1930 1 : json_object_object_add(poPropertyMap, "index",
1931 : json_object_new_string("not_analyzed"));
1932 : }
1933 20 : else if (CSLFindString(m_papszNotIndexedFields,
1934 20 : poFieldDefn->GetNameRef()) >= 0)
1935 1 : json_object_object_add(poPropertyMap, "index",
1936 : json_object_new_string("no"));
1937 :
1938 28 : if (bAnalyzed && (CSLFindString(m_papszFieldsWithRawValue,
1939 7 : poFieldDefn->GetNameRef()) >= 0 ||
1940 7 : (CSLCount(m_papszFieldsWithRawValue) == 1 &&
1941 0 : EQUAL(m_papszFieldsWithRawValue[0], "{ALL}"))))
1942 : {
1943 0 : json_object *poFields = json_object_new_object();
1944 0 : json_object *poRaw = json_object_new_object();
1945 0 : json_object_object_add(poFields, "raw", poRaw);
1946 0 : if (m_poDS->m_nMajorVersion >= 5)
1947 : {
1948 0 : json_object_object_add(poRaw, "type",
1949 : json_object_new_string("keyword"));
1950 : }
1951 : else
1952 : {
1953 0 : json_object_object_add(poRaw, "type",
1954 : json_object_new_string("string"));
1955 0 : json_object_object_add(poRaw, "index",
1956 : json_object_new_string("not_analyzed"));
1957 : }
1958 0 : json_object_object_add(poPropertyMap, "fields", poFields);
1959 : }
1960 :
1961 21 : json_object_object_add(poContainer, pszLastComponent, poPropertyMap);
1962 : }
1963 :
1964 31 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
1965 : {
1966 36 : std::vector<CPLString> aosPath = m_aaosGeomFieldPaths[i];
1967 18 : bool bAddGeoJSONType = false;
1968 22 : if (m_abIsGeoPoint[i] && aosPath.size() >= 2 &&
1969 4 : aosPath.back() == "coordinates")
1970 : {
1971 4 : bAddGeoJSONType = true;
1972 4 : aosPath.resize((int)aosPath.size() - 1);
1973 : }
1974 :
1975 : json_object *poContainer =
1976 18 : GetContainerForMapping(poMappingProperties, aosPath, oMap);
1977 18 : const char *pszLastComponent = aosPath.back();
1978 :
1979 18 : if (m_abIsGeoPoint[i])
1980 : {
1981 5 : json_object *geo_point = AddPropertyMap("geo_point");
1982 5 : if (bAddGeoJSONType)
1983 : {
1984 : json_object *geometry =
1985 4 : AppendGroup(poContainer, pszLastComponent);
1986 4 : json_object_object_add(
1987 : geometry, "type",
1988 4 : AddPropertyMap(m_poDS->m_nMajorVersion >= 5 ? "text"
1989 : : "string"));
1990 4 : json_object_object_add(geometry, "coordinates", geo_point);
1991 : }
1992 : else
1993 : {
1994 1 : json_object_object_add(poContainer, pszLastComponent,
1995 : geo_point);
1996 : }
1997 5 : if (!m_osPrecision.empty())
1998 : {
1999 1 : json_object *field_data = json_object_new_object();
2000 1 : json_object_object_add(geo_point, "fielddata", field_data);
2001 1 : json_object_object_add(field_data, "format",
2002 : json_object_new_string("compressed"));
2003 1 : json_object_object_add(
2004 : field_data, "precision",
2005 : json_object_new_string(m_osPrecision.c_str()));
2006 : }
2007 : }
2008 : else
2009 : {
2010 13 : json_object *geometry = json_object_new_object();
2011 13 : json_object_object_add(poContainer, pszLastComponent, geometry);
2012 13 : json_object_object_add(geometry, "type",
2013 : json_object_new_string("geo_shape"));
2014 13 : if (!m_osPrecision.empty())
2015 1 : json_object_object_add(
2016 : geometry, "precision",
2017 : json_object_new_string(m_osPrecision.c_str()));
2018 : }
2019 : }
2020 :
2021 13 : json_object *poMeta = nullptr;
2022 13 : json_object *poGeomFields = nullptr;
2023 13 : json_object *poFields = nullptr;
2024 13 : if (!m_osFID.empty())
2025 : {
2026 5 : poMeta = json_object_new_object();
2027 5 : json_object_object_add(poMeta, "fid",
2028 : json_object_new_string(m_osFID.c_str()));
2029 : }
2030 31 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
2031 : {
2032 : OGRGeomFieldDefn *poGeomFieldDefn =
2033 18 : m_poFeatureDefn->GetGeomFieldDefn(i);
2034 18 : if (!m_abIsGeoPoint[i] && poGeomFieldDefn->GetType() != wkbUnknown)
2035 : {
2036 1 : if (poMeta == nullptr)
2037 1 : poMeta = json_object_new_object();
2038 1 : if (poGeomFields == nullptr)
2039 : {
2040 1 : poGeomFields = json_object_new_object();
2041 1 : json_object_object_add(poMeta, "geomfields", poGeomFields);
2042 : }
2043 1 : json_object_object_add(poGeomFields, poGeomFieldDefn->GetNameRef(),
2044 : json_object_new_string(OGRToOGCGeomType(
2045 : poGeomFieldDefn->GetType())));
2046 : }
2047 : }
2048 47 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
2049 : {
2050 34 : OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
2051 34 : OGRFieldType eType = poFieldDefn->GetType();
2052 34 : if (eType == OFTIntegerList || eType == OFTInteger64List ||
2053 31 : eType == OFTRealList || eType == OFTStringList)
2054 : {
2055 4 : if (poMeta == nullptr)
2056 1 : poMeta = json_object_new_object();
2057 4 : if (poFields == nullptr)
2058 : {
2059 1 : poFields = json_object_new_object();
2060 1 : json_object_object_add(poMeta, "fields", poFields);
2061 : }
2062 4 : json_object_object_add(
2063 : poFields, poFieldDefn->GetNameRef(),
2064 : json_object_new_string(OGR_GetFieldTypeName(eType)));
2065 : }
2066 : }
2067 13 : if (poMeta)
2068 7 : json_object_object_add(poMapping, "_meta", poMeta);
2069 :
2070 13 : CPLString jsonMap(json_object_to_json_string(map));
2071 13 : json_object_put(map);
2072 :
2073 : // Got personally caught by that...
2074 13 : if (CSLCount(m_papszStoredFields) == 1 &&
2075 1 : (EQUAL(m_papszStoredFields[0], "YES") ||
2076 14 : EQUAL(m_papszStoredFields[0], "TRUE")) &&
2077 0 : m_poFeatureDefn->GetFieldIndex(m_papszStoredFields[0]) < 0)
2078 : {
2079 0 : CPLError(CE_Warning, CPLE_AppDefined,
2080 : "STORED_FIELDS=%s was specified. Perhaps you meant "
2081 : "STORE_FIELDS=%s instead?",
2082 0 : m_papszStoredFields[0], m_papszStoredFields[0]);
2083 : }
2084 :
2085 26 : return jsonMap;
2086 : }
2087 :
2088 : /************************************************************************/
2089 : /* BuildGeoJSONGeometry() */
2090 : /************************************************************************/
2091 :
2092 19 : static void BuildGeoJSONGeometry(json_object *geometry,
2093 : const OGRGeometry *poGeom)
2094 : {
2095 19 : const int nPrecision = 10;
2096 19 : double dfEps = pow(10.0, -(double)nPrecision);
2097 19 : const char *pszGeomType = "";
2098 19 : switch (wkbFlatten(poGeom->getGeometryType()))
2099 : {
2100 6 : case wkbPoint:
2101 6 : pszGeomType = "point";
2102 6 : break;
2103 3 : case wkbLineString:
2104 3 : pszGeomType = "linestring";
2105 3 : break;
2106 2 : case wkbPolygon:
2107 2 : pszGeomType = "polygon";
2108 2 : break;
2109 2 : case wkbMultiPoint:
2110 2 : pszGeomType = "multipoint";
2111 2 : break;
2112 2 : case wkbMultiLineString:
2113 2 : pszGeomType = "multilinestring";
2114 2 : break;
2115 2 : case wkbMultiPolygon:
2116 2 : pszGeomType = "multipolygon";
2117 2 : break;
2118 2 : case wkbGeometryCollection:
2119 2 : pszGeomType = "geometrycollection";
2120 2 : break;
2121 0 : default:
2122 0 : break;
2123 : }
2124 19 : json_object_object_add(geometry, "type",
2125 : json_object_new_string(pszGeomType));
2126 :
2127 19 : switch (wkbFlatten(poGeom->getGeometryType()))
2128 : {
2129 6 : case wkbPoint:
2130 : {
2131 6 : const OGRPoint *poPoint = poGeom->toPoint();
2132 6 : json_object *coordinates = json_object_new_array();
2133 6 : json_object_object_add(geometry, "coordinates", coordinates);
2134 6 : json_object_array_add(coordinates,
2135 : json_object_new_double_with_precision(
2136 : poPoint->getX(), nPrecision));
2137 6 : json_object_array_add(coordinates,
2138 : json_object_new_double_with_precision(
2139 : poPoint->getY(), nPrecision));
2140 6 : break;
2141 : }
2142 :
2143 3 : case wkbLineString:
2144 : {
2145 3 : const OGRLineString *poLS = poGeom->toLineString();
2146 3 : json_object *coordinates = json_object_new_array();
2147 3 : json_object_object_add(geometry, "coordinates", coordinates);
2148 9 : for (int i = 0; i < poLS->getNumPoints(); i++)
2149 : {
2150 6 : json_object *point = json_object_new_array();
2151 6 : json_object_array_add(coordinates, point);
2152 6 : json_object_array_add(
2153 : point, json_object_new_double_with_precision(poLS->getX(i),
2154 : nPrecision));
2155 6 : json_object_array_add(
2156 : point, json_object_new_double_with_precision(poLS->getY(i),
2157 : nPrecision));
2158 : }
2159 3 : break;
2160 : }
2161 :
2162 2 : case wkbPolygon:
2163 : {
2164 2 : const OGRPolygon *poPoly = poGeom->toPolygon();
2165 2 : json_object *coordinates = json_object_new_array();
2166 2 : json_object_object_add(geometry, "coordinates", coordinates);
2167 6 : for (auto &&poLS : *poPoly)
2168 : {
2169 4 : json_object *ring = json_object_new_array();
2170 4 : json_object_array_add(coordinates, ring);
2171 20 : for (int j = 0; j < poLS->getNumPoints(); j++)
2172 : {
2173 28 : if (j > 0 &&
2174 20 : fabs(poLS->getX(j) - poLS->getX(j - 1)) < dfEps &&
2175 4 : fabs(poLS->getY(j) - poLS->getY(j - 1)) < dfEps)
2176 0 : continue;
2177 16 : json_object *point = json_object_new_array();
2178 16 : json_object_array_add(ring, point);
2179 16 : json_object_array_add(point,
2180 : json_object_new_double_with_precision(
2181 16 : poLS->getX(j), nPrecision));
2182 16 : json_object_array_add(point,
2183 : json_object_new_double_with_precision(
2184 16 : poLS->getY(j), nPrecision));
2185 : }
2186 : }
2187 2 : break;
2188 : }
2189 :
2190 2 : case wkbMultiPoint:
2191 : {
2192 2 : const OGRMultiPoint *poMP = poGeom->toMultiPoint();
2193 2 : json_object *coordinates = json_object_new_array();
2194 2 : json_object_object_add(geometry, "coordinates", coordinates);
2195 6 : for (auto &&poPoint : *poMP)
2196 : {
2197 4 : json_object *point = json_object_new_array();
2198 4 : json_object_array_add(coordinates, point);
2199 4 : json_object_array_add(point,
2200 : json_object_new_double_with_precision(
2201 : poPoint->getX(), nPrecision));
2202 4 : json_object_array_add(point,
2203 : json_object_new_double_with_precision(
2204 : poPoint->getY(), nPrecision));
2205 : }
2206 2 : break;
2207 : }
2208 :
2209 2 : case wkbMultiLineString:
2210 : {
2211 2 : const OGRMultiLineString *poMLS = poGeom->toMultiLineString();
2212 2 : json_object *coordinates = json_object_new_array();
2213 2 : json_object_object_add(geometry, "coordinates", coordinates);
2214 6 : for (auto &&poLS : *poMLS)
2215 : {
2216 4 : json_object *ls = json_object_new_array();
2217 4 : json_object_array_add(coordinates, ls);
2218 12 : for (auto &&oPoint : *poLS)
2219 : {
2220 8 : json_object *point = json_object_new_array();
2221 8 : json_object_array_add(ls, point);
2222 8 : json_object_array_add(point,
2223 : json_object_new_double_with_precision(
2224 : oPoint.getX(), nPrecision));
2225 8 : json_object_array_add(point,
2226 : json_object_new_double_with_precision(
2227 : oPoint.getY(), nPrecision));
2228 : }
2229 : }
2230 2 : break;
2231 : }
2232 :
2233 2 : case wkbMultiPolygon:
2234 : {
2235 2 : const OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
2236 2 : json_object *coordinates = json_object_new_array();
2237 2 : json_object_object_add(geometry, "coordinates", coordinates);
2238 6 : for (auto &&poPoly : *poMP)
2239 : {
2240 4 : json_object *poly = json_object_new_array();
2241 4 : json_object_array_add(coordinates, poly);
2242 10 : for (auto &&poLS : *poPoly)
2243 : {
2244 6 : json_object *ring = json_object_new_array();
2245 6 : json_object_array_add(poly, ring);
2246 30 : for (int k = 0; k < poLS->getNumPoints(); k++)
2247 : {
2248 42 : if (k > 0 &&
2249 30 : fabs(poLS->getX(k) - poLS->getX(k - 1)) < dfEps &&
2250 6 : fabs(poLS->getY(k) - poLS->getY(k - 1)) < dfEps)
2251 0 : continue;
2252 24 : json_object *point = json_object_new_array();
2253 24 : json_object_array_add(ring, point);
2254 24 : json_object_array_add(
2255 : point, json_object_new_double_with_precision(
2256 24 : poLS->getX(k), nPrecision));
2257 24 : json_object_array_add(
2258 : point, json_object_new_double_with_precision(
2259 24 : poLS->getY(k), nPrecision));
2260 : }
2261 : }
2262 : }
2263 2 : break;
2264 : }
2265 :
2266 2 : case wkbGeometryCollection:
2267 : {
2268 2 : const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
2269 2 : json_object *geometries = json_object_new_array();
2270 2 : json_object_object_add(geometry, "geometries", geometries);
2271 14 : for (auto &&poSubGeom : *poGC)
2272 : {
2273 12 : json_object *subgeom = json_object_new_object();
2274 12 : json_object_array_add(geometries, subgeom);
2275 12 : BuildGeoJSONGeometry(subgeom, poSubGeom);
2276 : }
2277 2 : break;
2278 : }
2279 :
2280 0 : default:
2281 0 : break;
2282 : }
2283 19 : }
2284 :
2285 : /************************************************************************/
2286 : /* WriteMapIfNecessary() */
2287 : /************************************************************************/
2288 :
2289 86 : OGRErr OGRElasticLayer::WriteMapIfNecessary()
2290 : {
2291 86 : if (m_bManualMapping)
2292 11 : return OGRERR_NONE;
2293 :
2294 : // Check to see if the user has elected to only write out the mapping file
2295 : // This method will only write out one layer from the vector file in cases
2296 : // where there are multiple layers
2297 75 : if (!m_osWriteMapFilename.empty())
2298 : {
2299 4 : if (m_bSerializeMapping)
2300 : {
2301 2 : m_bSerializeMapping = false;
2302 4 : CPLString map = BuildMap();
2303 :
2304 : // Write the map to a file
2305 2 : VSILFILE *f = VSIFOpenL(m_osWriteMapFilename, "wb");
2306 2 : if (f)
2307 : {
2308 2 : VSIFWriteL(map.c_str(), 1, map.length(), f);
2309 2 : VSIFCloseL(f);
2310 : }
2311 : }
2312 4 : return OGRERR_NONE;
2313 : }
2314 :
2315 : // Check to see if we have any fields to upload to this index
2316 71 : if (m_osWriteMapFilename.empty() && m_bSerializeMapping)
2317 : {
2318 11 : m_bSerializeMapping = false;
2319 11 : CPLString osURL = BuildMappingURL(true);
2320 11 : if (!m_poDS->UploadFile(osURL.c_str(), BuildMap()))
2321 : {
2322 1 : return OGRERR_FAILURE;
2323 : }
2324 : }
2325 :
2326 70 : return OGRERR_NONE;
2327 : }
2328 :
2329 : /************************************************************************/
2330 : /* GetContainerForFeature() */
2331 : /************************************************************************/
2332 :
2333 : static json_object *
2334 66 : GetContainerForFeature(json_object *poContainer,
2335 : const std::vector<CPLString> &aosPath,
2336 : std::map<std::vector<CPLString>, json_object *> &oMap)
2337 : {
2338 66 : std::vector<CPLString> aosSubPath;
2339 121 : for (int j = 0; j < (int)aosPath.size() - 1; j++)
2340 : {
2341 55 : aosSubPath.push_back(aosPath[j]);
2342 : std::map<std::vector<CPLString>, json_object *>::iterator oIter =
2343 55 : oMap.find(aosSubPath);
2344 55 : if (oIter == oMap.end())
2345 : {
2346 22 : json_object *poNewContainer = json_object_new_object();
2347 22 : json_object_object_add(poContainer, aosPath[j], poNewContainer);
2348 22 : oMap[aosSubPath] = poNewContainer;
2349 22 : poContainer = poNewContainer;
2350 : }
2351 : else
2352 : {
2353 33 : poContainer = oIter->second;
2354 : }
2355 : }
2356 132 : return poContainer;
2357 : }
2358 :
2359 : /************************************************************************/
2360 : /* BuildMappingURL() */
2361 : /************************************************************************/
2362 90 : CPLString OGRElasticLayer::BuildMappingURL(bool bMappingApi)
2363 : {
2364 : CPLString osURL =
2365 90 : CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str());
2366 90 : if (bMappingApi)
2367 11 : osURL += "/_mapping";
2368 90 : if (m_poDS->m_nMajorVersion < 7)
2369 85 : osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
2370 90 : return osURL;
2371 : }
2372 :
2373 : /************************************************************************/
2374 : /* BuildJSonFromFeature() */
2375 : /************************************************************************/
2376 :
2377 24 : CPLString OGRElasticLayer::BuildJSonFromFeature(OGRFeature *poFeature)
2378 : {
2379 :
2380 24 : CPLString fields;
2381 24 : int nJSonFieldIndex = m_poFeatureDefn->GetFieldIndex("_json");
2382 25 : if (nJSonFieldIndex >= 0 &&
2383 1 : poFeature->IsFieldSetAndNotNull(nJSonFieldIndex))
2384 : {
2385 1 : fields = poFeature->GetFieldAsString(nJSonFieldIndex);
2386 : }
2387 : else
2388 : {
2389 23 : json_object *fieldObject = json_object_new_object();
2390 :
2391 23 : if (poFeature->GetFID() >= 0 && !m_osFID.empty())
2392 : {
2393 9 : json_object_object_add(fieldObject, m_osFID.c_str(),
2394 9 : json_object_new_int64(poFeature->GetFID()));
2395 : }
2396 :
2397 46 : std::map<std::vector<CPLString>, json_object *> oMap;
2398 :
2399 47 : for (int i = 0; i < poFeature->GetGeomFieldCount(); i++)
2400 : {
2401 24 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
2402 24 : if (poGeom != nullptr && !poGeom->IsEmpty())
2403 : {
2404 13 : OGREnvelope env;
2405 13 : poGeom->getEnvelope(&env);
2406 :
2407 13 : if (m_apoCT[i] != nullptr)
2408 1 : poGeom->transform(m_apoCT[i]);
2409 12 : else if (env.MinX < -180 || env.MinY < -90 || env.MaxX > 180 ||
2410 11 : env.MaxY > 90)
2411 : {
2412 1 : CPLErrorOnce(
2413 : CE_Warning, CPLE_AppDefined,
2414 : "At least one geometry has a bounding box outside "
2415 : "of [-180,180] longitude range and/or [-90,90] "
2416 : "latitude range. Undefined behavior");
2417 : }
2418 :
2419 26 : std::vector<CPLString> aosPath = m_aaosGeomFieldPaths[i];
2420 13 : bool bAddGeoJSONType = false;
2421 17 : if (m_abIsGeoPoint[i] && aosPath.size() >= 2 &&
2422 4 : aosPath.back() == "coordinates")
2423 : {
2424 4 : bAddGeoJSONType = true;
2425 4 : aosPath.resize((int)aosPath.size() - 1);
2426 : }
2427 :
2428 : json_object *poContainer =
2429 13 : GetContainerForFeature(fieldObject, aosPath, oMap);
2430 13 : const char *pszLastComponent = aosPath.back();
2431 :
2432 13 : if (m_abIsGeoPoint[i])
2433 : {
2434 5 : json_object *coordinates = json_object_new_array();
2435 5 : const int nPrecision = 10;
2436 5 : json_object_array_add(
2437 : coordinates,
2438 : json_object_new_double_with_precision(
2439 5 : (env.MaxX + env.MinX) * 0.5, nPrecision));
2440 5 : json_object_array_add(
2441 : coordinates,
2442 : json_object_new_double_with_precision(
2443 5 : (env.MaxY + env.MinY) * 0.5, nPrecision));
2444 :
2445 5 : if (bAddGeoJSONType)
2446 : {
2447 4 : json_object *geometry = json_object_new_object();
2448 4 : json_object_object_add(poContainer, pszLastComponent,
2449 : geometry);
2450 4 : json_object_object_add(geometry, "type",
2451 : json_object_new_string("Point"));
2452 4 : json_object_object_add(geometry, "coordinates",
2453 : coordinates);
2454 : }
2455 : else
2456 : {
2457 1 : json_object_object_add(poContainer, pszLastComponent,
2458 : coordinates);
2459 : }
2460 : }
2461 : else
2462 : {
2463 8 : if (m_bGeoShapeAsGeoJSON)
2464 : {
2465 7 : json_object *geometry = json_object_new_object();
2466 7 : json_object_object_add(poContainer, pszLastComponent,
2467 : geometry);
2468 7 : BuildGeoJSONGeometry(geometry, poGeom);
2469 : }
2470 : else
2471 : {
2472 1 : char *pszWKT = nullptr;
2473 1 : poGeom->exportToWkt(&pszWKT);
2474 1 : json_object_object_add(poContainer, pszLastComponent,
2475 : json_object_new_string(pszWKT));
2476 1 : CPLFree(pszWKT);
2477 : }
2478 : }
2479 : }
2480 : }
2481 :
2482 23 : if (m_osMappingName == "FeatureCollection")
2483 : {
2484 37 : if (poFeature->GetGeomFieldCount() == 1 &&
2485 17 : poFeature->GetGeomFieldRef(0))
2486 : {
2487 7 : json_object_object_add(fieldObject, "type",
2488 : json_object_new_string("Feature"));
2489 : }
2490 :
2491 40 : std::vector<CPLString> aosPath;
2492 20 : aosPath.push_back("properties");
2493 20 : aosPath.push_back("dummy");
2494 20 : GetContainerForFeature(fieldObject, aosPath, oMap);
2495 : }
2496 :
2497 : // For every field (except _id)
2498 23 : int fieldCount = m_poFeatureDefn->GetFieldCount();
2499 115 : for (int i = 1; i < fieldCount; i++)
2500 : {
2501 92 : if (!poFeature->IsFieldSet(i))
2502 : {
2503 59 : continue;
2504 : }
2505 :
2506 : json_object *poContainer =
2507 33 : GetContainerForFeature(fieldObject, m_aaosFieldPaths[i], oMap);
2508 33 : const char *pszLastComponent = m_aaosFieldPaths[i].back();
2509 :
2510 33 : if (poFeature->IsFieldNull(i))
2511 : {
2512 1 : json_object_object_add(poContainer, pszLastComponent, nullptr);
2513 1 : continue;
2514 : }
2515 :
2516 32 : switch (m_poFeatureDefn->GetFieldDefn(i)->GetType())
2517 : {
2518 5 : case OFTInteger:
2519 5 : if (m_poFeatureDefn->GetFieldDefn(i)->GetSubType() ==
2520 : OFSTBoolean)
2521 2 : json_object_object_add(
2522 : poContainer, pszLastComponent,
2523 : json_object_new_boolean(
2524 : poFeature->GetFieldAsInteger(i)));
2525 : else
2526 3 : json_object_object_add(
2527 : poContainer, pszLastComponent,
2528 : json_object_new_int(
2529 : poFeature->GetFieldAsInteger(i)));
2530 5 : break;
2531 2 : case OFTInteger64:
2532 2 : json_object_object_add(
2533 : poContainer, pszLastComponent,
2534 : json_object_new_int64(
2535 2 : poFeature->GetFieldAsInteger64(i)));
2536 2 : break;
2537 2 : case OFTReal:
2538 2 : json_object_object_add(
2539 : poContainer, pszLastComponent,
2540 : json_object_new_double_with_significant_figures(
2541 : poFeature->GetFieldAsDouble(i), -1));
2542 2 : break;
2543 2 : case OFTIntegerList:
2544 : {
2545 2 : int nCount = 0;
2546 : const int *panValues =
2547 2 : poFeature->GetFieldAsIntegerList(i, &nCount);
2548 2 : json_object *poArray = json_object_new_array();
2549 6 : for (int j = 0; j < nCount; j++)
2550 4 : json_object_array_add(
2551 4 : poArray, json_object_new_int(panValues[j]));
2552 2 : json_object_object_add(poContainer, pszLastComponent,
2553 : poArray);
2554 2 : break;
2555 : }
2556 2 : case OFTInteger64List:
2557 : {
2558 2 : int nCount = 0;
2559 : const GIntBig *panValues =
2560 2 : poFeature->GetFieldAsInteger64List(i, &nCount);
2561 2 : json_object *poArray = json_object_new_array();
2562 6 : for (int j = 0; j < nCount; j++)
2563 4 : json_object_array_add(
2564 4 : poArray, json_object_new_int64(panValues[j]));
2565 2 : json_object_object_add(poContainer, pszLastComponent,
2566 : poArray);
2567 2 : break;
2568 : }
2569 2 : case OFTRealList:
2570 : {
2571 2 : int nCount = 0;
2572 : const double *padfValues =
2573 2 : poFeature->GetFieldAsDoubleList(i, &nCount);
2574 2 : json_object *poArray = json_object_new_array();
2575 6 : for (int j = 0; j < nCount; j++)
2576 4 : json_object_array_add(
2577 : poArray,
2578 : json_object_new_double_with_significant_figures(
2579 4 : padfValues[j], -1));
2580 2 : json_object_object_add(poContainer, pszLastComponent,
2581 : poArray);
2582 2 : break;
2583 : }
2584 2 : case OFTStringList:
2585 : {
2586 2 : char **papszValues = poFeature->GetFieldAsStringList(i);
2587 2 : json_object *poArray = json_object_new_array();
2588 6 : for (int j = 0; papszValues[j] != nullptr; j++)
2589 4 : json_object_array_add(
2590 4 : poArray, json_object_new_string(papszValues[j]));
2591 2 : json_object_object_add(poContainer, pszLastComponent,
2592 : poArray);
2593 2 : break;
2594 : }
2595 2 : case OFTBinary:
2596 : {
2597 2 : int nCount = 0;
2598 2 : GByte *pabyVal = poFeature->GetFieldAsBinary(i, &nCount);
2599 2 : char *pszVal = CPLBase64Encode(nCount, pabyVal);
2600 2 : json_object_object_add(poContainer, pszLastComponent,
2601 : json_object_new_string(pszVal));
2602 2 : CPLFree(pszVal);
2603 2 : break;
2604 : }
2605 3 : case OFTDateTime:
2606 : {
2607 3 : int nYear = 0;
2608 3 : int nMonth = 0;
2609 3 : int nDay = 0;
2610 3 : int nHour = 0;
2611 3 : int nMin = 0;
2612 3 : int nTZ = 0;
2613 3 : float fSec = 0.0f;
2614 3 : poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay,
2615 : &nHour, &nMin, &fSec, &nTZ);
2616 3 : if (nTZ == 0)
2617 : {
2618 2 : json_object_object_add(
2619 : poContainer, pszLastComponent,
2620 : json_object_new_string(CPLSPrintf(
2621 : "%04d/%02d/%02d %02d:%02d:%06.3f", nYear,
2622 : nMonth, nDay, nHour, nMin, fSec)));
2623 : }
2624 : else
2625 : {
2626 1 : const int TZOffset = std::abs(nTZ - 100) * 15;
2627 1 : const int TZHour = TZOffset / 60;
2628 1 : const int TZMinute = TZOffset - TZHour * 60;
2629 1 : json_object_object_add(
2630 : poContainer, pszLastComponent,
2631 : json_object_new_string(CPLSPrintf(
2632 : "%04d/%02d/%02d %02d:%02d:%06.3f%c%02d:%02d",
2633 : nYear, nMonth, nDay, nHour, nMin, fSec,
2634 1 : (nTZ >= 100) ? '+' : '-', TZHour, TZMinute)));
2635 : }
2636 3 : break;
2637 : }
2638 10 : default:
2639 : {
2640 10 : const char *pszVal = poFeature->GetFieldAsString(i);
2641 10 : json_object_object_add(poContainer, pszLastComponent,
2642 : json_object_new_string(pszVal));
2643 : }
2644 : }
2645 : }
2646 :
2647 : // Build the field string
2648 23 : fields = json_object_to_json_string(fieldObject);
2649 23 : json_object_put(fieldObject);
2650 : }
2651 :
2652 24 : return fields;
2653 : }
2654 :
2655 : /************************************************************************/
2656 : /* ICreateFeature() */
2657 : /************************************************************************/
2658 :
2659 20 : OGRErr OGRElasticLayer::ICreateFeature(OGRFeature *poFeature)
2660 : {
2661 20 : if (m_poDS->GetAccess() != GA_Update)
2662 : {
2663 1 : CPLError(CE_Failure, CPLE_AppDefined,
2664 : "Dataset opened in read-only mode");
2665 1 : return OGRERR_FAILURE;
2666 : }
2667 :
2668 19 : FinalizeFeatureDefn();
2669 :
2670 19 : if (WriteMapIfNecessary() != OGRERR_NONE)
2671 1 : return OGRERR_FAILURE;
2672 :
2673 18 : if (!m_osWriteMapFilename.empty())
2674 2 : return OGRERR_NONE;
2675 :
2676 16 : if (poFeature->GetFID() < 0)
2677 : {
2678 14 : if (m_nNextFID < 0)
2679 1 : m_nNextFID = GetFeatureCount(FALSE);
2680 14 : poFeature->SetFID(++m_nNextFID);
2681 : }
2682 :
2683 32 : CPLString osFields(BuildJSonFromFeature(poFeature));
2684 :
2685 16 : const char *pszId = nullptr;
2686 16 : if (poFeature->IsFieldSetAndNotNull(0) && !m_bIgnoreSourceID)
2687 1 : pszId = poFeature->GetFieldAsString(0);
2688 :
2689 : // Check to see if we're using bulk uploading
2690 16 : if (m_nBulkUpload > 0)
2691 : {
2692 : m_osBulkContent +=
2693 7 : CPLSPrintf("{\"index\" :{\"_index\":\"%s\"", m_osIndexName.c_str());
2694 7 : if (m_poDS->m_nMajorVersion < 7)
2695 : m_osBulkContent +=
2696 6 : CPLSPrintf(", \"_type\":\"%s\"", m_osMappingName.c_str());
2697 7 : if (pszId)
2698 0 : m_osBulkContent += CPLSPrintf(",\"_id\":\"%s\"", pszId);
2699 7 : m_osBulkContent += "}}\n" + osFields + "\n\n";
2700 :
2701 : // Only push the data if we are over our bulk upload limit
2702 7 : if ((int)m_osBulkContent.length() > m_nBulkUpload)
2703 : {
2704 0 : if (!PushIndex())
2705 : {
2706 0 : return OGRERR_FAILURE;
2707 : }
2708 : }
2709 : }
2710 : else
2711 : {
2712 : // Fall back to using single item upload for every feature.
2713 9 : CPLString osURL(BuildMappingURL(false));
2714 9 : if (pszId)
2715 1 : osURL += CPLSPrintf("/%s", pszId);
2716 9 : json_object *poRes = m_poDS->RunRequest(osURL, osFields);
2717 9 : if (poRes == nullptr)
2718 : {
2719 1 : return OGRERR_FAILURE;
2720 : }
2721 8 : if (pszId == nullptr)
2722 : {
2723 7 : json_object *poId = CPL_json_object_object_get(poRes, "_id");
2724 8 : if (poId != nullptr &&
2725 1 : json_object_get_type(poId) == json_type_string)
2726 : {
2727 1 : pszId = json_object_get_string(poId);
2728 1 : poFeature->SetField(0, pszId);
2729 : }
2730 : }
2731 8 : json_object_put(poRes);
2732 : }
2733 :
2734 15 : return OGRERR_NONE;
2735 : }
2736 :
2737 : /************************************************************************/
2738 : /* ISetFeature() */
2739 : /************************************************************************/
2740 :
2741 6 : OGRErr OGRElasticLayer::ISetFeature(OGRFeature *poFeature)
2742 : {
2743 6 : if (m_poDS->GetAccess() != GA_Update)
2744 : {
2745 1 : CPLError(CE_Failure, CPLE_AppDefined,
2746 : "Dataset opened in read-only mode");
2747 1 : return OGRERR_FAILURE;
2748 : }
2749 :
2750 5 : FinalizeFeatureDefn();
2751 :
2752 5 : if (!poFeature->IsFieldSetAndNotNull(0))
2753 : {
2754 1 : CPLError(CE_Failure, CPLE_AppDefined, "_id field not set");
2755 1 : return OGRERR_FAILURE;
2756 : }
2757 4 : if (poFeature->GetFID() < 0 && !m_osFID.empty())
2758 : {
2759 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid FID");
2760 0 : return OGRERR_FAILURE;
2761 : }
2762 :
2763 4 : if (WriteMapIfNecessary() != OGRERR_NONE)
2764 0 : return OGRERR_FAILURE;
2765 4 : PushIndex();
2766 :
2767 8 : CPLString osFields(BuildJSonFromFeature(poFeature));
2768 :
2769 : // TODO? we should theoretically detect if the provided _id doesn't exist
2770 : CPLString osURL(
2771 8 : CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str()));
2772 4 : if (m_poDS->m_nMajorVersion < 7)
2773 4 : osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
2774 4 : osURL += CPLSPrintf("/%s", poFeature->GetFieldAsString(0));
2775 4 : json_object *poRes = m_poDS->RunRequest(osURL, osFields);
2776 4 : if (poRes == nullptr)
2777 : {
2778 1 : return OGRERR_FAILURE;
2779 : }
2780 : // CPLDebug("ES", "SetFeature(): %s", json_object_to_json_string(poRes));
2781 3 : json_object_put(poRes);
2782 :
2783 3 : return OGRERR_NONE;
2784 : }
2785 :
2786 : /************************************************************************/
2787 : /* IUpsertFeature() */
2788 : /************************************************************************/
2789 :
2790 5 : OGRErr OGRElasticLayer::IUpsertFeature(OGRFeature *poFeature)
2791 : {
2792 5 : if (m_poDS->GetAccess() != GA_Update)
2793 : {
2794 1 : CPLError(CE_Failure, CPLE_AppDefined,
2795 : "Dataset opened in read-only mode");
2796 1 : return OGRERR_FAILURE;
2797 : }
2798 :
2799 4 : FinalizeFeatureDefn();
2800 :
2801 4 : if (WriteMapIfNecessary() != OGRERR_NONE)
2802 0 : return OGRERR_FAILURE;
2803 :
2804 4 : if (!m_osWriteMapFilename.empty())
2805 0 : return OGRERR_NONE;
2806 :
2807 4 : if (poFeature->GetFID() < 0)
2808 : {
2809 1 : if (m_nNextFID < 0)
2810 0 : m_nNextFID = GetFeatureCount(FALSE);
2811 1 : poFeature->SetFID(++m_nNextFID);
2812 : }
2813 :
2814 8 : CPLString osFields(BuildJSonFromFeature(poFeature));
2815 :
2816 4 : const char *pszId = nullptr;
2817 4 : if (poFeature->IsFieldSetAndNotNull(0))
2818 : {
2819 4 : pszId = poFeature->GetFieldAsString(0);
2820 : }
2821 : else
2822 : {
2823 0 : return OGRERR_FAILURE;
2824 : }
2825 :
2826 : // Check to see if we're using bulk uploading
2827 4 : if (m_nBulkUpload > 0)
2828 : {
2829 : m_osBulkContent +=
2830 : CPLSPrintf("{\"update\":{\"_index\":\"%s\",\"_id\":\"%s\"",
2831 2 : m_osIndexName.c_str(), pszId);
2832 2 : if (m_poDS->m_nMajorVersion < 7)
2833 : {
2834 : m_osBulkContent +=
2835 2 : CPLSPrintf(", \"_type\":\"%s\"", m_osMappingName.c_str());
2836 : }
2837 : m_osBulkContent +=
2838 2 : "}}\n{\"doc\":" + osFields + ",\"doc_as_upsert\":true}\n\n";
2839 :
2840 : // Only push the data if we are over our bulk upload limit
2841 2 : if (m_osBulkContent.length() > static_cast<size_t>(m_nBulkUpload))
2842 : {
2843 0 : if (!PushIndex())
2844 : {
2845 0 : return OGRERR_FAILURE;
2846 : }
2847 : }
2848 : }
2849 : else
2850 : {
2851 : // Fall back to using single item upload for every feature.
2852 2 : CPLString osURL(BuildMappingURL(false));
2853 2 : if (m_poDS->m_nMajorVersion < 7)
2854 : {
2855 2 : osURL += CPLSPrintf("/%s/_update", pszId);
2856 : }
2857 : else
2858 : {
2859 0 : osURL += CPLSPrintf("/_update/%s", pszId);
2860 : }
2861 :
2862 : const CPLString osUpdate =
2863 2 : CPLSPrintf("{\"doc\":%s,\"doc_as_upsert\":true}", osFields.c_str());
2864 2 : const CPLString osMethod = "POST";
2865 2 : if (!m_poDS->UploadFile(osURL, osUpdate, osMethod))
2866 : {
2867 0 : return OGRERR_FAILURE;
2868 : }
2869 : }
2870 :
2871 4 : return OGRERR_NONE;
2872 : }
2873 :
2874 : /************************************************************************/
2875 : /* PushIndex() */
2876 : /************************************************************************/
2877 :
2878 63 : bool OGRElasticLayer::PushIndex()
2879 : {
2880 63 : if (m_osBulkContent.empty())
2881 : {
2882 56 : return true;
2883 : }
2884 :
2885 14 : const bool bRet = m_poDS->UploadFile(
2886 7 : CPLSPrintf("%s/_bulk", m_poDS->GetURL()), m_osBulkContent);
2887 7 : m_osBulkContent.clear();
2888 :
2889 7 : return bRet;
2890 : }
2891 :
2892 : /************************************************************************/
2893 : /* CreateField() */
2894 : /************************************************************************/
2895 :
2896 17 : OGRErr OGRElasticLayer::CreateField(const OGRFieldDefn *poFieldDefn,
2897 : int /*bApproxOK*/)
2898 : {
2899 17 : if (m_poDS->GetAccess() != GA_Update)
2900 : {
2901 1 : CPLError(CE_Failure, CPLE_AppDefined,
2902 : "Dataset opened in read-only mode");
2903 1 : return OGRERR_FAILURE;
2904 : }
2905 :
2906 16 : FinalizeFeatureDefn();
2907 16 : ResetReading();
2908 :
2909 16 : if (m_poFeatureDefn->GetFieldIndex(poFieldDefn->GetNameRef()) >= 0)
2910 : {
2911 0 : if (!EQUAL(poFieldDefn->GetNameRef(), "_id") &&
2912 0 : !EQUAL(poFieldDefn->GetNameRef(), "_json"))
2913 : {
2914 0 : CPLError(
2915 : CE_Failure, CPLE_AppDefined,
2916 : "CreateField() called with an already existing field name: %s",
2917 : poFieldDefn->GetNameRef());
2918 : }
2919 0 : return OGRERR_FAILURE;
2920 : }
2921 :
2922 16 : std::vector<CPLString> aosPath;
2923 16 : if (m_osMappingName == "FeatureCollection")
2924 14 : aosPath.push_back("properties");
2925 :
2926 16 : if (m_bDotAsNestedField)
2927 : {
2928 : char **papszTokens =
2929 16 : CSLTokenizeString2(poFieldDefn->GetNameRef(), ".", 0);
2930 33 : for (int i = 0; papszTokens[i]; i++)
2931 17 : aosPath.push_back(papszTokens[i]);
2932 16 : CSLDestroy(papszTokens);
2933 : }
2934 : else
2935 0 : aosPath.push_back(poFieldDefn->GetNameRef());
2936 :
2937 16 : AddFieldDefn(poFieldDefn->GetNameRef(), poFieldDefn->GetType(), aosPath,
2938 : poFieldDefn->GetSubType());
2939 :
2940 16 : m_bSerializeMapping = true;
2941 :
2942 16 : return OGRERR_NONE;
2943 : }
2944 :
2945 : /************************************************************************/
2946 : /* CreateGeomField() */
2947 : /************************************************************************/
2948 :
2949 20 : OGRErr OGRElasticLayer::CreateGeomField(const OGRGeomFieldDefn *poFieldIn,
2950 : int /*bApproxOK*/)
2951 :
2952 : {
2953 20 : if (m_poDS->GetAccess() != GA_Update)
2954 : {
2955 1 : CPLError(CE_Failure, CPLE_AppDefined,
2956 : "Dataset opened in read-only mode");
2957 1 : return OGRERR_FAILURE;
2958 : }
2959 :
2960 19 : FinalizeFeatureDefn();
2961 19 : ResetReading();
2962 :
2963 19 : if (m_poFeatureDefn->GetGeomFieldIndex(poFieldIn->GetNameRef()) >= 0)
2964 : {
2965 0 : CPLError(
2966 : CE_Failure, CPLE_AppDefined,
2967 : "CreateGeomField() called with an already existing field name: %s",
2968 : poFieldIn->GetNameRef());
2969 0 : return OGRERR_FAILURE;
2970 : }
2971 :
2972 38 : OGRGeomFieldDefn oFieldDefn(poFieldIn);
2973 19 : auto poSRSOri = poFieldIn->GetSpatialRef();
2974 19 : if (poSRSOri)
2975 : {
2976 17 : auto poSRS = poSRSOri->Clone();
2977 17 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2978 17 : oFieldDefn.SetSpatialRef(poSRS);
2979 17 : poSRS->Release();
2980 : }
2981 19 : if (EQUAL(oFieldDefn.GetNameRef(), ""))
2982 0 : oFieldDefn.SetName("geometry");
2983 :
2984 19 : std::vector<CPLString> aosPath;
2985 19 : if (m_bDotAsNestedField)
2986 : {
2987 : char **papszTokens =
2988 19 : CSLTokenizeString2(oFieldDefn.GetNameRef(), ".", 0);
2989 39 : for (int i = 0; papszTokens[i]; i++)
2990 20 : aosPath.push_back(papszTokens[i]);
2991 19 : CSLDestroy(papszTokens);
2992 : }
2993 : else
2994 0 : aosPath.push_back(oFieldDefn.GetNameRef());
2995 :
2996 37 : if (m_eGeomTypeMapping == ES_GEOMTYPE_GEO_SHAPE ||
2997 35 : (m_eGeomTypeMapping == ES_GEOMTYPE_AUTO &&
2998 17 : poFieldIn->GetType() != wkbPoint))
2999 : {
3000 16 : m_abIsGeoPoint.push_back(FALSE);
3001 : }
3002 : else
3003 : {
3004 3 : m_abIsGeoPoint.push_back(TRUE);
3005 3 : aosPath.push_back("coordinates");
3006 : }
3007 :
3008 19 : m_aaosGeomFieldPaths.push_back(aosPath);
3009 :
3010 19 : m_aosMapToGeomFieldIndex[BuildPathFromArray(aosPath)] =
3011 19 : m_poFeatureDefn->GetGeomFieldCount();
3012 :
3013 19 : m_poFeatureDefn->AddGeomFieldDefn(&oFieldDefn);
3014 :
3015 19 : OGRCoordinateTransformation *poCT = nullptr;
3016 19 : if (oFieldDefn.GetSpatialRef() != nullptr)
3017 : {
3018 34 : OGRSpatialReference oSRS_WGS84;
3019 17 : oSRS_WGS84.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
3020 17 : oSRS_WGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3021 17 : if (!oSRS_WGS84.IsSame(oFieldDefn.GetSpatialRef()))
3022 : {
3023 1 : poCT = OGRCreateCoordinateTransformation(oFieldDefn.GetSpatialRef(),
3024 : &oSRS_WGS84);
3025 1 : if (poCT == nullptr)
3026 : {
3027 0 : CPLError(CE_Warning, CPLE_AppDefined,
3028 : "On-the-fly reprojection to WGS84 long/lat would be "
3029 : "needed, but instantiation of transformer failed");
3030 : }
3031 : }
3032 : }
3033 : else
3034 : {
3035 2 : CPLError(CE_Warning, CPLE_AppDefined,
3036 : "No SRS given for geometry column %s. SRS is assumed to "
3037 : "be EPSG:4326 (WGS84 long/lat)",
3038 : oFieldDefn.GetNameRef());
3039 : }
3040 :
3041 19 : m_apoCT.push_back(poCT);
3042 :
3043 19 : m_bSerializeMapping = true;
3044 :
3045 19 : return OGRERR_NONE;
3046 : }
3047 :
3048 : /************************************************************************/
3049 : /* TestCapability() */
3050 : /************************************************************************/
3051 :
3052 67 : int OGRElasticLayer::TestCapability(const char *pszCap) const
3053 : {
3054 67 : if (EQUAL(pszCap, OLCFastFeatureCount))
3055 1 : return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr;
3056 :
3057 66 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
3058 27 : return TRUE;
3059 :
3060 39 : else if (EQUAL(pszCap, OLCSequentialWrite) ||
3061 38 : EQUAL(pszCap, OLCUpsertFeature) || EQUAL(pszCap, OLCRandomWrite))
3062 1 : return m_poDS->GetAccess() == GA_Update;
3063 38 : else if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCCreateGeomField))
3064 2 : return m_poDS->GetAccess() == GA_Update;
3065 : else
3066 36 : return FALSE;
3067 : }
3068 :
3069 : /************************************************************************/
3070 : /* AddTimeoutTerminateAfterToURL() */
3071 : /************************************************************************/
3072 :
3073 16 : void OGRElasticLayer::AddTimeoutTerminateAfterToURL(CPLString &osURL)
3074 : {
3075 16 : if (!m_osSingleQueryTimeout.empty())
3076 5 : osURL += "&timeout=" + m_osSingleQueryTimeout;
3077 16 : if (!m_osSingleQueryTerminateAfter.empty())
3078 5 : osURL += "&terminate_after=" + m_osSingleQueryTerminateAfter;
3079 16 : }
3080 :
3081 : /************************************************************************/
3082 : /* GetFeatureCount() */
3083 : /************************************************************************/
3084 :
3085 12 : GIntBig OGRElasticLayer::GetFeatureCount(int bForce)
3086 : {
3087 12 : if (m_bFilterMustBeClientSideEvaluated)
3088 : {
3089 0 : m_bUseSingleQueryParams = true;
3090 0 : const auto nRet = OGRLayer::GetFeatureCount(bForce);
3091 0 : m_bUseSingleQueryParams = false;
3092 0 : return nRet;
3093 : }
3094 :
3095 12 : json_object *poResponse = nullptr;
3096 24 : CPLString osURL(CPLSPrintf("%s", m_poDS->GetURL()));
3097 24 : CPLString osFilter = "";
3098 12 : if (!m_osESSearch.empty())
3099 : {
3100 1 : if (m_osESSearch[0] != '{')
3101 0 : return OGRLayer::GetFeatureCount(bForce);
3102 1 : osURL += "/_search?pretty";
3103 1 : osFilter = "{ \"size\": 0 ";
3104 1 : if (m_osESSearch == "{}")
3105 0 : osFilter += '}';
3106 : else
3107 1 : osFilter += ", " + m_osESSearch.substr(1);
3108 : }
3109 11 : else if ((m_poSpatialFilter && m_osJSONFilter.empty()) || m_poJSONFilter)
3110 : {
3111 3 : osFilter = BuildQuery(true);
3112 3 : osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
3113 3 : if (m_poDS->m_nMajorVersion < 7)
3114 2 : osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
3115 3 : if (m_poDS->m_nMajorVersion >= 5 && m_osSingleQueryTimeout.empty())
3116 : {
3117 1 : osURL += "/_count?pretty";
3118 : }
3119 : else
3120 : {
3121 2 : osURL += "/_search?pretty";
3122 : }
3123 : }
3124 8 : else if (!m_osJSONFilter.empty())
3125 : {
3126 2 : osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
3127 2 : if (m_poDS->m_nMajorVersion < 7)
3128 1 : osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
3129 2 : osURL += "/_search?pretty";
3130 2 : osFilter = ("{ \"size\": 0, " + m_osJSONFilter.substr(1));
3131 : }
3132 : else
3133 : {
3134 6 : osURL += CPLSPrintf("/%s", m_osIndexName.c_str());
3135 6 : if (m_poDS->m_nMajorVersion < 7)
3136 5 : osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
3137 6 : if (m_osSingleQueryTimeout.empty())
3138 : {
3139 5 : osURL += "/_count?pretty";
3140 : }
3141 : else
3142 : {
3143 1 : osFilter = "{ \"size\": 0 }";
3144 1 : osURL += CPLSPrintf("/_search?pretty");
3145 : }
3146 : }
3147 12 : AddTimeoutTerminateAfterToURL(osURL);
3148 :
3149 12 : poResponse = m_poDS->RunRequest(osURL.c_str(), osFilter.c_str());
3150 :
3151 12 : json_object *poCount = json_ex_get_object_by_path(poResponse, "hits.count");
3152 12 : if (poCount == nullptr)
3153 : {
3154 : // For _search request
3155 10 : poCount = json_ex_get_object_by_path(poResponse, "hits.total");
3156 10 : if (poCount && json_object_get_type(poCount) == json_type_object)
3157 : {
3158 : // Since ES 7.0
3159 4 : poCount = json_ex_get_object_by_path(poCount, "value");
3160 : }
3161 : }
3162 12 : if (poCount == nullptr)
3163 : {
3164 : // For _count request
3165 4 : poCount = json_ex_get_object_by_path(poResponse, "count");
3166 : }
3167 12 : if (poCount == nullptr || json_object_get_type(poCount) != json_type_int)
3168 : {
3169 3 : json_object_put(poResponse);
3170 3 : CPLDebug("ES", "Cannot find hits in GetFeatureCount() response. "
3171 : "Falling back to slow implementation");
3172 3 : m_bUseSingleQueryParams = true;
3173 3 : const auto nRet = OGRLayer::GetFeatureCount(bForce);
3174 3 : m_bUseSingleQueryParams = false;
3175 3 : return nRet;
3176 : }
3177 :
3178 9 : GIntBig nCount = json_object_get_int64(poCount);
3179 9 : json_object_put(poResponse);
3180 9 : return nCount;
3181 : }
3182 :
3183 : /************************************************************************/
3184 : /* GetValue() */
3185 : /************************************************************************/
3186 :
3187 28 : json_object *OGRElasticLayer::GetValue(int nFieldIdx, swq_expr_node *poValNode)
3188 : {
3189 28 : json_object *poVal = nullptr;
3190 28 : if (poValNode->field_type == SWQ_FLOAT)
3191 1 : poVal = json_object_new_double(poValNode->float_value);
3192 27 : else if (poValNode->field_type == SWQ_INTEGER ||
3193 26 : poValNode->field_type == SWQ_INTEGER64)
3194 2 : poVal = json_object_new_int64(poValNode->int_value);
3195 25 : else if (poValNode->field_type == SWQ_STRING)
3196 23 : poVal = json_object_new_string(poValNode->string_value);
3197 2 : else if (poValNode->field_type == SWQ_TIMESTAMP)
3198 : {
3199 2 : int nYear = 0;
3200 2 : int nMonth = 0;
3201 2 : int nDay = 0;
3202 2 : int nHour = 0;
3203 2 : int nMinute = 0;
3204 2 : float fSecond = 0;
3205 4 : if (sscanf(poValNode->string_value, "%04d/%02d/%02d %02d:%02d:%f",
3206 2 : &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) >= 3 ||
3207 0 : sscanf(poValNode->string_value, "%04d-%02d-%02dT%02d:%02d:%f",
3208 : &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) >= 3)
3209 : {
3210 : OGRFieldType eType(
3211 2 : m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType());
3212 2 : if (eType == OFTDateTime)
3213 2 : poVal = json_object_new_string(
3214 : CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02.03f", nYear,
3215 : nMonth, nDay, nHour, nMinute, fSecond));
3216 0 : else if (eType == OFTDate)
3217 0 : poVal = json_object_new_string(
3218 : CPLSPrintf("%04d/%02d/%02d", nYear, nMonth, nDay));
3219 : else
3220 0 : poVal = json_object_new_string(
3221 : CPLSPrintf("%02d:%02d:%02.03f", nHour, nMinute, fSecond));
3222 : }
3223 : else
3224 : {
3225 0 : return nullptr;
3226 : }
3227 : }
3228 : else
3229 : {
3230 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unhandled type: %d",
3231 0 : poValNode->field_type);
3232 : }
3233 28 : return poVal;
3234 : }
3235 :
3236 : /************************************************************************/
3237 : /* OGRESGetFieldIndexFromSQL() */
3238 : /************************************************************************/
3239 :
3240 31 : static int OGRESGetFieldIndexFromSQL(const swq_expr_node *poNode)
3241 : {
3242 31 : if (poNode->eNodeType == SNT_COLUMN)
3243 27 : return poNode->field_index;
3244 :
3245 4 : if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_CAST &&
3246 1 : poNode->nSubExprCount >= 1 &&
3247 1 : poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN)
3248 1 : return poNode->papoSubExpr[0]->field_index;
3249 :
3250 3 : return -1;
3251 : }
3252 :
3253 : /************************************************************************/
3254 : /* TranslateSQLToFilter() */
3255 : /************************************************************************/
3256 :
3257 40 : json_object *OGRElasticLayer::TranslateSQLToFilter(swq_expr_node *poNode)
3258 : {
3259 40 : if (poNode->eNodeType == SNT_OPERATION)
3260 : {
3261 40 : int nFieldIdx = 0;
3262 40 : CPL_IGNORE_RET_VAL(nFieldIdx); // to make cppcheck happy
3263 40 : if (poNode->nOperation == SWQ_AND && poNode->nSubExprCount == 2)
3264 : {
3265 : // For AND, we can deal with a failure in one of the branch
3266 : // since client-side will do that extra filtering
3267 : json_object *poFilter1 =
3268 3 : TranslateSQLToFilter(poNode->papoSubExpr[0]);
3269 : json_object *poFilter2 =
3270 3 : TranslateSQLToFilter(poNode->papoSubExpr[1]);
3271 3 : if (poFilter1 && poFilter2)
3272 : {
3273 1 : json_object *poRet = json_object_new_object();
3274 1 : json_object *poBool = json_object_new_object();
3275 1 : json_object_object_add(poRet, "bool", poBool);
3276 1 : json_object *poMust = json_object_new_array();
3277 1 : json_object_object_add(poBool, "must", poMust);
3278 1 : json_object_array_add(poMust, poFilter1);
3279 1 : json_object_array_add(poMust, poFilter2);
3280 35 : return poRet;
3281 : }
3282 2 : else if (poFilter1)
3283 1 : return poFilter1;
3284 : else
3285 1 : return poFilter2;
3286 : }
3287 37 : else if (poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
3288 : {
3289 : json_object *poFilter1 =
3290 3 : TranslateSQLToFilter(poNode->papoSubExpr[0]);
3291 : json_object *poFilter2 =
3292 3 : TranslateSQLToFilter(poNode->papoSubExpr[1]);
3293 3 : if (poFilter1 && poFilter2)
3294 : {
3295 2 : json_object *poRet = json_object_new_object();
3296 2 : json_object *poBool = json_object_new_object();
3297 2 : json_object_object_add(poRet, "bool", poBool);
3298 2 : json_object *poShould = json_object_new_array();
3299 2 : json_object_object_add(poBool, "should", poShould);
3300 2 : json_object_array_add(poShould, poFilter1);
3301 2 : json_object_array_add(poShould, poFilter2);
3302 2 : return poRet;
3303 : }
3304 : else
3305 : {
3306 1 : json_object_put(poFilter1);
3307 1 : json_object_put(poFilter2);
3308 1 : return nullptr;
3309 : }
3310 : }
3311 34 : else if (poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
3312 : {
3313 6 : if (poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
3314 2 : poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
3315 1 : poNode->papoSubExpr[0]->nSubExprCount == 1 &&
3316 5 : poNode->papoSubExpr[0]->papoSubExpr[0]->field_index != 0 &&
3317 1 : poNode->papoSubExpr[0]->papoSubExpr[0]->field_index <
3318 1 : m_poFeatureDefn->GetFieldCount())
3319 : {
3320 1 : json_object *poRet = json_object_new_object();
3321 1 : json_object *poExists = json_object_new_object();
3322 : CPLString osFieldName(BuildPathFromArray(
3323 : m_aaosFieldPaths
3324 1 : [poNode->papoSubExpr[0]->papoSubExpr[0]->field_index]));
3325 1 : json_object_object_add(poExists, "field",
3326 : json_object_new_string(osFieldName));
3327 1 : json_object_object_add(poRet, "exists", poExists);
3328 1 : return poRet;
3329 : }
3330 : else
3331 : {
3332 : json_object *poFilter =
3333 1 : TranslateSQLToFilter(poNode->papoSubExpr[0]);
3334 1 : if (poFilter)
3335 : {
3336 1 : json_object *poRet = json_object_new_object();
3337 1 : json_object *poBool = json_object_new_object();
3338 1 : json_object_object_add(poRet, "bool", poBool);
3339 1 : json_object_object_add(poBool, "must_not", poFilter);
3340 1 : return poRet;
3341 : }
3342 : else
3343 : {
3344 0 : return nullptr;
3345 : }
3346 : }
3347 : }
3348 65 : else if (poNode->nOperation == SWQ_ISNULL &&
3349 1 : poNode->nSubExprCount == 1 &&
3350 1 : (nFieldIdx =
3351 34 : OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
3352 1 : nFieldIdx < m_poFeatureDefn->GetFieldCount())
3353 : {
3354 1 : json_object *poRet = json_object_new_object();
3355 1 : json_object *poExists = json_object_new_object();
3356 : CPLString osFieldName(
3357 1 : BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
3358 1 : json_object_object_add(poExists, "field",
3359 : json_object_new_string(osFieldName));
3360 1 : json_object *poBool = json_object_new_object();
3361 1 : json_object_object_add(poRet, "bool", poBool);
3362 1 : json_object *poMustNot = json_object_new_object();
3363 1 : json_object_object_add(poMustNot, "exists", poExists);
3364 1 : json_object_object_add(poBool, "must_not", poMustNot);
3365 1 : return poRet;
3366 : }
3367 31 : else if (poNode->nOperation == SWQ_NE)
3368 : {
3369 1 : poNode->nOperation = SWQ_EQ;
3370 1 : json_object *poFilter = TranslateSQLToFilter(poNode);
3371 1 : poNode->nOperation = SWQ_NE;
3372 1 : if (poFilter)
3373 : {
3374 1 : json_object *poRet = json_object_new_object();
3375 1 : json_object *poBool = json_object_new_object();
3376 1 : json_object_object_add(poRet, "bool", poBool);
3377 1 : json_object_object_add(poBool, "must_not", poFilter);
3378 1 : return poRet;
3379 : }
3380 : else
3381 : {
3382 0 : return nullptr;
3383 : }
3384 : }
3385 16 : else if (poNode->nOperation == SWQ_EQ && poNode->nSubExprCount == 2 &&
3386 16 : poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
3387 16 : (nFieldIdx =
3388 62 : OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) >= 0 &&
3389 13 : nFieldIdx < m_poFeatureDefn->GetFieldCount())
3390 : {
3391 13 : json_object *poVal = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
3392 13 : if (poVal == nullptr)
3393 : {
3394 0 : return nullptr;
3395 : }
3396 13 : json_object *poRet = json_object_new_object();
3397 13 : if (nFieldIdx == 0)
3398 : {
3399 1 : json_object *poIds = json_object_new_object();
3400 1 : json_object *poValues = json_object_new_array();
3401 1 : json_object_object_add(poIds, "values", poValues);
3402 1 : json_object_array_add(poValues, poVal);
3403 1 : json_object_object_add(poRet, "ids", poIds);
3404 : }
3405 : else
3406 : {
3407 12 : json_object *poTerm = json_object_new_object();
3408 : CPLString osPath(
3409 24 : BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
3410 12 : bool bNotAnalyzed = true;
3411 12 : if (poNode->papoSubExpr[1]->field_type == SWQ_STRING)
3412 : {
3413 : const char *pszFieldName =
3414 12 : m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
3415 12 : bNotAnalyzed = CSLFindString(m_papszNotAnalyzedFields,
3416 : pszFieldName) >= 0;
3417 12 : if (!bNotAnalyzed)
3418 : {
3419 3 : if (CSLFindString(m_papszFieldsWithRawValue,
3420 3 : pszFieldName) >= 0)
3421 : {
3422 1 : osPath += ".raw";
3423 1 : bNotAnalyzed = true;
3424 : }
3425 2 : else if (!m_bFilterMustBeClientSideEvaluated)
3426 : {
3427 2 : m_bFilterMustBeClientSideEvaluated = true;
3428 2 : CPLDebug("ES",
3429 : "Part or full filter will have to be "
3430 : "evaluated on "
3431 : "client side (equality test on a analyzed "
3432 : "field).");
3433 : }
3434 : }
3435 : }
3436 12 : json_object_object_add(poRet, bNotAnalyzed ? "term" : "match",
3437 : poTerm);
3438 12 : json_object_object_add(poTerm, osPath, poVal);
3439 :
3440 12 : if (!bNotAnalyzed && m_poDS->m_nMajorVersion < 2)
3441 : {
3442 0 : json_object *poNewRet = json_object_new_object();
3443 0 : json_object_object_add(poNewRet, "query", poRet);
3444 0 : poRet = poNewRet;
3445 : }
3446 : }
3447 13 : return poRet;
3448 : }
3449 50 : else if ((poNode->nOperation == SWQ_LT ||
3450 16 : poNode->nOperation == SWQ_LE ||
3451 15 : poNode->nOperation == SWQ_GT ||
3452 14 : poNode->nOperation == SWQ_GE) &&
3453 5 : poNode->nSubExprCount == 2 &&
3454 5 : poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
3455 5 : (nFieldIdx =
3456 39 : OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
3457 5 : nFieldIdx < m_poFeatureDefn->GetFieldCount())
3458 : {
3459 5 : json_object *poVal = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
3460 5 : if (poVal == nullptr)
3461 : {
3462 0 : return nullptr;
3463 : }
3464 5 : json_object *poRet = json_object_new_object();
3465 5 : json_object *poRange = json_object_new_object();
3466 5 : json_object_object_add(poRet, "range", poRange);
3467 5 : json_object *poFieldConstraint = json_object_new_object();
3468 : CPLString osFieldName(
3469 5 : BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
3470 5 : json_object_object_add(poRange, osFieldName, poFieldConstraint);
3471 9 : const char *pszOp = (poNode->nOperation == SWQ_LT) ? "lt"
3472 7 : : (poNode->nOperation == SWQ_LE) ? "lte"
3473 3 : : (poNode->nOperation == SWQ_GT)
3474 3 : ? "gt"
3475 : :
3476 : /*(poNode->nOperation == SWQ_GE) ?*/ "gte";
3477 5 : json_object_object_add(poFieldConstraint, pszOp, poVal);
3478 5 : return poRet;
3479 : }
3480 25 : else if (poNode->nOperation == SWQ_BETWEEN &&
3481 1 : poNode->nSubExprCount == 3 &&
3482 1 : poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
3483 1 : poNode->papoSubExpr[2]->eNodeType == SNT_CONSTANT &&
3484 1 : (nFieldIdx =
3485 14 : OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
3486 1 : nFieldIdx < m_poFeatureDefn->GetFieldCount())
3487 : {
3488 1 : json_object *poVal1 = GetValue(nFieldIdx, poNode->papoSubExpr[1]);
3489 1 : if (poVal1 == nullptr)
3490 : {
3491 0 : return nullptr;
3492 : }
3493 1 : json_object *poVal2 = GetValue(nFieldIdx, poNode->papoSubExpr[2]);
3494 1 : if (poVal2 == nullptr)
3495 : {
3496 0 : json_object_put(poVal1);
3497 0 : return nullptr;
3498 : }
3499 :
3500 1 : json_object *poRet = json_object_new_object();
3501 1 : json_object *poRange = json_object_new_object();
3502 1 : json_object_object_add(poRet, "range", poRange);
3503 1 : json_object *poFieldConstraint = json_object_new_object();
3504 : CPLString osFieldName(
3505 1 : BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
3506 1 : json_object_object_add(poRange, osFieldName, poFieldConstraint);
3507 1 : json_object_object_add(poFieldConstraint, "gte", poVal1);
3508 1 : json_object_object_add(poFieldConstraint, "lte", poVal2);
3509 1 : return poRet;
3510 : }
3511 4 : else if (poNode->nOperation == SWQ_IN && poNode->nSubExprCount > 1 &&
3512 4 : (nFieldIdx =
3513 19 : OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) >= 0 &&
3514 4 : nFieldIdx < m_poFeatureDefn->GetFieldCount())
3515 : {
3516 4 : bool bAllConstant = true;
3517 12 : for (int i = 1; i < poNode->nSubExprCount; i++)
3518 : {
3519 8 : if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT)
3520 : {
3521 0 : bAllConstant = false;
3522 0 : break;
3523 : }
3524 : }
3525 4 : if (bAllConstant)
3526 : {
3527 4 : json_object *poRet = json_object_new_object();
3528 4 : if (nFieldIdx == 0)
3529 : {
3530 1 : json_object *poIds = json_object_new_object();
3531 1 : json_object *poValues = json_object_new_array();
3532 1 : json_object_object_add(poIds, "values", poValues);
3533 1 : json_object_object_add(poRet, "ids", poIds);
3534 3 : for (int i = 1; i < poNode->nSubExprCount; i++)
3535 : {
3536 : json_object *poVal =
3537 2 : GetValue(nFieldIdx, poNode->papoSubExpr[i]);
3538 2 : if (poVal == nullptr)
3539 : {
3540 0 : json_object_put(poRet);
3541 0 : return nullptr;
3542 : }
3543 2 : json_object_array_add(poValues, poVal);
3544 : }
3545 : }
3546 : else
3547 : {
3548 3 : bool bNotAnalyzed = true;
3549 : CPLString osPath(
3550 3 : BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
3551 3 : if (poNode->papoSubExpr[1]->field_type == SWQ_STRING)
3552 : {
3553 : const char *pszFieldName =
3554 3 : m_poFeatureDefn->GetFieldDefn(nFieldIdx)
3555 3 : ->GetNameRef();
3556 3 : bNotAnalyzed = CSLFindString(m_papszNotAnalyzedFields,
3557 : pszFieldName) >= 0;
3558 5 : if (!bNotAnalyzed &&
3559 2 : CSLFindString(m_papszFieldsWithRawValue,
3560 : pszFieldName) >= 0)
3561 : {
3562 1 : osPath += ".raw";
3563 1 : bNotAnalyzed = true;
3564 : }
3565 :
3566 3 : if (!bNotAnalyzed &&
3567 1 : !m_bFilterMustBeClientSideEvaluated)
3568 : {
3569 1 : m_bFilterMustBeClientSideEvaluated = true;
3570 1 : CPLDebug("ES",
3571 : "Part or full filter will have to be "
3572 : "evaluated on client side (IN test on a "
3573 : "analyzed field).");
3574 : }
3575 : }
3576 :
3577 3 : if (bNotAnalyzed)
3578 : {
3579 2 : json_object *poTerms = json_object_new_object();
3580 2 : json_object_object_add(poRet, "terms", poTerms);
3581 2 : json_object *poTermsValues = json_object_new_array();
3582 2 : json_object_object_add(poTerms, osPath, poTermsValues);
3583 6 : for (int i = 1; i < poNode->nSubExprCount; i++)
3584 : {
3585 : json_object *poVal =
3586 4 : GetValue(nFieldIdx, poNode->papoSubExpr[i]);
3587 4 : if (poVal == nullptr)
3588 : {
3589 0 : json_object_put(poRet);
3590 0 : return nullptr;
3591 : }
3592 4 : json_object_array_add(poTermsValues, poVal);
3593 : }
3594 : }
3595 : else
3596 : {
3597 1 : json_object *poBool = json_object_new_object();
3598 1 : json_object_object_add(poRet, "bool", poBool);
3599 1 : json_object *poShould = json_object_new_array();
3600 1 : json_object_object_add(poBool, "should", poShould);
3601 3 : for (int i = 1; i < poNode->nSubExprCount; i++)
3602 : {
3603 : json_object *poVal =
3604 2 : GetValue(nFieldIdx, poNode->papoSubExpr[i]);
3605 2 : if (poVal == nullptr)
3606 : {
3607 0 : json_object_put(poRet);
3608 0 : return nullptr;
3609 : }
3610 2 : json_object *poShouldElt = json_object_new_object();
3611 2 : json_object *poMatch = json_object_new_object();
3612 2 : json_object_object_add(poShouldElt, "match",
3613 : poMatch);
3614 2 : json_object_object_add(poMatch, osPath, poVal);
3615 :
3616 2 : if (m_poDS->m_nMajorVersion < 2)
3617 : {
3618 : json_object *poNewShouldElt =
3619 0 : json_object_new_object();
3620 0 : json_object_object_add(poNewShouldElt, "query",
3621 : poShouldElt);
3622 0 : poShouldElt = poNewShouldElt;
3623 : }
3624 2 : json_object_array_add(poShould, poShouldElt);
3625 : }
3626 : }
3627 : }
3628 4 : return poRet;
3629 : }
3630 : }
3631 17 : else if ((poNode->nOperation == SWQ_LIKE ||
3632 3 : poNode->nOperation ==
3633 4 : SWQ_ILIKE) && // ES actual semantics doesn't match
3634 : // exactly either...
3635 4 : poNode->nSubExprCount >= 2 &&
3636 4 : (nFieldIdx =
3637 18 : OGRESGetFieldIndexFromSQL(poNode->papoSubExpr[0])) > 0 &&
3638 4 : nFieldIdx < m_poFeatureDefn->GetFieldCount())
3639 : {
3640 4 : char chEscape = '\0';
3641 4 : if (poNode->nSubExprCount == 3)
3642 1 : chEscape = poNode->papoSubExpr[2]->string_value[0];
3643 4 : const char *pszPattern = poNode->papoSubExpr[1]->string_value;
3644 : const char *pszFieldName =
3645 4 : m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
3646 : bool bNotAnalyzed =
3647 4 : CSLFindString(m_papszNotAnalyzedFields, pszFieldName) >= 0;
3648 4 : CPLString osPath(BuildPathFromArray(m_aaosFieldPaths[nFieldIdx]));
3649 7 : if (!bNotAnalyzed &&
3650 3 : CSLFindString(m_papszFieldsWithRawValue, pszFieldName) >= 0)
3651 : {
3652 1 : osPath += ".raw";
3653 1 : bNotAnalyzed = true;
3654 : }
3655 :
3656 4 : if (strchr(pszPattern, '*') || strchr(pszPattern, '?'))
3657 : {
3658 1 : CPLDebug("ES", "Cannot handle * or ? in LIKE pattern");
3659 : }
3660 3 : else if (!bNotAnalyzed)
3661 : {
3662 1 : if (!m_bFilterMustBeClientSideEvaluated)
3663 : {
3664 1 : m_bFilterMustBeClientSideEvaluated = true;
3665 1 : CPLDebug(
3666 : "ES",
3667 : "Part or full filter will have to be evaluated on "
3668 : "client side (wildcard test on a analyzed field).");
3669 : }
3670 : }
3671 : else
3672 : {
3673 2 : CPLString osUnescaped;
3674 8 : for (int i = 0; pszPattern[i] != '\0'; ++i)
3675 : {
3676 6 : if (chEscape == pszPattern[i])
3677 : {
3678 1 : if (pszPattern[i + 1] == '\0')
3679 0 : break;
3680 1 : osUnescaped += pszPattern[i + 1];
3681 1 : i++;
3682 : }
3683 5 : else if (pszPattern[i] == '%')
3684 : {
3685 2 : osUnescaped += '*';
3686 : }
3687 3 : else if (pszPattern[i] == '_')
3688 : {
3689 2 : osUnescaped += '?';
3690 : }
3691 : else
3692 : {
3693 1 : osUnescaped += pszPattern[i];
3694 : }
3695 : }
3696 2 : json_object *poRet = json_object_new_object();
3697 2 : json_object *poWildcard = json_object_new_object();
3698 2 : json_object_object_add(poRet, "wildcard", poWildcard);
3699 2 : json_object_object_add(poWildcard, osPath,
3700 : json_object_new_string(osUnescaped));
3701 2 : return poRet;
3702 : }
3703 : }
3704 : }
3705 :
3706 5 : if (!m_bFilterMustBeClientSideEvaluated)
3707 : {
3708 4 : m_bFilterMustBeClientSideEvaluated = true;
3709 4 : CPLDebug("ES", "Part or full filter will have to be evaluated on "
3710 : "client side.");
3711 : }
3712 5 : return nullptr;
3713 : }
3714 :
3715 : /************************************************************************/
3716 : /* SetAttributeFilter() */
3717 : /************************************************************************/
3718 :
3719 37 : OGRErr OGRElasticLayer::SetAttributeFilter(const char *pszFilter)
3720 : {
3721 37 : m_bFilterMustBeClientSideEvaluated = false;
3722 37 : if (pszFilter != nullptr && pszFilter[0] == '{')
3723 : {
3724 2 : if (!m_osESSearch.empty())
3725 : {
3726 0 : CPLError(CE_Failure, CPLE_AppDefined,
3727 : "Setting an Elasticsearch filter on a resulting layer "
3728 : "is not supported");
3729 0 : return OGRERR_FAILURE;
3730 : }
3731 2 : OGRLayer::SetAttributeFilter(nullptr);
3732 2 : m_osJSONFilter = pszFilter;
3733 2 : return OGRERR_NONE;
3734 : }
3735 : else
3736 : {
3737 35 : m_osJSONFilter.clear();
3738 35 : json_object_put(m_poJSONFilter);
3739 35 : m_poJSONFilter = nullptr;
3740 35 : OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
3741 35 : if (eErr == OGRERR_NONE && m_poAttrQuery != nullptr)
3742 : {
3743 : swq_expr_node *poNode =
3744 26 : reinterpret_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
3745 26 : m_poJSONFilter = TranslateSQLToFilter(poNode);
3746 : }
3747 35 : return eErr;
3748 : }
3749 : }
3750 :
3751 : /************************************************************************/
3752 : /* ClampEnvelope() */
3753 : /************************************************************************/
3754 :
3755 7 : void OGRElasticLayer::ClampEnvelope(OGREnvelope &sEnvelope)
3756 : {
3757 7 : if (sEnvelope.MinX < -180)
3758 1 : sEnvelope.MinX = -180;
3759 7 : if (sEnvelope.MinX > 180)
3760 0 : sEnvelope.MinX = 180;
3761 :
3762 7 : if (sEnvelope.MinY < -90)
3763 1 : sEnvelope.MinY = -90;
3764 7 : if (sEnvelope.MinY > 90)
3765 0 : sEnvelope.MinY = 90;
3766 :
3767 7 : if (sEnvelope.MaxX > 180)
3768 1 : sEnvelope.MaxX = 180;
3769 7 : if (sEnvelope.MaxX < -180)
3770 0 : sEnvelope.MaxX = -180;
3771 :
3772 7 : if (sEnvelope.MaxY > 90)
3773 1 : sEnvelope.MaxY = 90;
3774 7 : if (sEnvelope.MaxY < -90)
3775 0 : sEnvelope.MaxY = -90;
3776 7 : }
3777 :
3778 : /************************************************************************/
3779 : /* ISetSpatialFilter() */
3780 : /************************************************************************/
3781 :
3782 14 : OGRErr OGRElasticLayer::ISetSpatialFilter(int iGeomField,
3783 : const OGRGeometry *poGeomIn)
3784 :
3785 : {
3786 14 : FinalizeFeatureDefn();
3787 :
3788 14 : m_iGeomFieldFilter = iGeomField;
3789 :
3790 14 : InstallFilter(poGeomIn);
3791 :
3792 14 : json_object_put(m_poSpatialFilter);
3793 14 : m_poSpatialFilter = nullptr;
3794 :
3795 14 : if (poGeomIn == nullptr)
3796 9 : return OGRERR_NONE;
3797 :
3798 5 : if (!m_osESSearch.empty())
3799 : {
3800 0 : CPLError(
3801 : CE_Failure, CPLE_AppDefined,
3802 : "Setting a spatial filter on a resulting layer is not supported");
3803 0 : return OGRERR_FAILURE;
3804 : }
3805 :
3806 5 : OGREnvelope sEnvelope;
3807 5 : poGeomIn->getEnvelope(&sEnvelope);
3808 5 : ClampEnvelope(sEnvelope);
3809 :
3810 5 : if (sEnvelope.MinX == -180 && sEnvelope.MinY == -90 &&
3811 1 : sEnvelope.MaxX == 180 && sEnvelope.MaxY == 90)
3812 : {
3813 1 : return OGRERR_NONE;
3814 : }
3815 :
3816 4 : m_poSpatialFilter = json_object_new_object();
3817 :
3818 4 : if (m_abIsGeoPoint[iGeomField])
3819 : {
3820 1 : json_object *geo_bounding_box = json_object_new_object();
3821 1 : json_object_object_add(m_poSpatialFilter, "geo_bounding_box",
3822 : geo_bounding_box);
3823 :
3824 2 : CPLString osPath = BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]);
3825 :
3826 1 : json_object *field = json_object_new_object();
3827 1 : json_object_object_add(geo_bounding_box, osPath.c_str(), field);
3828 :
3829 1 : json_object *top_left = json_object_new_object();
3830 1 : json_object_object_add(field, "top_left", top_left);
3831 1 : json_object_object_add(
3832 : top_left, "lat",
3833 : json_object_new_double_with_precision(sEnvelope.MaxY, 6));
3834 1 : json_object_object_add(
3835 : top_left, "lon",
3836 : json_object_new_double_with_precision(sEnvelope.MinX, 6));
3837 :
3838 1 : json_object *bottom_right = json_object_new_object();
3839 1 : json_object_object_add(field, "bottom_right", bottom_right);
3840 1 : json_object_object_add(
3841 : bottom_right, "lat",
3842 : json_object_new_double_with_precision(sEnvelope.MinY, 6));
3843 1 : json_object_object_add(
3844 : bottom_right, "lon",
3845 : json_object_new_double_with_precision(sEnvelope.MaxX, 6));
3846 : }
3847 : else
3848 : {
3849 3 : json_object *geo_shape = json_object_new_object();
3850 3 : json_object_object_add(m_poSpatialFilter, "geo_shape", geo_shape);
3851 :
3852 6 : CPLString osPath = BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]);
3853 :
3854 3 : json_object *field = json_object_new_object();
3855 3 : json_object_object_add(geo_shape, osPath.c_str(), field);
3856 :
3857 3 : json_object *shape = json_object_new_object();
3858 3 : json_object_object_add(field, "shape", shape);
3859 :
3860 3 : json_object_object_add(shape, "type",
3861 : json_object_new_string("envelope"));
3862 :
3863 3 : json_object *coordinates = json_object_new_array();
3864 3 : json_object_object_add(shape, "coordinates", coordinates);
3865 :
3866 3 : json_object *top_left = json_object_new_array();
3867 3 : json_object_array_add(
3868 : top_left, json_object_new_double_with_precision(sEnvelope.MinX, 6));
3869 3 : json_object_array_add(
3870 : top_left, json_object_new_double_with_precision(sEnvelope.MaxY, 6));
3871 3 : json_object_array_add(coordinates, top_left);
3872 :
3873 3 : json_object *bottom_right = json_object_new_array();
3874 3 : json_object_array_add(
3875 : bottom_right,
3876 : json_object_new_double_with_precision(sEnvelope.MaxX, 6));
3877 3 : json_object_array_add(
3878 : bottom_right,
3879 : json_object_new_double_with_precision(sEnvelope.MinY, 6));
3880 3 : json_object_array_add(coordinates, bottom_right);
3881 : }
3882 :
3883 4 : return OGRERR_NONE;
3884 : }
3885 :
3886 : /************************************************************************/
3887 : /* IGetExtent() */
3888 : /************************************************************************/
3889 :
3890 5 : OGRErr OGRElasticLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
3891 : bool bForce)
3892 : {
3893 5 : FinalizeFeatureDefn();
3894 :
3895 : // geo_shape aggregation is only available since ES 7.8, but only with XPack
3896 : // for now
3897 6 : if (!m_abIsGeoPoint[iGeomField] &&
3898 1 : !(m_poDS->m_nMajorVersion > 7 ||
3899 1 : (m_poDS->m_nMajorVersion == 7 && m_poDS->m_nMinorVersion >= 8)))
3900 : {
3901 1 : m_bUseSingleQueryParams = true;
3902 1 : const auto eRet = OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
3903 1 : m_bUseSingleQueryParams = false;
3904 1 : return eRet;
3905 : }
3906 :
3907 : CPLString osFilter = CPLSPrintf(
3908 : "{ \"size\": 0, \"aggs\" : { \"bbox\" : { \"geo_bounds\" : { \"field\" "
3909 : ": \"%s\" } } } }",
3910 8 : BuildPathFromArray(m_aaosGeomFieldPaths[iGeomField]).c_str());
3911 : CPLString osURL =
3912 8 : CPLSPrintf("%s/%s", m_poDS->GetURL(), m_osIndexName.c_str());
3913 4 : if (m_poDS->m_nMajorVersion < 7)
3914 3 : osURL += CPLSPrintf("/%s", m_osMappingName.c_str());
3915 4 : osURL += "/_search?pretty";
3916 4 : AddTimeoutTerminateAfterToURL(osURL);
3917 :
3918 4 : CPLPushErrorHandler(CPLQuietErrorHandler);
3919 : json_object *poResponse =
3920 4 : m_poDS->RunRequest(osURL.c_str(), osFilter.c_str());
3921 4 : CPLPopErrorHandler();
3922 4 : if (poResponse == nullptr)
3923 : {
3924 2 : const char *pszLastErrorMsg = CPLGetLastErrorMsg();
3925 2 : if (!m_abIsGeoPoint[iGeomField] &&
3926 0 : strstr(pszLastErrorMsg, "Fielddata is not supported on field") !=
3927 : nullptr)
3928 : {
3929 0 : CPLDebug("ES",
3930 : "geo_bounds aggregation failed, likely because of lack "
3931 : "of XPack. Using client-side method");
3932 0 : CPLErrorReset();
3933 : }
3934 : else
3935 : {
3936 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", pszLastErrorMsg);
3937 : }
3938 : }
3939 :
3940 : json_object *poBounds =
3941 4 : json_ex_get_object_by_path(poResponse, "aggregations.bbox.bounds");
3942 4 : json_object *poTopLeft = json_ex_get_object_by_path(poBounds, "top_left");
3943 : json_object *poBottomRight =
3944 4 : json_ex_get_object_by_path(poBounds, "bottom_right");
3945 4 : json_object *poTopLeftLon = json_ex_get_object_by_path(poTopLeft, "lon");
3946 4 : json_object *poTopLeftLat = json_ex_get_object_by_path(poTopLeft, "lat");
3947 : json_object *poBottomRightLon =
3948 4 : json_ex_get_object_by_path(poBottomRight, "lon");
3949 : json_object *poBottomRightLat =
3950 4 : json_ex_get_object_by_path(poBottomRight, "lat");
3951 :
3952 : OGRErr eErr;
3953 4 : if (poTopLeftLon == nullptr || poTopLeftLat == nullptr ||
3954 2 : poBottomRightLon == nullptr || poBottomRightLat == nullptr)
3955 : {
3956 2 : m_bUseSingleQueryParams = true;
3957 2 : const auto eRet = OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
3958 2 : m_bUseSingleQueryParams = false;
3959 2 : return eRet;
3960 : }
3961 : else
3962 : {
3963 2 : double dfMinX = json_object_get_double(poTopLeftLon);
3964 2 : double dfMaxY = json_object_get_double(poTopLeftLat);
3965 2 : double dfMaxX = json_object_get_double(poBottomRightLon);
3966 2 : double dfMinY = json_object_get_double(poBottomRightLat);
3967 :
3968 2 : psExtent->MinX = dfMinX;
3969 2 : psExtent->MaxY = dfMaxY;
3970 2 : psExtent->MaxX = dfMaxX;
3971 2 : psExtent->MinY = dfMinY;
3972 :
3973 2 : eErr = OGRERR_NONE;
3974 : }
3975 2 : json_object_put(poResponse);
3976 :
3977 2 : return eErr;
3978 : }
3979 :
3980 : /************************************************************************/
3981 : /* GetDataset() */
3982 : /************************************************************************/
3983 :
3984 1 : GDALDataset *OGRElasticLayer::GetDataset()
3985 : {
3986 1 : return m_poDS;
3987 : }
|