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