1 /******************************************************************************
2  *
3  * Project:  MongoDB Translator
4  * Purpose:  Implements OGRMongoDBDriver.
5  * Author:   Even Rouault, even.rouault at spatialys.com
6  *
7  ******************************************************************************
8  * Copyright (c) 2014-2019, Even Rouault <even dot rouault at spatialys dot com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
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 "mongocxxv3_headers.h"
31 
32 #include "cpl_time.h"
33 #include "gdal_priv.h"
34 #include "ogrsf_frmts.h"
35 #include "ogr_p.h"
36 
37 #include <limits>
38 #include <map>
39 #include <memory>
40 #include <mutex>
41 #include <vector>
42 
43 extern "C" void CPL_DLL RegisterOGRMongoDBv3();
44 
45 // g++ -Wall -Wextra -std=c++11 -fPIC -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -I$HOME/install-mongocxx-3.4.0/include/bsoncxx/v_noabi  -I$HOME/install-mongocxx-3.4.0/include/mongocxx/v_noabi ogr/ogrsf_frmts/mongodbv3/ogrmongodbv3driver.cpp -shared -o ogr_MongoDBv3.so -L$HOME/install-mongocxx-3.4.0/lib -lmongocxx -lgdal
46 
47 using bsoncxx::builder::basic::kvp;
48 
49 static mongocxx::instance* g_pInst = nullptr;
50 static bool g_bCanInstantiateMongo = true;
51 
52 namespace {
53 typedef struct _IntOrMap IntOrMap;
54 
55 struct _IntOrMap
56 {
57     int bIsMap;
58     union
59     {
60         int nField;
61         std::map< CPLString, IntOrMap*>* poMap;
62     } u;
63 };
64 } // namespace
65 
66 class OGRMongoDBv3Layer;
67 
68 class OGRMongoDBv3Dataset final: public GDALDataset
69 {
70         friend class OGRMongoDBv3Layer;
71 
72         mongocxx::client                                m_oConn{};
73         CPLString                                       m_osDatabase{};
74         std::vector<std::unique_ptr<OGRMongoDBv3Layer>> m_apoLayers{};
75         bool                                            m_bFlattenNestedAttributes = false;
76         int                                             m_nBatchSize = 0;
77         int                                             m_nFeatureCountToEstablishFeatureDefn = 0;
78         bool                                            m_bJSonField = false;
79         CPLString                                       m_osFID{};
80         bool                                            m_bUseOGRMetadata = true;
81         bool                                            m_bBulkInsert = true;
82 
83         void                        CreateLayers(mongocxx::database& db);
84 
85     public:
86         OGRMongoDBv3Dataset() = default;
87 
GetLayerCount()88         int       GetLayerCount() override { return static_cast<int>(m_apoLayers.size()); }
89         OGRLayer* GetLayer(int) override;
90         OGRLayer* GetLayerByName(const char* pszLayerName) override;
91 
92         OGRLayer *  ExecuteSQL( const char *pszSQLCommand,
93                                             OGRGeometry *poSpatialFilter,
94                                             const char *pszDialect ) override;
95         void        ReleaseResultSet( OGRLayer * poLayer ) override;
96 
97         OGRLayer* ICreateLayer( const char *pszName,
98                                   OGRSpatialReference *poSpatialRef,
99                                   OGRwkbGeometryType eGType,
100                                   char ** papszOptions ) override;
101         OGRErr    DeleteLayer( int iLayer ) override;
102         int       TestCapability( const char * pszCap ) override;
103 
104 
105         bool Open(GDALOpenInfo* poOpenInfo);
106 };
107 
108 class OGRMongoDBv3Layer final: public OGRLayer
109 {
110         friend class OGRMongoDBv3Dataset;
111 
112         OGRMongoDBv3Dataset*                  m_poDS = nullptr;
113         OGRFeatureDefn*                       m_poFeatureDefn = nullptr;
114         bool                                  m_bHasEstablishedFeatureDefn = false;
115         mongocxx::database                    m_oDb{};
116         mongocxx::collection                  m_oColl{};
117         CPLString                             m_osFID{};
118         bsoncxx::document::value              m_oQueryAttr{bsoncxx::builder::basic::make_document()};
119         bsoncxx::document::value              m_oQuerySpat{bsoncxx::builder::basic::make_document()};
120         bool                                  m_bLayerMetadataUpdatable = false;
121         bool                                  m_bUpdateLayerMetadata = false;
122         bool                                  m_bDotAsNestedField = true;
123         bool                                  m_bIgnoreSourceID = false;
124         bool                                  m_bCreateSpatialIndex = true;
125         GIntBig                                m_nIndex = 0;
126         GIntBig                                m_nNextFID = 0;
127         std::unique_ptr<mongocxx::cursor>      m_poCursor{};
128         std::unique_ptr<mongocxx::cursor::iterator> m_poIterator{};
129 
130         std::vector< std::vector<CPLString> > m_aaosFieldPaths{};
131 
132         std::vector< std::vector<CPLString> >    m_aaosGeomFieldPaths{};
133         std::vector< CPLString >                 m_aosGeomIndexes{};
134         std::vector< std::unique_ptr<OGRCoordinateTransformation> > m_apoCT{};
135 
136         std::map< CPLString, CPLString> CollectGeomIndices();
137         bool                            ReadOGRMetadata(std::map< CPLString, CPLString>& oMapIndices);
138         void                            EstablishFeatureDefn();
139         void                            WriteOGRMetadata();
140         void                            AddOrUpdateField(const char* pszAttrName,
141                                                         const bsoncxx::document::element& elt,
142                                                         char chNestedAttributeSeparator,
143                                                         std::vector<CPLString>& aosPaths,
144                                                         std::map< CPLString, CPLString>& oMapIndices);
145         std::unique_ptr<OGRFeature>     Translate(const bsoncxx::document::view& doc);
146         bsoncxx::document::value        BuildQuery();
147 
148         void                     SerializeField(bsoncxx::builder::basic::document& b,
149                                                 OGRFeature *poFeature,
150                                                 int iField,
151                                                 const char* pszJSonField);
152         void                     SerializeGeometry(bsoncxx::builder::basic::document& b,
153                                                     OGRGeometry* poGeom, int iField,
154                                                     const char* pszJSonField);
155         void                     SerializeRecursive(bsoncxx::builder::basic::document& b,
156                                         OGRFeature *poFeature,
157                                         std::map< CPLString, IntOrMap*>& aoMap );
158         static void                     InsertInMap(IntOrMap* rootMap,
159                                                 std::map< std::vector<CPLString>, IntOrMap*>& aoMap,
160                                                 const std::vector<CPLString>& aosFieldPathFull,
161                                                 int nField);
162         bsoncxx::document::value BuildBSONObjFromFeature(OGRFeature* poFeature, bool bUpdate);
163         std::vector<bsoncxx::document::value> m_aoDocsToInsert{};
164 
165     public:
166             OGRMongoDBv3Layer(OGRMongoDBv3Dataset* poDS,
167                               const std::string& osDbName,
168                               const std::string& osCollection);
169             ~OGRMongoDBv3Layer() override;
170 
171             void            ResetReading() override;
172             const char*     GetFIDColumn() override;
173             OGRFeature*     GetNextFeature() override;
174             OGRFeature*     GetFeature(GIntBig nFID) override;
175             OGRErr          DeleteFeature(GIntBig nFID) override;
176             GIntBig         GetFeatureCount(int bForce) override;
177             OGRErr          SetAttributeFilter(const char* pszFilter) override;
SetSpatialFilter(OGRGeometry * poGeom)178             void            SetSpatialFilter( OGRGeometry *poGeom ) override { SetSpatialFilter(0, poGeom); }
179             void            SetSpatialFilter( int iGeomField, OGRGeometry *poGeom ) override;
180             int             TestCapability( const char* pszCap ) override;
181             OGRFeatureDefn* GetLayerDefn() override;
182             OGRErr          CreateField( OGRFieldDefn *poFieldIn, int ) override;
183             OGRErr          CreateGeomField( OGRGeomFieldDefn *poFieldIn, int ) override;
184             OGRErr          ICreateFeature( OGRFeature *poFeature ) override;
185             OGRErr          ISetFeature( OGRFeature *poFeature ) override;
186 
187             OGRErr          SyncToDisk() override;
188 
189 };
190 
191 /************************************************************************/
192 /*                         OGRMongoDBv3Layer()                          */
193 /************************************************************************/
194 
OGRMongoDBv3Layer(OGRMongoDBv3Dataset * poDS,const std::string & osDbName,const std::string & osCollection)195 OGRMongoDBv3Layer::OGRMongoDBv3Layer(OGRMongoDBv3Dataset* poDS,
196                                      const std::string& osDbName,
197                                      const std::string& osCollection) :
198     m_poDS(poDS)
199 {
200     CPLString osLayerName;
201     m_oDb = m_poDS->m_oConn[osDbName];
202     m_oColl = m_oDb[osCollection];
203     if( m_poDS->m_osDatabase == osDbName )
204         osLayerName = osCollection;
205     else
206         osLayerName = osDbName + "." + osCollection;
207     m_poFeatureDefn = new OGRFeatureDefn(osLayerName);
208     m_poFeatureDefn->Reference();
209     m_poFeatureDefn->SetGeomType(wkbNone);
210     SetDescription(m_poFeatureDefn->GetName());
211 
212     OGRFieldDefn oFieldDefn("_id", OFTString);
213     std::vector<CPLString> aosPath;
214     aosPath.push_back("_id");
215     m_aaosFieldPaths.push_back(aosPath);
216     m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
217 }
218 
219 /************************************************************************/
220 /*                         ~OGRMongoDBv3Layer()                         */
221 /************************************************************************/
222 
~OGRMongoDBv3Layer()223 OGRMongoDBv3Layer::~OGRMongoDBv3Layer()
224 {
225     OGRMongoDBv3Layer::SyncToDisk();
226 
227     if( m_poFeatureDefn )
228         m_poFeatureDefn->Release();
229 }
230 
231 /************************************************************************/
232 /*                            WriteOGRMetadata()                        */
233 /************************************************************************/
234 
WriteOGRMetadata()235 void OGRMongoDBv3Layer::WriteOGRMetadata()
236 {
237     //CPLDebug("MongoDBv3", "WriteOGRMetadata(%s)", m_poFeatureDefn->GetName());
238     if( !m_bUpdateLayerMetadata )
239         return;
240     m_bUpdateLayerMetadata = false;
241 
242     try
243     {
244         bsoncxx::builder::basic::document b;
245 
246         b.append(kvp("layer", m_oColl.name()));
247 
248         if( !m_osFID.empty() )
249         {
250             b.append(kvp("fid", m_osFID));
251         }
252 
253         bsoncxx::builder::basic::array fields;
254 
255         CPLAssert( static_cast<int>(m_aaosFieldPaths.size()) == m_poFeatureDefn->GetFieldCount() );
256         for(int i=1;i<m_poFeatureDefn->GetFieldCount();i++)
257         {
258             OGRFieldDefn* poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
259             const char* pszFieldName = poFieldDefn->GetNameRef();
260             if( EQUAL(pszFieldName, "_json") )
261                 continue;
262             bsoncxx::builder::basic::array path;
263             for(int j=0;j<static_cast<int>(m_aaosFieldPaths[i].size());j++)
264                 path.append(m_aaosFieldPaths[i][j]);
265             bsoncxx::builder::basic::document rec;
266             rec.append(kvp("name", pszFieldName));
267             OGRFieldType eType = poFieldDefn->GetType();
268             rec.append(kvp("type", OGR_GetFieldTypeName(eType)));
269             if( eType == OFTInteger && poFieldDefn->GetSubType() == OFSTBoolean )
270             {
271                 rec.append(kvp("subtype", "Boolean"));
272             }
273             rec.append(kvp("path", path.extract()));
274             fields.append(rec.extract());
275         }
276         b.append(kvp("fields", fields.extract()));
277 
278         bsoncxx::builder::basic::array geomfields;
279         CPLAssert( static_cast<int>(m_aaosGeomFieldPaths.size()) == m_poFeatureDefn->GetGeomFieldCount() );
280         for(int i=0;i<m_poFeatureDefn->GetGeomFieldCount();i++)
281         {
282             OGRGeomFieldDefn* poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
283             const char* pszFieldName = poGeomFieldDefn->GetNameRef();
284             bsoncxx::builder::basic::array path;
285             for(int j=0;j<static_cast<int>(m_aaosGeomFieldPaths[i].size());j++)
286                 path.append(m_aaosGeomFieldPaths[i][j]);
287             const char* pszGeomType = OGRToOGCGeomType(poGeomFieldDefn->GetType());
288             bsoncxx::builder::basic::document rec;
289             rec.append(kvp("name", pszFieldName));
290             rec.append(kvp("type", pszGeomType));
291             rec.append(kvp("path", path.extract()));
292             geomfields.append(rec.extract());
293         }
294         b.append(kvp("geomfields", geomfields.extract()));
295 
296         {
297             bsoncxx::builder::basic::document filter{};
298             filter.append(kvp("layer", m_oColl.name()));
299             m_oDb["_ogr_metadata"].find_one_and_delete(filter.extract());
300         }
301 
302         m_oDb["_ogr_metadata"].insert_one( b.extract() );
303     }
304     catch( const std::exception &ex )
305     {
306         CPLError(CE_Failure, CPLE_AppDefined, "%s: %s",
307                  "WriteOGRMetadata()", ex.what());
308     }
309 }
310 
311 /************************************************************************/
312 /*                             SyncToDisk()                             */
313 /************************************************************************/
314 
SyncToDisk()315 OGRErr OGRMongoDBv3Layer::SyncToDisk()
316 {
317     try
318     {
319         if( !m_aoDocsToInsert.empty() )
320         {
321             m_oColl.insert_many( m_aoDocsToInsert.begin(), m_aoDocsToInsert.end() );
322             m_aoDocsToInsert.clear();
323         }
324     }
325     catch( const std::exception &ex )
326     {
327         m_aoDocsToInsert.clear();
328         CPLError(CE_Failure, CPLE_AppDefined, "%s: %s",
329                  "CreateFeature()", ex.what());
330         return OGRERR_FAILURE;
331     }
332 
333     WriteOGRMetadata();
334 
335     return OGRERR_NONE;
336 }
337 
338 /************************************************************************/
339 /*                            ResetReading()                            */
340 /************************************************************************/
341 
ResetReading()342 void OGRMongoDBv3Layer::ResetReading()
343 {
344     m_poCursor.reset();
345     m_poIterator.reset();
346     m_nIndex = 0;
347 }
348 
349 /************************************************************************/
350 /*                            GetLayerDefn()                            */
351 /************************************************************************/
352 
GetLayerDefn()353 OGRFeatureDefn* OGRMongoDBv3Layer::GetLayerDefn()
354 {
355     if( !m_bHasEstablishedFeatureDefn )
356         EstablishFeatureDefn();
357 
358     return m_poFeatureDefn;
359 }
360 
361 /************************************************************************/
362 /*                    OGRMongoDBv3GetFieldTypeFromBSON()                */
363 /************************************************************************/
364 
365 static
OGRMongoDBv3GetFieldTypeFromBSON(const bsoncxx::document::element & elt,OGRFieldSubType & eSubType)366 OGRFieldType OGRMongoDBv3GetFieldTypeFromBSON(
367                                         const bsoncxx::document::element& elt,
368                                         OGRFieldSubType& eSubType )
369 {
370     eSubType = OFSTNone;
371 
372     auto eBSONType = elt.type();
373     if( eBSONType == bsoncxx::type::k_bool )
374     {
375         eSubType = OFSTBoolean;
376         return OFTInteger;
377     }
378     else if( eBSONType == bsoncxx::type::k_double )
379         return OFTReal;
380     else if( eBSONType == bsoncxx::type::k_int32 )
381         return OFTInteger;
382     else if( eBSONType == bsoncxx::type::k_int64 )
383         return OFTInteger64;
384     else if( eBSONType == bsoncxx::type::k_utf8 )
385         return OFTString;
386     else if( eBSONType == bsoncxx::type::k_array )
387     {
388         auto arrayView = elt.get_array().value;
389         if( arrayView.empty() )
390             return OFTStringList; /* we don't know, so let's assume it is a string list */
391         OGRFieldType eType = OFTIntegerList;
392         bool bOnlyBoolean = true;
393         for (auto&& subElt : arrayView)
394         {
395             eBSONType = subElt.type();
396 
397             bOnlyBoolean &= (eBSONType == bsoncxx::type::k_bool);
398             if (eBSONType == bsoncxx::type::k_double)
399                 eType = OFTRealList;
400             else if (eType == OFTIntegerList && eBSONType == bsoncxx::type::k_int64)
401                 eType = OFTInteger64List;
402             else if (eBSONType != bsoncxx::type::k_int32 &&
403                      eBSONType != bsoncxx::type::k_int64 &&
404                      eBSONType != bsoncxx::type::k_bool)
405                 return OFTStringList;
406         }
407         if( bOnlyBoolean )
408             eSubType = OFSTBoolean;
409         return eType;
410     }
411     else if( eBSONType == bsoncxx::type::k_date )
412         return OFTDateTime;
413     else if( eBSONType == bsoncxx::type::k_binary )
414         return OFTBinary;
415     else
416         return OFTString; /* null, object */
417 }
418 
419 /************************************************************************/
420 /*                         AddOrUpdateField()                           */
421 /************************************************************************/
422 
AddOrUpdateField(const char * pszAttrName,const bsoncxx::document::element & elt,char chNestedAttributeSeparator,std::vector<CPLString> & aosPaths,std::map<CPLString,CPLString> & oMapIndices)423 void OGRMongoDBv3Layer::AddOrUpdateField(const char* pszAttrName,
424                                          const bsoncxx::document::element& elt,
425                                          char chNestedAttributeSeparator,
426                                          std::vector<CPLString>& aosPaths,
427                                          std::map< CPLString, CPLString>& oMapIndices)
428 {
429     const auto eBSONType = elt.type();
430     if( eBSONType == bsoncxx::type::k_null ||
431         eBSONType == bsoncxx::type::k_undefined ||
432         eBSONType == bsoncxx::type::k_minkey ||
433         eBSONType == bsoncxx::type::k_maxkey )
434         return;
435 
436     if( eBSONType == bsoncxx::type::k_document )
437     {
438         bsoncxx::document::view doc{elt.get_document()};
439         auto eltType = doc["type"];
440         if( eltType && eltType.type() == bsoncxx::type::k_utf8 )
441         {
442             OGRwkbGeometryType eGeomType = OGRFromOGCGeomType(
443                 std::string(eltType.get_utf8().value).c_str());
444             if( eGeomType != wkbUnknown )
445             {
446                 int nIndex = m_poFeatureDefn->GetGeomFieldIndex(pszAttrName);
447                 if( nIndex < 0 )
448                 {
449                     OGRGeomFieldDefn fldDefn( pszAttrName, eGeomType );
450                     OGRSpatialReference* poSRS = new OGRSpatialReference();
451                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
452                     poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
453                     fldDefn.SetSpatialRef(poSRS);
454                     poSRS->Release();
455                     m_poFeatureDefn->AddGeomFieldDefn( &fldDefn );
456 
457                     aosPaths.push_back(std::string(elt.key()));
458                     m_aaosGeomFieldPaths.push_back(aosPaths);
459                     if( oMapIndices.find(pszAttrName) == oMapIndices.end() )
460                         m_aosGeomIndexes.push_back(oMapIndices[pszAttrName]);
461                     else
462                         m_aosGeomIndexes.push_back("none");
463                     m_apoCT.push_back(nullptr);
464                 }
465                 else
466                 {
467                     OGRGeomFieldDefn* poFDefn = m_poFeatureDefn->GetGeomFieldDefn(nIndex);
468                     if( poFDefn->GetType() != eGeomType )
469                         poFDefn->SetType(wkbUnknown);
470                 }
471             }
472         }
473         else if( m_poDS->m_bFlattenNestedAttributes )
474         {
475             if( m_poFeatureDefn->GetGeomFieldIndex(pszAttrName) >= 0 )
476                 return;
477             aosPaths.push_back(std::string(elt.key()));
478             for (auto&& subElt : doc)
479             {
480                 char szSeparator[2];
481                 szSeparator[0] = chNestedAttributeSeparator;
482                 szSeparator[1] = 0;
483                 CPLString osAttrName(pszAttrName);
484                 osAttrName += szSeparator;
485                 osAttrName += std::string(subElt.key());
486 
487                 std::vector<CPLString> aosNewPaths(aosPaths);
488                 AddOrUpdateField(osAttrName, subElt,
489                                  chNestedAttributeSeparator,
490                                  aosNewPaths, oMapIndices);
491             }
492             return;
493         }
494     }
495     else if( eBSONType == bsoncxx::type::k_array )
496     {
497         if( m_poFeatureDefn->GetGeomFieldIndex(pszAttrName) >= 0 )
498             return;
499         if( oMapIndices.find(pszAttrName) != oMapIndices.end() &&
500             oMapIndices[pszAttrName] == "2d" )
501         {
502             OGRGeomFieldDefn fldDefn( pszAttrName, wkbPoint );
503             OGRSpatialReference* poSRS = new OGRSpatialReference();
504             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
505             poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
506             fldDefn.SetSpatialRef(poSRS);
507             poSRS->Release();
508             m_poFeatureDefn->AddGeomFieldDefn( &fldDefn );
509 
510             aosPaths.push_back(std::string(elt.key()));
511             m_aaosGeomFieldPaths.push_back(aosPaths);
512             m_aosGeomIndexes.push_back("2d");
513             m_apoCT.push_back(nullptr);
514         }
515     }
516 
517     if( m_poFeatureDefn->GetGeomFieldIndex(pszAttrName) >= 0 )
518         return;
519 
520     OGRFieldSubType eNewSubType;
521     OGRFieldType eNewType = OGRMongoDBv3GetFieldTypeFromBSON( elt, eNewSubType );
522 
523     int nIndex = m_poFeatureDefn->GetFieldIndex(pszAttrName);
524     if( nIndex < 0 )
525     {
526         OGRFieldDefn fldDefn( pszAttrName, eNewType );
527         fldDefn.SetSubType(eNewSubType);
528         if( eNewSubType == OFSTBoolean )
529             fldDefn.SetWidth(1);
530         m_poFeatureDefn->AddFieldDefn( &fldDefn );
531 
532         aosPaths.push_back(std::string(elt.key()));
533         m_aaosFieldPaths.push_back(aosPaths);
534     }
535     else
536     {
537         OGRFieldDefn* poFDefn = m_poFeatureDefn->GetFieldDefn(nIndex);
538         OGRUpdateFieldType(poFDefn, eNewType, eNewSubType);
539     }
540 }
541 
542 /************************************************************************/
543 /*                         CollectGeomIndices()                         */
544 /************************************************************************/
545 
CollectGeomIndices()546 std::map< CPLString, CPLString> OGRMongoDBv3Layer::CollectGeomIndices()
547 {
548     std::map< CPLString, CPLString> oMapIndices;
549     try
550     {
551         auto cursor = m_oColl.list_indexes();
552         for (auto&& l_index : cursor)
553         {
554             //std::string s(bsoncxx::to_json(l_index));
555             //CPLDebug("MongoDBv3", "%s", s.c_str());
556             auto key = l_index["key"];
557             if( key && key.type() == bsoncxx::type::k_document )
558             {
559                 bsoncxx::document::view keyDoc{key.get_document()};
560                 for (auto&& field : keyDoc)
561                 {
562                     if( field.type() == bsoncxx::type::k_utf8 )
563                     {
564                         std::string v(field.get_utf8().value);
565                         if( v == "2d" || v == "2dsphere" )
566                         {
567                             std::string idxColName(field.key());
568                             //CPLDebug("MongoDBv3", "Index %s for %s of %s",
569                             //         v.c_str(),
570                             //         idxColName.c_str(),
571                             //         m_poFeatureDefn->GetName());
572                             oMapIndices[idxColName] = v;
573                         }
574                     }
575                 }
576             }
577         }
578     }
579     catch( const std::exception& ex )
580     {
581         CPLDebug("MongoDBv3", "Error when listing indices: %s", ex.what());
582     }
583     return oMapIndices;
584 }
585 
586 /************************************************************************/
587 /*                          ReadOGRMetadata()                           */
588 /************************************************************************/
589 
ReadOGRMetadata(std::map<CPLString,CPLString> & oMapIndices)590 bool OGRMongoDBv3Layer::ReadOGRMetadata(std::map< CPLString, CPLString>& oMapIndices)
591 {
592     try
593     {
594         bsoncxx::builder::basic::document filter{};
595         filter.append(kvp("layer", m_oColl.name()));
596         auto docOpt = m_oDb["_ogr_metadata"].find_one(filter.extract());
597         if( docOpt )
598         {
599             auto doc = docOpt->view();
600             auto fid = doc["fid"];
601             if( fid && fid.type() == bsoncxx::type::k_utf8 )
602                 m_osFID = std::string(fid.get_utf8().value);
603 
604             auto fields = doc["fields"];
605             if( fields && fields.type() == bsoncxx::type::k_array )
606             {
607                 auto arrayView(fields.get_array().value);
608                 for( const auto& elt: arrayView )
609                 {
610                     if( elt.type() == bsoncxx::type::k_document )
611                     {
612                         auto obj2_doc(elt.get_document());
613                         auto obj2(obj2_doc.view());
614                         auto name = obj2["name"];
615                         auto type = obj2["type"];
616                         auto subtype = obj2["subtype"];
617                         auto path = obj2["path"];
618                         if( name && name.type() == bsoncxx::type::k_utf8 &&
619                             type && type.type() == bsoncxx::type::k_utf8 &&
620                             path && path.type() == bsoncxx::type::k_array )
621                         {
622                             if( std::string(name.get_utf8().value) == "_id" )
623                                 continue;
624                             OGRFieldType eType(OFTString);
625                             for(int j=0; j<=OFTMaxType;j++)
626                             {
627                                 if( EQUAL(OGR_GetFieldTypeName(static_cast<OGRFieldType>(j)),
628                                           std::string(type.get_utf8().value).c_str()) )
629                                 {
630                                     eType = static_cast<OGRFieldType>(j);
631                                     break;
632                                 }
633                             }
634 
635                             std::vector<CPLString> aosPaths;
636                             auto oPathArray(path.get_array().value);
637                             bool ok = true;
638                             for( const auto& eltPath: oPathArray )
639                             {
640                                 if( eltPath.type() != bsoncxx::type::k_utf8 )
641                                 {
642                                     ok = false;
643                                     break;
644                                 }
645                                 aosPaths.push_back(std::string(eltPath.get_utf8().value));
646                             }
647                             if( !ok )
648                                 continue;
649 
650                             OGRFieldDefn oFieldDefn(std::string(name.get_utf8().value).c_str(), eType);
651                             if( subtype && subtype.type() == bsoncxx::type::k_utf8 &&
652                                 std::string(subtype.get_utf8().value) == "Boolean" )
653                                 oFieldDefn.SetSubType(OFSTBoolean);
654                             m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
655 
656                             m_aaosFieldPaths.push_back(aosPaths);
657                         }
658                     }
659                 }
660             }
661 
662             auto geomfields = doc["geomfields"];
663             if( geomfields && geomfields.type() == bsoncxx::type::k_array )
664             {
665                 auto arrayView(geomfields.get_array().value);
666                 for( const auto& elt: arrayView )
667                 {
668                     if( elt.type() == bsoncxx::type::k_document )
669                     {
670                         auto obj2_doc(elt.get_document());
671                         auto obj2(obj2_doc.view());
672                         auto name = obj2["name"];
673                         auto type = obj2["type"];
674                         auto path = obj2["path"];
675                         if( name && name.type() == bsoncxx::type::k_utf8 &&
676                             type && type.type() == bsoncxx::type::k_utf8 &&
677                             path && path.type() == bsoncxx::type::k_array )
678                         {
679 
680                             std::vector<CPLString> aosPaths;
681                             auto oPathArray(path.get_array().value);
682                             bool ok = true;
683                             for( const auto& eltPath: oPathArray )
684                             {
685                                 if( eltPath.type() != bsoncxx::type::k_utf8 )
686                                 {
687                                     ok = false;
688                                     break;
689                                 }
690                                 aosPaths.push_back(std::string(eltPath.get_utf8().value));
691                             }
692                             if( !ok )
693                                 continue;
694 
695                             OGRwkbGeometryType eType(OGRFromOGCGeomType(std::string(type.get_utf8().value).c_str()));
696                             OGRGeomFieldDefn oFieldDefn(std::string(name.get_utf8().value).c_str(), eType);
697                             OGRSpatialReference* poSRS = new OGRSpatialReference();
698                             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
699                             poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
700                             oFieldDefn.SetSpatialRef(poSRS);
701                             poSRS->Release();
702                             m_poFeatureDefn->AddGeomFieldDefn(&oFieldDefn);
703 
704                             m_aaosGeomFieldPaths.push_back(aosPaths);
705                             if( oMapIndices.find(oFieldDefn.GetNameRef()) != oMapIndices.end() )
706                                 m_aosGeomIndexes.push_back(oMapIndices[oFieldDefn.GetNameRef()]);
707                             else
708                                 m_aosGeomIndexes.push_back("none");
709                             //CPLDebug("MongoDBv3", "Layer %s: m_aosGeomIndexes[%d] = %s",
710                             //         m_poFeatureDefn->GetName(),
711                             //         m_poFeatureDefn->GetGeomFieldCount()-1,
712                             //         m_aosGeomIndexes[m_poFeatureDefn->GetGeomFieldCount()-1].c_str());
713                             m_apoCT.push_back(nullptr);
714                         }
715                     }
716                 }
717             }
718 
719             m_bLayerMetadataUpdatable = true;
720             return true;
721         }
722     }
723     catch( const std::exception& ex )
724     {
725         CPLError(CE_Warning, CPLE_AppDefined, "%s: %s",
726                     "ReadOGRMetadata()", ex.what());
727     }
728     return false;
729 }
730 
731 /************************************************************************/
732 /*                         EstablishFeatureDefn()                       */
733 /************************************************************************/
734 
EstablishFeatureDefn()735 void OGRMongoDBv3Layer::EstablishFeatureDefn()
736 {
737     if( m_bHasEstablishedFeatureDefn )
738         return;
739 
740     std::map< CPLString, CPLString> oMapIndices(CollectGeomIndices());
741 
742     int nCount = m_poDS->m_nFeatureCountToEstablishFeatureDefn;
743     if( m_poDS->m_bUseOGRMetadata )
744     {
745         if( ReadOGRMetadata(oMapIndices) )
746             nCount = 0;
747     }
748 
749     if( nCount != 0 )
750     {
751         try
752         {
753             mongocxx::options::find options;
754             if( nCount > 0 )
755             {
756                 options.limit(nCount);
757             }
758             if( m_poDS->m_nBatchSize > 0 )
759             {
760                 options.batch_size(m_poDS->m_nBatchSize);
761             }
762 
763             auto cursor = m_oColl.find({}, options);
764             for (auto&& doc : cursor)
765             {
766                 //std::string s(bsoncxx::to_json(doc));
767                 //CPLDebug("MongoDBv3", "%s", s.c_str());
768                 for (auto&& field : doc)
769                 {
770                     std::vector<CPLString> aosPaths;
771                     std::string osKey(field.key());
772                     if( osKey == m_poDS->m_osFID )
773                     {
774                         m_osFID = osKey;
775                     }
776                     else
777                     {
778                         AddOrUpdateField(osKey.c_str(), field,
779                                          '.', aosPaths, oMapIndices);
780                     }
781                 }
782             }
783         }
784         catch( const std::exception& ex )
785         {
786             CPLError(CE_Failure, CPLE_AppDefined, "%s: %s",
787                      "EstablishFeatureDefn()", ex.what());
788         }
789     }
790 
791     if( m_poDS->m_bJSonField )
792     {
793         OGRFieldDefn fldDefn("_json", OFTString);
794         m_poFeatureDefn->AddFieldDefn( &fldDefn );
795         std::vector<CPLString> aosPaths;
796         m_aaosFieldPaths.push_back(aosPaths);
797     }
798 
799     m_bHasEstablishedFeatureDefn = true;
800 }
801 
802 /************************************************************************/
803 /*                            GetFIDColumn()                            */
804 /************************************************************************/
805 
GetFIDColumn()806 const char* OGRMongoDBv3Layer::GetFIDColumn()
807 {
808     if( !m_bHasEstablishedFeatureDefn )
809         EstablishFeatureDefn();
810     return m_osFID.c_str();
811 }
812 
813 /************************************************************************/
814 /*                            BuildQuery()                              */
815 /************************************************************************/
816 
BuildQuery()817 bsoncxx::document::value OGRMongoDBv3Layer::BuildQuery()
818 {
819     bsoncxx::builder::basic::document b{};
820     auto queryAttrView(m_oQueryAttr.view());
821     for( const auto& field: queryAttrView )
822     {
823         b.append(kvp(field.key(), field.get_value()));
824     }
825     auto querySpatView(m_oQuerySpat.view());
826     for( const auto& field: querySpatView )
827     {
828         b.append(kvp(field.key(), field.get_value()));
829     }
830     return b.extract();
831 }
832 
833 /************************************************************************/
834 /*                           GetFeatureCount()                          */
835 /************************************************************************/
836 
GetFeatureCount(int bForce)837 GIntBig OGRMongoDBv3Layer::GetFeatureCount(int bForce)
838 {
839     if( m_poAttrQuery != nullptr ||
840         (m_poFilterGeom != nullptr && !TestCapability(OLCFastSpatialFilter)) )
841     {
842         return OGRLayer::GetFeatureCount(bForce);
843     }
844 
845     if( !m_bHasEstablishedFeatureDefn )
846         EstablishFeatureDefn();
847     SyncToDisk();
848 
849     try
850     {
851         return static_cast<GIntBig>(m_oColl.count_documents(BuildQuery()));
852     }
853     catch( const std::exception& ex )
854     {
855         CPLError(CE_Warning, CPLE_AppDefined, "%s: %s",
856                  "GetFeatureCount()", ex.what());
857         return OGRLayer::GetFeatureCount(bForce);
858     }
859 }
860 
861 /************************************************************************/
862 /*                             Stringify()                              */
863 /************************************************************************/
864 
865 #if BSONCXX_VERSION_MAJOR > 3 || BSONCXX_VERSION_MINOR >= 6
Stringify(const bsoncxx::types::bson_value::view & val)866 static CPLString Stringify(const bsoncxx::types::bson_value::view& val)
867 #else
868 static CPLString Stringify(const bsoncxx::types::value& val)
869 #endif
870 {
871     const auto eBSONType = val.type();
872     if( eBSONType == bsoncxx::type::k_utf8 )
873     {
874         return std::string( val.get_utf8().value );
875     }
876     else if( eBSONType == bsoncxx::type::k_int32 )
877         return CPLSPrintf("%d", val.get_int32().value);
878     else if( eBSONType == bsoncxx::type::k_int64 )
879         return CPLSPrintf(CPL_FRMT_GIB, static_cast<GIntBig>(val.get_int64().value));
880     else if( eBSONType == bsoncxx::type::k_double )
881         return CPLSPrintf("%.16g", val.get_double().value);
882     else if( eBSONType == bsoncxx::type::k_oid )
883         return val.get_oid().value.to_string();
884     else if( eBSONType == bsoncxx::type::k_bool )
885         return CPLSPrintf("%d", val.get_bool().value);
886     else if( eBSONType == bsoncxx::type::k_date )
887     {
888         GIntBig secsandmillis = static_cast<GIntBig>(val.get_date().to_int64());
889         struct tm tm;
890         GIntBig secs = secsandmillis / 1000;
891         int millis = static_cast<int>(secsandmillis % 1000);
892         if( millis < 0 )
893         {
894             secs --;
895             millis += 1000;
896         }
897         CPLUnixTimeToYMDHMS(secs, &tm);
898         return CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
899                           tm.tm_year + 1900,
900                           tm.tm_mon + 1,
901                           tm.tm_mday,
902                           tm.tm_hour,
903                           tm.tm_min,
904                           tm.tm_sec,
905                           millis);
906     }
907     else if( eBSONType == bsoncxx::type::k_document )
908     {
909         return CPLString(bsoncxx::to_json(val.get_document().value));
910     }
911     else
912     {
913         // This looks like a hack, but to_json() only works on documents
914         // so we have to wrap our value as a fake { "v": val } document...
915         bsoncxx::builder::basic::document b{};
916         b.append(kvp("v", val));
917         CPLString ret(bsoncxx::to_json(b.extract()));
918         CPLAssert(ret.find("{ \"v\" : ") == 0);
919         CPLAssert(ret.substr(ret.size() - 2) == " }");
920         ret = ret.substr(strlen("{ \"v\" : "), ret.size() - strlen("{ \"v\" : ") - 2);
921         return ret;
922     }
923 }
924 
925 /************************************************************************/
926 /*                   OGRMongoDBV3ReaderSetField()                       */
927 /************************************************************************/
928 
OGRMongoDBV3ReaderSetField(OGRFeature * poFeature,const char * pszAttrName,const bsoncxx::document::element & elt,bool bFlattenNestedAttributes,char chNestedAttributeSeparator)929 static void OGRMongoDBV3ReaderSetField(OGRFeature* poFeature,
930                                        const char* pszAttrName,
931                                        const bsoncxx::document::element& elt,
932                                        bool bFlattenNestedAttributes,
933                                        char chNestedAttributeSeparator)
934 {
935     int nGeomFieldIndex;
936     auto eBSONType = elt.type();
937 
938     if( eBSONType == bsoncxx::type::k_document &&
939         (nGeomFieldIndex = poFeature->GetGeomFieldIndex(pszAttrName)) >= 0 )
940     {
941         CPLPushErrorHandler(CPLQuietErrorHandler);
942         OGRGeometry* poGeom = OGRGeometry::FromHandle(
943             OGR_G_CreateGeometryFromJson( Stringify(elt.get_value()) ));
944         CPLPopErrorHandler();
945         if( poGeom != nullptr )
946         {
947             poGeom->assignSpatialReference(
948                 poFeature->GetDefnRef()->GetGeomFieldDefn(nGeomFieldIndex)->GetSpatialRef() );
949             poFeature->SetGeomFieldDirectly(nGeomFieldIndex, poGeom);
950         }
951         return;
952     }
953     else if( eBSONType == bsoncxx::type::k_array &&
954         (nGeomFieldIndex = poFeature->GetGeomFieldIndex(pszAttrName)) >= 0 )
955     {
956         auto arrayView = elt.get_array().value;
957         const unsigned nSize = static_cast<unsigned>(
958             std::distance(arrayView.begin(), arrayView.end()));
959         if( nSize == 2 )
960         {
961             auto x = arrayView[0];
962             auto y = arrayView[1];
963             if( x.type() == bsoncxx::type::k_double && y.type() == bsoncxx::type::k_double )
964             {
965                 OGRGeometry* poGeom = new OGRPoint( x.get_double().value, y.get_double().value );
966                 poGeom->assignSpatialReference(
967                     poFeature->GetDefnRef()->GetGeomFieldDefn(nGeomFieldIndex)->GetSpatialRef() );
968                 poFeature->SetGeomFieldDirectly(nGeomFieldIndex, poGeom);
969             }
970         }
971         return;
972     }
973 
974     if( bFlattenNestedAttributes && eBSONType == bsoncxx::type::k_document )
975     {
976         auto doc(elt.get_document().value);
977         for (auto&& field : doc)
978         {
979             CPLString osAttrName(pszAttrName);
980             osAttrName += chNestedAttributeSeparator;
981             std::string keyName(field.key());
982             osAttrName += keyName;
983             OGRMongoDBV3ReaderSetField(poFeature,
984                                        osAttrName, field,
985                                        bFlattenNestedAttributes,
986                                        chNestedAttributeSeparator);
987         }
988         return ;
989     }
990 
991     int nField = poFeature->GetFieldIndex(pszAttrName);
992     if( nField < 0 )
993         return;
994     OGRFieldDefn* poFieldDefn = poFeature->GetFieldDefnRef(nField);
995     CPLAssert( nullptr != poFieldDefn );
996     OGRFieldType eType = poFieldDefn->GetType();
997     if( eBSONType == bsoncxx::type::k_null )
998         poFeature->SetFieldNull( nField );
999     else if( eBSONType == bsoncxx::type::k_int32 )
1000         poFeature->SetField( nField, elt.get_int32().value );
1001     else if( eBSONType == bsoncxx::type::k_int64 )
1002         poFeature->SetField( nField, static_cast<GIntBig>(elt.get_int64().value) );
1003     else if( eBSONType == bsoncxx::type::k_double )
1004         poFeature->SetField( nField, elt.get_double().value );
1005     else if( eBSONType == bsoncxx::type::k_minkey && eType == OFTReal )
1006         poFeature->SetField( nField, -std::numeric_limits<double>::infinity() );
1007     else if( eBSONType == bsoncxx::type::k_maxkey && eType == OFTReal )
1008         poFeature->SetField( nField, std::numeric_limits<double>::infinity() );
1009      else if( eBSONType == bsoncxx::type::k_minkey && eType == OFTInteger )
1010         poFeature->SetField( nField, INT_MIN );
1011     else if( eBSONType == bsoncxx::type::k_maxkey  && eType == OFTInteger )
1012         poFeature->SetField( nField, INT_MAX );
1013     else if( eBSONType == bsoncxx::type::k_minkey && eType == OFTInteger64 )
1014         poFeature->SetField( nField, std::numeric_limits<GIntBig>::min() );
1015     else if( eBSONType == bsoncxx::type::k_maxkey  && eType == OFTInteger64 )
1016         poFeature->SetField( nField, std::numeric_limits<GIntBig>::max() );
1017     else if( eBSONType == bsoncxx::type::k_array )
1018     {
1019         auto arrayView = elt.get_array().value;
1020         const unsigned nSize = static_cast<unsigned>(
1021             std::distance(arrayView.begin(), arrayView.end()));
1022         if( eType == OFTStringList )
1023         {
1024             char** papszValues = static_cast<char**>(CPLCalloc(nSize + 1, sizeof(char*)));
1025             unsigned int i = 0;
1026             for( auto&& subElt : arrayView )
1027             {
1028                 papszValues[i] = CPLStrdup(Stringify(subElt.get_value()));
1029                 ++i;
1030             }
1031             poFeature->SetField( nField, papszValues );
1032             CSLDestroy(papszValues);
1033         }
1034         else if( eType == OFTRealList )
1035         {
1036             double* padfValues = static_cast<double*>(
1037                 CPLMalloc(nSize * sizeof(double)));
1038             unsigned int i = 0;
1039             for( auto&& subElt : arrayView )
1040             {
1041                 eBSONType = subElt.type();
1042                 if( eBSONType == bsoncxx::type::k_int32 )
1043                     padfValues[i] = subElt.get_int32().value;
1044                 else if( eBSONType == bsoncxx::type::k_int64 )
1045                     padfValues[i] = static_cast<double>(subElt.get_int64().value);
1046                 else if( eBSONType == bsoncxx::type::k_double )
1047                     padfValues[i] = subElt.get_double().value;
1048                 else if( eBSONType == bsoncxx::type::k_minkey )
1049                     padfValues[i] = -std::numeric_limits<double>::infinity();
1050                 else if( eBSONType == bsoncxx::type::k_maxkey )
1051                     padfValues[i] = std::numeric_limits<double>::infinity();
1052                 else
1053                     padfValues[i] = CPLAtof(Stringify(subElt.get_value()));
1054                 ++i;
1055             }
1056             poFeature->SetField( nField, nSize, padfValues );
1057             CPLFree(padfValues);
1058         }
1059         else if( eType == OFTIntegerList )
1060         {
1061             int* panValues = static_cast<int*>(CPLMalloc(nSize * sizeof(int)));
1062             unsigned int i = 0;
1063             for( auto&& subElt : arrayView )
1064             {
1065                 eBSONType = subElt.type();
1066                 if( eBSONType == bsoncxx::type::k_int32 )
1067                     panValues[i] = subElt.get_int32().value;
1068                 else if( eBSONType == bsoncxx::type::k_int64 )
1069                 {
1070                     GIntBig nVal = subElt.get_int64().value;
1071                     if( nVal < INT_MIN )
1072                         panValues[i] = INT_MIN;
1073                     else if( nVal > INT_MAX )
1074                         panValues[i] = INT_MAX;
1075                     else
1076                         panValues[i] = static_cast<int>(nVal);
1077                 }
1078                 else if( eBSONType == bsoncxx::type::k_double )
1079                 {
1080                     double dfVal = subElt.get_double().value;
1081                     if( dfVal < INT_MIN )
1082                         panValues[i] = INT_MIN;
1083                     else if( dfVal > INT_MAX )
1084                         panValues[i] = INT_MAX;
1085                     else
1086                         panValues[i] = static_cast<int>(dfVal);
1087                 }
1088                 else if( eBSONType == bsoncxx::type::k_minkey )
1089                     panValues[i] = INT_MIN;
1090                 else if( eBSONType == bsoncxx::type::k_maxkey )
1091                     panValues[i] = INT_MAX;
1092                 else
1093                     panValues[i] = atoi(Stringify(subElt.get_value()));
1094                 ++i;
1095             }
1096             poFeature->SetField( nField, nSize, panValues );
1097             CPLFree(panValues);
1098         }
1099         else if( eType == OFTInteger64List )
1100         {
1101             GIntBig* panValues = static_cast<GIntBig*>(
1102                 CPLMalloc(nSize * sizeof(GIntBig)));
1103             unsigned int i = 0;
1104             for( auto&& subElt : arrayView )
1105             {
1106                 eBSONType = subElt.type();
1107                 if( eBSONType == bsoncxx::type::k_int32 )
1108                     panValues[i] = subElt.get_int32().value;
1109                 else if( eBSONType == bsoncxx::type::k_int64 )
1110                     panValues[i] = subElt.get_int64().value;
1111                 else if( eBSONType == bsoncxx::type::k_double )
1112                 {
1113                     double dfVal = subElt.get_double().value;
1114                     if( dfVal < std::numeric_limits<GIntBig>::min() )
1115                         panValues[i] = std::numeric_limits<GIntBig>::min();
1116                     else if( dfVal > static_cast<double>(std::numeric_limits<GIntBig>::max()) )
1117                         panValues[i] = std::numeric_limits<GIntBig>::max();
1118                     else
1119                         panValues[i] = static_cast<GIntBig>(dfVal);
1120                 }
1121                 else if( eBSONType == bsoncxx::type::k_minkey )
1122                     panValues[i] = std::numeric_limits<GIntBig>::min();
1123                 else if( eBSONType == bsoncxx::type::k_maxkey )
1124                     panValues[i] = std::numeric_limits<GIntBig>::max();
1125                 else
1126                     panValues[i] = CPLAtoGIntBig(Stringify(subElt.get_value()));
1127                 ++i;
1128             }
1129             poFeature->SetField( nField, nSize, panValues );
1130             CPLFree(panValues);
1131         }
1132     }
1133     else if( eBSONType == bsoncxx::type::k_utf8 )
1134     {
1135         std::string s( elt.get_utf8().value );
1136         poFeature->SetField( nField, s.c_str() );
1137     }
1138     else if( eBSONType == bsoncxx::type::k_oid )
1139         poFeature->SetField( nField, elt.get_oid().value.to_string().c_str() );
1140     else if( eBSONType == bsoncxx::type::k_bool )
1141         poFeature->SetField( nField, elt.get_bool().value );
1142     else if( eBSONType == bsoncxx::type::k_binary )
1143     {
1144         const auto v(elt.get_binary());
1145         int len = static_cast<int>(v.size);
1146         const GByte *pabyData = v.bytes;
1147         poFeature->SetField( nField, len, pabyData);
1148     }
1149     else
1150         poFeature->SetField( nField, Stringify(elt.get_value()) );
1151 }
1152 
1153 /************************************************************************/
1154 /*                            Translate()                               */
1155 /************************************************************************/
1156 
Translate(const bsoncxx::document::view & doc)1157 std::unique_ptr<OGRFeature> OGRMongoDBv3Layer::Translate(
1158                                             const bsoncxx::document::view& doc)
1159 {
1160     std::unique_ptr<OGRFeature> poFeature(new OGRFeature(m_poFeatureDefn));
1161     for (auto&& field : doc)
1162     {
1163         std::string fieldName(field.key());
1164         if( !m_poDS->m_osFID.empty() && EQUAL(m_osFID, fieldName.c_str()) )
1165         {
1166             const auto eBSONType = field.type();
1167             if( eBSONType == bsoncxx::type::k_int32 )
1168             {
1169                 poFeature->SetFID(field.get_int32().value);
1170             }
1171             else if( eBSONType == bsoncxx::type::k_int64 )
1172             {
1173                 poFeature->SetFID(field.get_int64().value);
1174             }
1175             else if( eBSONType == bsoncxx::type::k_double )
1176             {
1177                 double dfV = field.get_double().value;
1178                 if( dfV >= static_cast<double>(
1179                             std::numeric_limits<GIntBig>::min()) &&
1180                     dfV <= static_cast<double>(
1181                             std::numeric_limits<GIntBig>::max()) )
1182                 {
1183                     auto nV = static_cast<GIntBig>(dfV);
1184                     if( static_cast<double>(nV) == dfV )
1185                     {
1186                         poFeature->SetFID(nV);
1187                     }
1188                 }
1189             }
1190         }
1191         else
1192         {
1193             OGRMongoDBV3ReaderSetField( poFeature.get(),
1194                                         fieldName.c_str(),
1195                                         field,
1196                                         m_poDS->m_bFlattenNestedAttributes,
1197                                         '.' );
1198         }
1199 
1200         if( m_poDS->m_bJSonField )
1201         {
1202             poFeature->SetField("_json", bsoncxx::to_json(doc).c_str());
1203         }
1204     }
1205     return poFeature;
1206 }
1207 
1208 /************************************************************************/
1209 /*                           GetNextFeature()                           */
1210 /************************************************************************/
1211 
GetNextFeature()1212 OGRFeature* OGRMongoDBv3Layer::GetNextFeature()
1213 {
1214     if( !m_bHasEstablishedFeatureDefn )
1215         EstablishFeatureDefn();
1216     if( !m_aoDocsToInsert.empty() )
1217         SyncToDisk();
1218 
1219     try
1220     {
1221         if( !m_poCursor )
1222         {
1223             mongocxx::options::find options;
1224             if( m_poDS->m_nBatchSize > 0 )
1225             {
1226                 options.batch_size(m_poDS->m_nBatchSize);
1227             }
1228             m_poCursor.reset(new mongocxx::cursor(m_oColl.find(BuildQuery(), options)));
1229             m_poIterator.reset(new mongocxx::cursor::iterator(m_poCursor->begin()));
1230         }
1231         if( !m_poCursor || !m_poIterator )
1232             return nullptr;
1233 
1234         while( *m_poIterator != m_poCursor->end() )
1235         {
1236             auto poFeature(Translate(**m_poIterator));
1237             if( poFeature->GetFID() < 0 )
1238                 poFeature->SetFID(++m_nIndex);
1239 
1240             (*m_poIterator) ++;
1241 
1242             if((m_poFilterGeom == nullptr
1243                 || FilterGeometry( poFeature->GetGeometryRef() ) )
1244             && (m_poAttrQuery == nullptr
1245                 || m_poAttrQuery->Evaluate( poFeature.get() )) )
1246             {
1247                 return poFeature.release();
1248             }
1249         }
1250     }
1251     catch( const std::exception& ex )
1252     {
1253         CPLError(CE_Failure, CPLE_AppDefined, "%s: %s",
1254                     "GetNextFeature()", ex.what());
1255     }
1256     return nullptr;
1257 }
1258 
1259 /************************************************************************/
1260 /*                             GetFeature()                             */
1261 /************************************************************************/
1262 
GetFeature(GIntBig nFID)1263 OGRFeature* OGRMongoDBv3Layer::GetFeature(GIntBig nFID)
1264 {
1265     if( !m_bHasEstablishedFeatureDefn )
1266         EstablishFeatureDefn();
1267     if( !m_aoDocsToInsert.empty() )
1268         SyncToDisk();
1269 
1270     if( m_osFID.empty() )
1271     {
1272         auto oQueryAttrBak = m_oQueryAttr;
1273         auto oQuerySpatBak = m_oQuerySpat;
1274         m_oQueryAttr = bsoncxx::builder::basic::make_document();
1275         m_oQuerySpat = bsoncxx::builder::basic::make_document();
1276         OGRFeature* poFeature = OGRLayer::GetFeature(nFID);
1277         m_oQueryAttr = oQueryAttrBak;
1278         m_oQuerySpat = oQuerySpatBak;
1279         return poFeature;
1280     }
1281 
1282     try
1283     {
1284         bsoncxx::builder::basic::document b{};
1285         b.append( kvp( std::string(m_osFID), static_cast<int64_t>(nFID) ) );
1286         auto obj = m_oColl.find_one(b.extract());
1287         if( !obj )
1288             return nullptr;
1289 
1290         auto poFeature = Translate(obj->view());
1291         poFeature->SetFID(nFID);
1292         return poFeature.release();
1293     }
1294     catch( const std::exception &ex )
1295     {
1296         CPLError(CE_Failure, CPLE_AppDefined, "%s: %s",
1297                  "GetFeature()", ex.what());
1298         return nullptr;
1299     }
1300 }
1301 
1302 /************************************************************************/
1303 /*                             DeleteFeature()                          */
1304 /************************************************************************/
1305 
DeleteFeature(GIntBig nFID)1306 OGRErr OGRMongoDBv3Layer::DeleteFeature(GIntBig nFID)
1307 {
1308     if( m_poDS->GetAccess() != GA_Update )
1309     {
1310         CPLError(CE_Failure, CPLE_AppDefined, "Dataset opened in read-only mode");
1311         return OGRERR_FAILURE;
1312     }
1313 
1314     if( !m_bHasEstablishedFeatureDefn )
1315         EstablishFeatureDefn();
1316     if( !m_aoDocsToInsert.empty() )
1317         SyncToDisk();
1318     if( m_osFID.empty() )
1319         return OGRERR_FAILURE;
1320 
1321     try
1322     {
1323         bsoncxx::builder::basic::document b{};
1324         b.append( kvp( std::string(m_osFID), static_cast<int64_t>(nFID) ) );
1325         auto obj = m_oColl.find_one_and_delete(b.extract());
1326         //if( obj )
1327         //{
1328         //    std::string s(bsoncxx::to_json(obj->view()));
1329         //    CPLDebug("MongoDBv3", "%s", s.c_str());
1330         //}
1331         return obj ? OGRERR_NONE : OGRERR_NON_EXISTING_FEATURE;
1332     }
1333     catch( const std::exception &ex )
1334     {
1335         CPLError(CE_Failure, CPLE_AppDefined, "%s: %s",
1336                  "DeleteFeature()", ex.what());
1337         return OGRERR_FAILURE;
1338     }
1339 }
1340 
1341 /************************************************************************/
1342 /*                            CreateField()                             */
1343 /************************************************************************/
1344 
CreateField(OGRFieldDefn * poFieldIn,int)1345 OGRErr OGRMongoDBv3Layer::CreateField( OGRFieldDefn *poFieldIn, int )
1346 
1347 {
1348     if( m_poDS->GetAccess() != GA_Update )
1349     {
1350         CPLError(CE_Failure, CPLE_AppDefined, "Dataset opened in read-only mode");
1351         return OGRERR_FAILURE;
1352     }
1353 
1354     const char* pszFieldName = poFieldIn->GetNameRef();
1355     if( m_poFeatureDefn->GetFieldIndex(pszFieldName) >= 0 )
1356     {
1357         if( !EQUAL(pszFieldName, "_id") &&
1358             !EQUAL(pszFieldName, "_json") )
1359         {
1360             CPLError(CE_Failure, CPLE_AppDefined,
1361                      "CreateField() called with an already existing field name: %s",
1362                      pszFieldName);
1363         }
1364         return OGRERR_FAILURE;
1365     }
1366 
1367     m_poFeatureDefn->AddFieldDefn( poFieldIn );
1368 
1369     std::vector<CPLString> aosPaths;
1370     if( m_bDotAsNestedField )
1371     {
1372         char** papszTokens = CSLTokenizeString2(pszFieldName, ".", 0);
1373         for(int i=0; papszTokens[i]; i++ )
1374             aosPaths.push_back(papszTokens[i]);
1375         CSLDestroy(papszTokens);
1376     }
1377     else
1378         aosPaths.push_back(pszFieldName);
1379     m_aaosFieldPaths.push_back(aosPaths);
1380 
1381     m_bUpdateLayerMetadata = m_bLayerMetadataUpdatable;
1382 
1383     return OGRERR_NONE;
1384 }
1385 
1386 /************************************************************************/
1387 /*                           CreateGeomField()                          */
1388 /************************************************************************/
1389 
CreateGeomField(OGRGeomFieldDefn * poFieldIn,int)1390 OGRErr OGRMongoDBv3Layer::CreateGeomField( OGRGeomFieldDefn *poFieldIn, int )
1391 
1392 {
1393     if( m_poDS->GetAccess() != GA_Update )
1394     {
1395         CPLError(CE_Failure, CPLE_AppDefined, "Dataset opened in read-only mode");
1396         return OGRERR_FAILURE;
1397     }
1398 
1399     if( m_poFeatureDefn->GetGeomFieldIndex(poFieldIn->GetNameRef()) >= 0 )
1400     {
1401         CPLError(CE_Failure, CPLE_AppDefined,
1402                  "CreateGeomField() called with an already existing field name: %s",
1403                   poFieldIn->GetNameRef());
1404         return OGRERR_FAILURE;
1405     }
1406 
1407     OGRGeomFieldDefn oFieldDefn(poFieldIn);
1408     if( EQUAL(oFieldDefn.GetNameRef(), "") )
1409         oFieldDefn.SetName("geometry");
1410 
1411     m_poFeatureDefn->AddGeomFieldDefn( &oFieldDefn );
1412 
1413     std::vector<CPLString> aosPaths;
1414     if( m_bDotAsNestedField )
1415     {
1416         char** papszTokens = CSLTokenizeString2(oFieldDefn.GetNameRef(), ".", 0);
1417         for(int i=0; papszTokens[i]; i++ )
1418             aosPaths.push_back(papszTokens[i]);
1419         CSLDestroy(papszTokens);
1420     }
1421     else
1422         aosPaths.push_back(oFieldDefn.GetNameRef());
1423     m_aaosGeomFieldPaths.push_back(aosPaths);
1424     m_aosGeomIndexes.push_back("none");
1425 
1426     std::unique_ptr<OGRCoordinateTransformation> poCT;
1427     if( oFieldDefn.GetSpatialRef() != nullptr )
1428     {
1429         OGRSpatialReference oSRS_WGS84;
1430         oSRS_WGS84.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
1431         oSRS_WGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1432         if( !oSRS_WGS84.IsSame(oFieldDefn.GetSpatialRef()) )
1433         {
1434             poCT.reset(OGRCreateCoordinateTransformation( oFieldDefn.GetSpatialRef(), &oSRS_WGS84 ));
1435             if( poCT.get() == nullptr )
1436             {
1437                 CPLError( CE_Warning, CPLE_AppDefined,
1438                           "On-the-fly reprojection to WGS84 long/lat would be "
1439                           "needed, but instantiation of transformer failed" );
1440             }
1441         }
1442     }
1443     m_apoCT.push_back(std::move(poCT));
1444 
1445     if( m_bCreateSpatialIndex )
1446     {
1447         //CPLDebug("MongoDBv3", "Create spatial index for %s of %s",
1448         //         poFieldIn->GetNameRef(), m_poFeatureDefn->GetName());
1449         try
1450         {
1451             const char* pszIndexType;
1452             if( wkbFlatten(poFieldIn->GetType()) != wkbPoint )
1453                 pszIndexType = "2dsphere";
1454             else
1455                 pszIndexType = CPLGetConfigOption("OGR_MONGODB_SPAT_INDEX_TYPE", "2dsphere");
1456 
1457             bsoncxx::builder::basic::document b{};
1458             b.append(kvp(
1459                 std::string(oFieldDefn.GetNameRef()),
1460                 std::string(pszIndexType)));
1461             m_oColl.create_index(b.extract());
1462 
1463             m_aosGeomIndexes.back() = pszIndexType;
1464         }
1465         catch( const std::exception &ex )
1466         {
1467             CPLError(CE_Failure, CPLE_AppDefined, "%s: %s",
1468                      "Index creation", ex.what());
1469         }
1470     }
1471 
1472     m_bUpdateLayerMetadata = m_bLayerMetadataUpdatable;
1473 
1474     return OGRERR_NONE;
1475 }
1476 
1477 /************************************************************************/
1478 /*                           SerializeField()                           */
1479 /************************************************************************/
1480 
SerializeField(bsoncxx::builder::basic::document & b,OGRFeature * poFeature,int iField,const char * pszJSonField)1481 void OGRMongoDBv3Layer::SerializeField(bsoncxx::builder::basic::document& b,
1482                                      OGRFeature *poFeature,
1483                                      int iField,
1484                                      const char* pszJSonField)
1485 {
1486     OGRFieldType eType = m_poFeatureDefn->GetFieldDefn(iField)->GetType();
1487     std::string osFieldName(pszJSonField);
1488     if( poFeature->IsFieldNull(iField) )
1489     {
1490         b.append( kvp(osFieldName, bsoncxx::types::b_null{}) );
1491     }
1492     else if( eType == OFTInteger )
1493     {
1494         if( m_poFeatureDefn->GetFieldDefn(iField)->GetSubType() == OFSTBoolean )
1495             b.append( kvp(osFieldName, CPL_TO_BOOL(poFeature->GetFieldAsInteger(iField)) ));
1496         else
1497             b.append( kvp(osFieldName, poFeature->GetFieldAsInteger(iField) ));
1498     }
1499     else if( eType == OFTInteger64 )
1500         b.append( kvp(osFieldName, static_cast<int64_t>(
1501             poFeature->GetFieldAsInteger64(iField))) );
1502     else if( eType == OFTReal )
1503         b.append( kvp(osFieldName, poFeature->GetFieldAsDouble(iField) ));
1504     else if( eType == OFTString )
1505         b.append( kvp(osFieldName, poFeature->GetFieldAsString(iField) ));
1506     else if( eType == OFTStringList )
1507     {
1508         char** papszValues = poFeature->GetFieldAsStringList(iField);
1509         bsoncxx::builder::basic::array arrayBuilder;
1510         for(int i=0; papszValues[i]; i++)
1511             arrayBuilder.append( papszValues[i] );
1512         b.append( kvp(osFieldName, arrayBuilder.extract() ));
1513     }
1514     else if( eType == OFTIntegerList )
1515     {
1516         int nSize;
1517         const int* panValues = poFeature->GetFieldAsIntegerList(iField, &nSize);
1518         bsoncxx::builder::basic::array arrayBuilder;
1519         for(int i=0; i<nSize; i++)
1520             arrayBuilder.append( panValues[i] );
1521         b.append( kvp(osFieldName, arrayBuilder.extract() ));
1522     }
1523     else if( eType == OFTInteger64List )
1524     {
1525         int nSize;
1526         const GIntBig* panValues = poFeature->GetFieldAsInteger64List(iField, &nSize);
1527         bsoncxx::builder::basic::array arrayBuilder;
1528         for(int i=0; i<nSize; i++)
1529             arrayBuilder.append( static_cast<int64_t>(panValues[i]) );
1530         b.append( kvp(osFieldName, arrayBuilder.extract() ));
1531     }
1532     else if( eType == OFTRealList )
1533     {
1534         int nSize;
1535         const double* padfValues = poFeature->GetFieldAsDoubleList(iField, &nSize);
1536         bsoncxx::builder::basic::array arrayBuilder;
1537         for(int i=0; i<nSize; i++)
1538             arrayBuilder.append( padfValues[i] );
1539         b.append( kvp(osFieldName, arrayBuilder.extract() ));
1540     }
1541     else if( eType == OFTBinary )
1542     {
1543         int nSize;
1544         const GByte* pabyData = poFeature->GetFieldAsBinary(iField, &nSize);
1545         bsoncxx::types::b_binary bin;
1546         bin.sub_type = bsoncxx::binary_sub_type::k_binary;
1547         bin.size = nSize;
1548         bin.bytes = pabyData;
1549         b.append( kvp(osFieldName, bin) );
1550     }
1551     else if( eType == OFTDate || eType == OFTDateTime || eType == OFTTime )
1552     {
1553         struct tm tm;
1554         int nYear, nMonth, nDay, nHour, nMinute, nTZ;
1555         float fSecond;
1556         poFeature->GetFieldAsDateTime(iField, &nYear, &nMonth, &nDay,
1557                                       &nHour, &nMinute, &fSecond, &nTZ);
1558         tm.tm_year = nYear - 1900;
1559         tm.tm_mon = nMonth - 1;
1560         tm.tm_mday = nDay;
1561         tm.tm_hour = nHour;
1562         tm.tm_min = nMinute;
1563         tm.tm_sec = static_cast<int>(fSecond);
1564         GIntBig millis = 1000 * CPLYMDHMSToUnixTime(&tm) +
1565                         static_cast<GIntBig>(1000 * fmod(fSecond, 1));
1566         b.append( kvp(osFieldName, bsoncxx::types::b_date(
1567             static_cast<std::chrono::milliseconds>(millis)) ) );
1568     }
1569 }
1570 
1571 /************************************************************************/
1572 /*                        SerializeGeometry()                           */
1573 /************************************************************************/
1574 
SerializeGeometry(bsoncxx::builder::basic::document & b,OGRGeometry * poGeom,int iField,const char * pszJSonField)1575 void OGRMongoDBv3Layer::SerializeGeometry(bsoncxx::builder::basic::document& b,
1576                                           OGRGeometry* poGeom, int iField,
1577                                           const char* pszJSonField)
1578 {
1579     std::string osFieldName(pszJSonField);
1580     if( m_aosGeomIndexes[iField] == "2d" &&
1581         wkbFlatten(poGeom->getGeometryType()) == wkbPoint )
1582     {
1583         bsoncxx::builder::basic::array arrayBuilder;
1584         OGRPoint* poPoint = poGeom->toPoint();
1585         arrayBuilder.append( poPoint->getX() );
1586         arrayBuilder.append( poPoint->getY() );
1587         b.append( kvp(osFieldName, arrayBuilder.extract()) );
1588     }
1589     else
1590     {
1591         char* pszJSon = OGR_G_ExportToJson(OGRGeometry::ToHandle(poGeom));
1592         if( pszJSon )
1593         {
1594             //CPLDebug("MongoDBv3", "%s", pszJSon);
1595             auto obj(bsoncxx::from_json(pszJSon));
1596             b.append( kvp(osFieldName, obj) );
1597         }
1598         CPLFree(pszJSon);
1599     }
1600 }
1601 
1602 /************************************************************************/
1603 /*                       SerializeRecursive()                           */
1604 /************************************************************************/
1605 
SerializeRecursive(bsoncxx::builder::basic::document & b,OGRFeature * poFeature,std::map<CPLString,IntOrMap * > & aoMap)1606 void OGRMongoDBv3Layer::SerializeRecursive(bsoncxx::builder::basic::document& b,
1607                                          OGRFeature *poFeature,
1608                                          std::map< CPLString, IntOrMap*>& aoMap )
1609 {
1610     std::map< CPLString, IntOrMap* >::iterator oIter = aoMap.begin();
1611     for( ; oIter != aoMap.end(); ++oIter)
1612     {
1613         IntOrMap* intOrMap = oIter->second;
1614         if( intOrMap->bIsMap )
1615         {
1616             bsoncxx::builder::basic::document subB;
1617             SerializeRecursive(subB, poFeature, *(intOrMap->u.poMap));
1618             b.append( kvp( std::string(oIter->first), subB.extract()) );
1619             delete intOrMap->u.poMap;
1620         }
1621         else
1622         {
1623             int i = intOrMap->u.nField;
1624             if( i >= 0)
1625             {
1626                 SerializeField(b, poFeature, i, oIter->first.c_str());
1627             }
1628             else
1629             {
1630                 i = -i - 1;
1631                 OGRGeometry* poGeom = poFeature->GetGeomFieldRef(i);
1632                 SerializeGeometry(b, poGeom, i, oIter->first.c_str());
1633             }
1634         }
1635         delete intOrMap;
1636     }
1637 }
1638 
1639 /************************************************************************/
1640 /*                           InsertInMap()                              */
1641 /************************************************************************/
1642 
InsertInMap(IntOrMap * rootMap,std::map<std::vector<CPLString>,IntOrMap * > & aoMap,const std::vector<CPLString> & aosFieldPathFull,int nField)1643 void OGRMongoDBv3Layer::InsertInMap(IntOrMap* rootMap,
1644                                   std::map< std::vector<CPLString>, IntOrMap*>& aoMap,
1645                                   const std::vector<CPLString>& aosFieldPathFull,
1646                                   int nField)
1647 {
1648     std::vector<CPLString> aosFieldPath;
1649     std::vector<CPLString> aosFieldPathPrev;
1650     for(int j=0; j< static_cast<int>(aosFieldPathFull.size()) - 1; j++)
1651     {
1652         aosFieldPath.push_back(aosFieldPathFull[j]);
1653         if( aoMap.find(aosFieldPath) == aoMap.end() )
1654         {
1655             IntOrMap* intOrMap = new IntOrMap;
1656             intOrMap->bIsMap = TRUE;
1657             intOrMap->u.poMap = new std::map< CPLString, IntOrMap*>;
1658             aoMap[aosFieldPath] = intOrMap;
1659         }
1660         if( j > 0 )
1661         {
1662             std::map< CPLString, IntOrMap* >* poPrevMap = aoMap[aosFieldPathPrev]->u.poMap;
1663             (*poPrevMap)[aosFieldPathFull[j]] = aoMap[aosFieldPath];
1664         }
1665         else
1666             (*(rootMap->u.poMap))[aosFieldPathFull[j]] = aoMap[aosFieldPath];
1667         aosFieldPathPrev.push_back(aosFieldPathFull[j]);
1668     }
1669     IntOrMap* intOrMap = new IntOrMap;
1670     intOrMap->bIsMap = FALSE;
1671     intOrMap->u.nField = nField;
1672     std::map< CPLString, IntOrMap* >* poPrevMap = aoMap[aosFieldPathPrev]->u.poMap;
1673     const CPLString& osLastComponent(aosFieldPathFull.back());
1674     CPLAssert( (*poPrevMap).find(osLastComponent) == (*poPrevMap).end() );
1675     (*(poPrevMap))[osLastComponent] = intOrMap;
1676 }
1677 
1678 /************************************************************************/
1679 /*                       BuildBSONObjFromFeature()                      */
1680 /************************************************************************/
1681 
BuildBSONObjFromFeature(OGRFeature * poFeature,bool bUpdate)1682 bsoncxx::document::value OGRMongoDBv3Layer::BuildBSONObjFromFeature(OGRFeature* poFeature, bool bUpdate)
1683 {
1684     bsoncxx::builder::basic::document b{};
1685 
1686     int nJSonFieldIndex = m_poFeatureDefn->GetFieldIndex("_json");
1687     if( nJSonFieldIndex >= 0 && poFeature->IsFieldSetAndNotNull(nJSonFieldIndex) )
1688     {
1689         CPLString osJSon(poFeature->GetFieldAsString(nJSonFieldIndex));
1690 
1691         auto obj(bsoncxx::from_json(osJSon));
1692         auto obj_view(obj.view());
1693         if( (m_bIgnoreSourceID || !obj_view["_id"]) && !bUpdate )
1694         {
1695             bsoncxx::oid generated;
1696             b.append(kvp("_id", generated));
1697             poFeature->SetField(0, generated.to_string().c_str());
1698         }
1699         for( const auto& field: obj_view )
1700         {
1701             b.append(kvp(field.key(), field.get_value()));
1702         }
1703         return b.extract();
1704     }
1705 
1706     if( poFeature->GetFID() >= 0 && !m_osFID.empty() )
1707     {
1708         b.append(kvp( std::string(m_osFID),
1709                       static_cast<int64_t>(poFeature->GetFID()) ));
1710     }
1711 
1712     CPLAssert(static_cast<int>(m_aaosFieldPaths.size()) == m_poFeatureDefn->GetFieldCount());
1713 
1714     if( !poFeature->IsFieldSetAndNotNull(0) || (!bUpdate && m_bIgnoreSourceID) )
1715     {
1716         bsoncxx::oid generated;
1717         b.append(kvp("_id", generated));
1718         poFeature->SetField(0, generated.to_string().c_str());
1719     }
1720     else
1721         b.append(kvp(
1722             "_id", bsoncxx::oid(poFeature->GetFieldAsString(0)) ));
1723 
1724     IntOrMap* rootMap = new IntOrMap;
1725     rootMap->bIsMap = TRUE;
1726     rootMap->u.poMap = new std::map< CPLString, IntOrMap*>;
1727     std::map< std::vector<CPLString>, IntOrMap*> aoMap;
1728 
1729     for(int i=1;i<m_poFeatureDefn->GetFieldCount();i++)
1730     {
1731         if( !poFeature->IsFieldSet(i) )
1732             continue;
1733 
1734         if( m_aaosFieldPaths[i].size() > 1 )
1735         {
1736             InsertInMap(rootMap, aoMap, m_aaosFieldPaths[i], i);
1737         }
1738         else
1739         {
1740             const char* pszFieldName = m_poFeatureDefn->GetFieldDefn(i)->GetNameRef();
1741             SerializeField(b, poFeature, i, pszFieldName);
1742         }
1743     }
1744 
1745     CPLAssert(static_cast<int>(m_aaosGeomFieldPaths.size()) == m_poFeatureDefn->GetGeomFieldCount());
1746     CPLAssert(static_cast<int>(m_apoCT.size()) == m_poFeatureDefn->GetGeomFieldCount());
1747     for(int i=0;i<m_poFeatureDefn->GetGeomFieldCount();i++)
1748     {
1749         OGRGeometry* poGeom = poFeature->GetGeomFieldRef(i);
1750         if( poGeom == nullptr )
1751             continue;
1752         if( !bUpdate && m_apoCT[i] != nullptr )
1753             poGeom->transform( m_apoCT[i].get() );
1754 
1755         if( m_aaosGeomFieldPaths[i].size() > 1 )
1756         {
1757             InsertInMap(rootMap, aoMap, m_aaosGeomFieldPaths[i],  -i-1);
1758         }
1759         else
1760         {
1761             const char* pszFieldName = m_poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef();
1762             SerializeGeometry(b, poGeom, i, pszFieldName);
1763         }
1764     }
1765 
1766     SerializeRecursive(b, poFeature, *(rootMap->u.poMap));
1767     delete rootMap->u.poMap;
1768     delete rootMap;
1769 
1770     return b.extract();
1771 }
1772 
1773 /************************************************************************/
1774 /*                           ICreateFeature()                           */
1775 /************************************************************************/
1776 
ICreateFeature(OGRFeature * poFeature)1777 OGRErr OGRMongoDBv3Layer::ICreateFeature( OGRFeature *poFeature )
1778 {
1779     if( m_poDS->GetAccess() != GA_Update )
1780     {
1781         CPLError(CE_Failure, CPLE_AppDefined, "Dataset opened in read-only mode");
1782         return OGRERR_FAILURE;
1783     }
1784 
1785     if( !m_bHasEstablishedFeatureDefn )
1786         EstablishFeatureDefn();
1787 
1788     try
1789     {
1790         if( poFeature->GetFID() < 0 )
1791         {
1792             if( m_nNextFID == 0 )
1793                 m_nNextFID = GetFeatureCount(false);
1794             poFeature->SetFID(++m_nNextFID);
1795         }
1796 
1797         auto bsonObj( BuildBSONObjFromFeature(poFeature, false) );
1798 
1799         if( m_poDS->m_bBulkInsert )
1800         {
1801             constexpr size_t knMAX_DOCS_IN_BULK = 1000;
1802             if( m_aoDocsToInsert.size() == knMAX_DOCS_IN_BULK )
1803                 SyncToDisk();
1804             m_aoDocsToInsert.emplace_back( std::move(bsonObj) );
1805         }
1806         else
1807         {
1808             //std::string s(bsoncxx::to_json(bsonObj));
1809             //CPLDebug("MongoDBv3", "%s", s.c_str());
1810             m_oColl.insert_one( std::move(bsonObj) );
1811         }
1812 
1813         return OGRERR_NONE;
1814     }
1815     catch( const std::exception &ex )
1816     {
1817         CPLError(CE_Failure, CPLE_AppDefined, "%s: %s",
1818                  "CreateFeature()", ex.what());
1819         return OGRERR_FAILURE;
1820     }
1821 }
1822 
1823 /************************************************************************/
1824 /*                            ISetFeature()                             */
1825 /************************************************************************/
1826 
ISetFeature(OGRFeature * poFeature)1827 OGRErr OGRMongoDBv3Layer::ISetFeature( OGRFeature *poFeature )
1828 {
1829     if( m_poDS->GetAccess() != GA_Update )
1830     {
1831         CPLError(CE_Failure, CPLE_AppDefined, "Dataset opened in read-only mode");
1832         return OGRERR_FAILURE;
1833     }
1834 
1835     if( !m_bHasEstablishedFeatureDefn )
1836         EstablishFeatureDefn();
1837     if( !m_aoDocsToInsert.empty() )
1838         SyncToDisk();
1839 
1840     if( !poFeature->IsFieldSetAndNotNull(0) )
1841     {
1842         CPLError(CE_Failure, CPLE_AppDefined, "_id field not set");
1843         return OGRERR_FAILURE;
1844     }
1845 
1846     try
1847     {
1848         auto bsonObj( BuildBSONObjFromFeature(poFeature, true) );
1849         auto view(bsonObj.view());
1850 
1851         bsoncxx::builder::basic::document filterBuilder{};
1852         filterBuilder.append( kvp("_id", view["_id"].get_oid().value ) );
1853         if( !m_osFID.empty() )
1854             filterBuilder.append( kvp( std::string(m_osFID),
1855                                 static_cast<int64_t>(poFeature->GetFID()) ) );
1856 
1857         auto filter(filterBuilder.extract());
1858         auto ret = m_oColl.find_one_and_replace( std::move(filter), std::move(bsonObj) );
1859         //if( ret )
1860         //{
1861         //    std::string s(bsoncxx::to_json(ret->view()));
1862         //    CPLDebug("MongoDBv3", "%s", s.c_str());
1863         //}
1864         return ret ? OGRERR_NONE : OGRERR_NON_EXISTING_FEATURE;
1865     }
1866     catch( const std::exception &ex )
1867     {
1868         CPLError(CE_Failure, CPLE_AppDefined, "%s: %s",
1869                  "SetFeature()", ex.what());
1870         return OGRERR_FAILURE;
1871     }
1872 }
1873 /************************************************************************/
1874 /*                            TestCapability()                          */
1875 /************************************************************************/
1876 
TestCapability(const char * pszCap)1877 int OGRMongoDBv3Layer::TestCapability( const char* pszCap )
1878 {
1879     if( EQUAL(pszCap, OLCStringsAsUTF8) )
1880     {
1881         return true;
1882     }
1883     if( EQUAL(pszCap,OLCRandomRead) )
1884     {
1885         EstablishFeatureDefn();
1886         return !m_osFID.empty();
1887     }
1888     if( EQUAL(pszCap, OLCFastSpatialFilter) )
1889     {
1890         EstablishFeatureDefn();
1891         for(int i=0;i<m_poFeatureDefn->GetGeomFieldCount();i++)
1892         {
1893             if( m_aosGeomIndexes[i] == "none" )
1894             {
1895                 return false;
1896             }
1897         }
1898         return true;
1899     }
1900     if( EQUAL(pszCap, OLCCreateField) ||
1901              EQUAL(pszCap, OLCCreateGeomField) ||
1902              EQUAL(pszCap, OLCSequentialWrite) ||
1903              EQUAL(pszCap, OLCRandomWrite) )
1904     {
1905         return m_poDS->GetAccess() == GA_Update;
1906     }
1907     else if( EQUAL(pszCap,OLCDeleteFeature) )
1908     {
1909         EstablishFeatureDefn();
1910         return m_poDS->GetAccess() == GA_Update &&
1911                !m_osFID.empty();
1912     }
1913 
1914     return false;
1915 }
1916 
1917 /************************************************************************/
1918 /*                          SetAttributeFilter()                        */
1919 /************************************************************************/
1920 
SetAttributeFilter(const char * pszFilter)1921 OGRErr OGRMongoDBv3Layer::SetAttributeFilter(const char* pszFilter)
1922 {
1923     m_oQueryAttr = bsoncxx::builder::basic::make_document();
1924 
1925     if( pszFilter != nullptr && pszFilter[0] == '{' )
1926     {
1927         OGRLayer::SetAttributeFilter(nullptr);
1928         try
1929         {
1930             m_oQueryAttr = bsoncxx::from_json(pszFilter);
1931             return OGRERR_NONE;
1932         }
1933         catch( const std::exception &ex )
1934         {
1935             CPLError(CE_Failure, CPLE_AppDefined, "%s: %s",
1936                      "SetAttributeFilter()", ex.what());
1937             return OGRERR_FAILURE;
1938         }
1939     }
1940     return OGRLayer::SetAttributeFilter(pszFilter);
1941 }
1942 
1943 /************************************************************************/
1944 /*                          SetSpatialFilter()                          */
1945 /************************************************************************/
1946 
SetSpatialFilter(int iGeomField,OGRGeometry * poGeomIn)1947 void OGRMongoDBv3Layer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeomIn )
1948 
1949 {
1950     if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
1951         GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone )
1952     {
1953         if( iGeomField != 0 )
1954         {
1955             CPLError(CE_Failure, CPLE_AppDefined,
1956                      "Invalid geometry field index : %d", iGeomField);
1957         }
1958         return;
1959     }
1960     m_iGeomFieldFilter = iGeomField;
1961 
1962     m_oQuerySpat = bsoncxx::builder::basic::make_document();
1963     if( InstallFilter( poGeomIn ) && poGeomIn )
1964     {
1965         OGREnvelope sEnvelope;
1966         poGeomIn->getEnvelope(&sEnvelope);
1967         if( sEnvelope.MaxX == sEnvelope.MinX )
1968             sEnvelope.MaxX += 1e-10;
1969         if( sEnvelope.MaxY == sEnvelope.MinY )
1970             sEnvelope.MaxY += 1e-10;
1971 
1972         if( sEnvelope.MinX < -180 )
1973             sEnvelope.MinX = -180;
1974         if( sEnvelope.MinY < -90 )
1975             sEnvelope.MinY = -90;
1976         if( sEnvelope.MaxX > 180 )
1977             sEnvelope.MaxX = 180;
1978         if( sEnvelope.MaxY > 90 )
1979             sEnvelope.MaxY = 90;
1980         if( sEnvelope.MinX == -180 && sEnvelope.MinY == -90 &&
1981             sEnvelope.MaxX == 180 && sEnvelope.MaxY == 90 )
1982         {
1983             return;
1984         }
1985 
1986         try
1987         {
1988             if( m_aosGeomIndexes[m_iGeomFieldFilter] == "2dsphere" )
1989             {
1990                 m_oQuerySpat = bsoncxx::from_json(CPLSPrintf("{ \"%s\" : { \"$geoIntersects\" : "
1991                 "{ \"$geometry\" : { \"type\" : \"Polygon\" , \"coordinates\" : [["
1992                 "[%.16g,%.16g],[%.16g,%.16g],[%.16g,%.16g],[%.16g,%.16g],[%.16g,%.16g]]] } } } }",
1993                               m_poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetNameRef(),
1994                               sEnvelope.MinX, sEnvelope.MinY,
1995                               sEnvelope.MaxX, sEnvelope.MinY,
1996                               sEnvelope.MaxX, sEnvelope.MaxY,
1997                               sEnvelope.MinX, sEnvelope.MaxY,
1998                               sEnvelope.MinX, sEnvelope.MinY));
1999             }
2000             else if( m_aosGeomIndexes[m_iGeomFieldFilter] == "2d" )
2001             {
2002                 m_oQuerySpat = bsoncxx::from_json(CPLSPrintf("{ \"%s\" : { \"$geoWithin\" : "
2003                 "{ \"$box\" : [ [ %.16g , %.16g ] , [ %.16g , %.16g ] ] } } }",
2004                               m_poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetNameRef(),
2005                               sEnvelope.MinX, sEnvelope.MinY,
2006                               sEnvelope.MaxX, sEnvelope.MaxY));
2007             }
2008         }
2009         catch( const std::exception &ex )
2010         {
2011             CPLError(CE_Failure, CPLE_AppDefined,
2012                      "%s: %s", "SetSpatialFilter()", ex.what());
2013         }
2014     }
2015 }
2016 
2017 /************************************************************************/
2018 /*                              GetLayer()                              */
2019 /************************************************************************/
2020 
GetLayer(int nIndex)2021 OGRLayer* OGRMongoDBv3Dataset::GetLayer(int nIndex)
2022 {
2023     if( nIndex < 0 || nIndex >= GetLayerCount() )
2024         return nullptr;
2025     return m_apoLayers[nIndex].get();
2026 }
2027 
2028 /************************************************************************/
2029 /*                         GetLayerByName()                             */
2030 /************************************************************************/
2031 
GetLayerByName(const char * pszLayerName)2032 OGRLayer *OGRMongoDBv3Dataset::GetLayerByName(const char* pszLayerName)
2033 {
2034     OGRLayer* poLayer = GDALDataset::GetLayerByName(pszLayerName);
2035     if( poLayer != nullptr )
2036         return poLayer;
2037 
2038     for( const auto& l_poLayer: m_apoLayers )
2039     {
2040         l_poLayer->SyncToDisk();
2041     }
2042 
2043     CPLString osDatabase;
2044     if( m_osDatabase.empty() )
2045     {
2046         const char* pszDot = strchr(pszLayerName, '.');
2047         if( pszDot == nullptr )
2048             return nullptr;
2049         osDatabase = pszLayerName;
2050         osDatabase.resize(pszDot - pszLayerName);
2051         pszLayerName = pszDot + 1;
2052     }
2053     else
2054         osDatabase = m_osDatabase;
2055 
2056     for(int i=0;i<2;i++)
2057     {
2058         try
2059         {
2060             auto db = m_oConn.database(osDatabase);
2061             auto aosCollections = db.list_collection_names();
2062             for( const auto& osCollection: aosCollections )
2063             {
2064                 if( EQUAL(osCollection.c_str(),pszLayerName) )
2065                 {
2066                     m_apoLayers.emplace_back(
2067                         new OGRMongoDBv3Layer(this,
2068                                               osDatabase,
2069                                               osCollection.c_str()));
2070                     return m_apoLayers.back().get();
2071                 }
2072             }
2073         }
2074         catch( const std::exception &ex)
2075         {
2076             CPLError(CE_Failure, CPLE_AppDefined,
2077                      "Command failed: %s", ex.what());
2078         }
2079         if( i == 0 )
2080         {
2081             if( m_osDatabase.empty() )
2082                 break;
2083             const char* pszDot = strchr(pszLayerName, '.');
2084             if( pszDot == nullptr )
2085                 break;
2086             osDatabase = pszLayerName;
2087             osDatabase.resize(pszDot - pszLayerName);
2088             pszLayerName = pszDot + 1;
2089         }
2090     }
2091 
2092     return nullptr;
2093 }
2094 
2095 /************************************************************************/
2096 /*                           CreateLayers()                             */
2097 /************************************************************************/
2098 
CreateLayers(mongocxx::database & db)2099 void OGRMongoDBv3Dataset::CreateLayers(mongocxx::database& db)
2100 {
2101     std::string dbName(db.name());
2102     auto aosCollections = db.list_collection_names();
2103     for( const auto& osCollection: aosCollections )
2104     {
2105         if( osCollection != "_ogr_metadata" )
2106         {
2107             m_apoLayers.emplace_back(
2108                 new OGRMongoDBv3Layer(this, dbName.c_str(), osCollection));
2109         }
2110     }
2111 }
2112 
2113 /************************************************************************/
2114 /*                               Open()                                 */
2115 /************************************************************************/
2116 
Open(GDALOpenInfo * poOpenInfo)2117 bool OGRMongoDBv3Dataset::Open(GDALOpenInfo* poOpenInfo)
2118 {
2119     eAccess = poOpenInfo->eAccess;
2120 
2121     const char* pszHost = CSLFetchNameValueDef(
2122         poOpenInfo->papszOpenOptions, "HOST", "localhost");
2123     const char* pszPort = CSLFetchNameValueDef(
2124         poOpenInfo->papszOpenOptions, "PORT", "27017");
2125     const char* pszURI = CSLFetchNameValue(
2126         poOpenInfo->papszOpenOptions, "URI");
2127     if( pszURI == nullptr )
2128     {
2129         if( STARTS_WITH_CI(poOpenInfo->pszFilename, "mongodbv3:") )
2130             pszURI = poOpenInfo->pszFilename + strlen("mongodbv3:");
2131         else if( STARTS_WITH_CI(poOpenInfo->pszFilename, "mongodb:") ||
2132                 STARTS_WITH_CI(poOpenInfo->pszFilename, "mongodb+srv:") )
2133             pszURI = poOpenInfo->pszFilename;
2134     }
2135     const char* pszUser = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "USER");
2136     const char* pszPassword = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "PASSWORD");
2137     if( (pszUser != nullptr && pszPassword == nullptr) ||
2138         (pszUser == nullptr && pszPassword != nullptr) )
2139     {
2140         CPLError(CE_Failure, CPLE_AppDefined,
2141                     "USER and PASSWORD open options must be both specified.");
2142         return false;
2143     }
2144 
2145     try
2146     {
2147         mongocxx::uri uri;
2148         if( pszURI && pszURI[0] )
2149         {
2150             uri = mongocxx::uri(pszURI);
2151         }
2152         else
2153         {
2154             CPLString osURI("mongodb://");
2155             if( pszUser && pszPassword )
2156             {
2157                 osURI += pszUser;
2158                 osURI += ':';
2159                 osURI += pszPassword;
2160                 osURI += '@';
2161             }
2162             osURI += pszHost;
2163             osURI += ':';
2164             osURI += pszPort;
2165             uri = mongocxx::uri(osURI);
2166         }
2167         m_osDatabase = uri.database();
2168         if( m_osDatabase.empty() )
2169         {
2170             m_osDatabase = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "DBNAME", "");
2171         }
2172 
2173         CPLString osPEMKeyFile = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "SSL_PEM_KEY_FILE", "");
2174         CPLString osPEMKeyPassword = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "SSL_PEM_KEY_PASSWORD", "");
2175         CPLString osCAFile = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "SSL_CA_FILE", "");
2176         CPLString osCRLFile = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "SSL_CRL_FILE", "");
2177         bool bAllowInvalidCertificates =
2178             CPLFetchBool(poOpenInfo->papszOpenOptions, "SSL_ALLOW_INVALID_CERTIFICATES", false);
2179 
2180         mongocxx::options::client client_options;
2181         if( !osPEMKeyFile.empty() || !osPEMKeyPassword.empty() ||
2182             !osCAFile.empty() || !osCRLFile.empty() ||
2183             bAllowInvalidCertificates )
2184         {
2185 #if BSONCXX_VERSION_MAJOR > 3 || BSONCXX_VERSION_MINOR >= 6
2186             mongocxx::options::tls tls_options;
2187 #else
2188             mongocxx::options::ssl tls_options;
2189 #endif
2190             if( !osPEMKeyFile.empty() )
2191                 tls_options.pem_file(osPEMKeyFile);
2192             if( !osPEMKeyPassword.empty() )
2193                 tls_options.pem_password(osPEMKeyPassword);
2194             if( !osCAFile.empty() )
2195                 tls_options.ca_file(osCAFile);
2196             if( !osCRLFile.empty() )
2197                 tls_options.crl_file(osCRLFile);
2198             tls_options.allow_invalid_certificates(bAllowInvalidCertificates);
2199 #if BSONCXX_VERSION_MAJOR > 3 || BSONCXX_VERSION_MINOR >= 6
2200             client_options.tls_opts(tls_options);
2201 #else
2202             client_options.ssl_opts(tls_options);
2203 #endif
2204         }
2205 
2206         m_oConn = mongocxx::client(uri, client_options);
2207 
2208         try
2209         {
2210             auto db = m_oConn[m_osDatabase.empty() ? "admin" : m_osDatabase.c_str()];
2211             auto ret = db.run_command(bsoncxx::from_json("{ \"buildInfo\" : 1 }"));
2212             std::string s(bsoncxx::to_json(ret.view()));
2213             CPLDebug("MongoDBv3", "%s", s.c_str());
2214         }
2215         catch (const std::exception& ex)
2216         {
2217             CPLDebug("MongoDBv3", "buildInfo(): %s", ex.what());
2218         }
2219 
2220         if( m_osDatabase.empty() )
2221         {
2222             auto dbs = m_oConn.list_databases();
2223             for( const auto& dbBson: dbs )
2224             {
2225                 std::string dbName(dbBson["name"].get_utf8().value);
2226                 if( dbName == "admin" || dbName == "config" || dbName == "local" )
2227                 {
2228                     continue;
2229                 }
2230                 CPLDebug("MongoDBv3", "Iterating over database %s", dbName.c_str() );
2231                 auto db = m_oConn.database(dbName);
2232                 CreateLayers(db);
2233             }
2234         }
2235         else
2236         {
2237             auto db = m_oConn.database(m_osDatabase);
2238             CreateLayers(db);
2239         }
2240     }
2241     catch (const std::exception& ex)
2242     {
2243         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
2244         return false;
2245     }
2246 
2247     m_nBatchSize = atoi(CSLFetchNameValueDef(
2248         poOpenInfo->papszOpenOptions, "BATCH_SIZE", "0"));
2249     m_nFeatureCountToEstablishFeatureDefn = atoi(CSLFetchNameValueDef(
2250         poOpenInfo->papszOpenOptions, "FEATURE_COUNT_TO_ESTABLISH_FEATURE_DEFN", "100"));
2251     m_bJSonField = CPLFetchBool(
2252         poOpenInfo->papszOpenOptions, "JSON_FIELD", false);
2253     m_bFlattenNestedAttributes =
2254         CPLFetchBool(poOpenInfo->papszOpenOptions, "FLATTEN_NESTED_ATTRIBUTES", true);
2255     m_osFID = CSLFetchNameValueDef(
2256         poOpenInfo->papszOpenOptions, "FID", "ogc_fid");
2257     m_bUseOGRMetadata =
2258         CPLFetchBool( poOpenInfo->papszOpenOptions, "USE_OGR_METADATA", true);
2259     m_bBulkInsert = CPLFetchBool(
2260         poOpenInfo->papszOpenOptions, "BULK_INSERT", true);
2261 
2262     return true;
2263 }
2264 
2265 /************************************************************************/
2266 /*                            ICreateLayer()                            */
2267 /************************************************************************/
2268 
ICreateLayer(const char * pszName,OGRSpatialReference * poSpatialRef,OGRwkbGeometryType eGType,char ** papszOptions)2269 OGRLayer* OGRMongoDBv3Dataset::ICreateLayer( const char *pszName,
2270                                               OGRSpatialReference *poSpatialRef,
2271                                               OGRwkbGeometryType eGType,
2272                                               char ** papszOptions )
2273 {
2274     if( m_osDatabase.empty() )
2275     {
2276         CPLError(CE_Failure, CPLE_AppDefined,
2277                  "Cannot create layer/collection when dataset opened without explicit database");
2278         return nullptr;
2279     }
2280 
2281     if( eAccess != GA_Update )
2282     {
2283         CPLError(CE_Failure, CPLE_AppDefined, "Dataset opened in read-only mode");
2284         return nullptr;
2285     }
2286 
2287     for(int i=0; i<static_cast<int>(m_apoLayers.size()); i++)
2288     {
2289         if( EQUAL(m_apoLayers[i]->GetName(), pszName) )
2290         {
2291             if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != nullptr
2292                 && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") )
2293             {
2294                 DeleteLayer( i );
2295             }
2296             else
2297             {
2298                 CPLError( CE_Failure, CPLE_AppDefined,
2299                           "Layer %s already exists, CreateLayer failed.\n"
2300                           "Use the layer creation option OVERWRITE=YES to "
2301                           "replace it.",
2302                           pszName );
2303                 return nullptr;
2304             }
2305         }
2306     }
2307 
2308     try
2309     {
2310         m_oConn[m_osDatabase].create_collection(std::string(pszName));
2311     }
2312     catch( const std::exception& ex)
2313     {
2314         CPLError(CE_Failure, CPLE_AppDefined, "%s", ex.what());
2315         return nullptr;
2316     }
2317 
2318     m_apoLayers.emplace_back(
2319         new OGRMongoDBv3Layer(this, m_osDatabase, pszName));
2320     auto poLayer = m_apoLayers.back().get();
2321 
2322     poLayer->m_osFID = CSLFetchNameValueDef(papszOptions, "FID", "ogc_fid");
2323     poLayer->m_bLayerMetadataUpdatable =
2324         CPLFetchBool(papszOptions, "WRITE_OGR_METADATA", true);
2325     poLayer->m_bUpdateLayerMetadata = poLayer->m_bLayerMetadataUpdatable;
2326     poLayer->m_bDotAsNestedField =
2327         CPLFetchBool(papszOptions, "DOT_AS_NESTED_FIELD", true);
2328     poLayer->m_bIgnoreSourceID =
2329         CPLFetchBool(papszOptions, "IGNORE_SOURCE_ID", false);
2330     poLayer->m_bCreateSpatialIndex =
2331         CPLFetchBool(papszOptions, "SPATIAL_INDEX", true);
2332 
2333     if( eGType != wkbNone )
2334     {
2335         const char* pszGeometryName = CSLFetchNameValueDef(papszOptions, "GEOMETRY_NAME", "geometry");
2336         OGRGeomFieldDefn oFieldDefn(pszGeometryName, eGType);
2337         oFieldDefn.SetSpatialRef(poSpatialRef);
2338         poLayer->CreateGeomField(&oFieldDefn, FALSE);
2339     }
2340 
2341     return poLayer;
2342 }
2343 
2344 /************************************************************************/
2345 /*                            DeleteLayer()                             */
2346 /************************************************************************/
2347 
DeleteLayer(int iLayer)2348 OGRErr OGRMongoDBv3Dataset::DeleteLayer( int iLayer )
2349 
2350 {
2351     if( eAccess != GA_Update )
2352     {
2353         CPLError(CE_Failure, CPLE_AppDefined, "Dataset opened in read-only mode");
2354         return OGRERR_FAILURE;
2355     }
2356 
2357     if( iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()) )
2358         return OGRERR_FAILURE;
2359 
2360 /* -------------------------------------------------------------------- */
2361 /*      Blow away our OGR structures related to the layer.  This is     */
2362 /*      pretty dangerous if anything has a reference to this layer!     */
2363 /* -------------------------------------------------------------------- */
2364     CPLString osLayerName = m_apoLayers[iLayer]->GetName();
2365     CPLDebug( "MongoDB", "DeleteLayer(%s)", osLayerName.c_str() );
2366 
2367     try
2368     {
2369         {
2370             bsoncxx::builder::basic::document b{};
2371             std::string colName(m_apoLayers[iLayer]->m_oColl.name());
2372             b.append(kvp("layer", colName));
2373             m_apoLayers[iLayer]->m_oDb["_ogr_metadata"].find_one_and_delete(b.extract());
2374         }
2375 
2376         m_apoLayers[iLayer]->m_oColl.drop();
2377 
2378         m_apoLayers.erase( m_apoLayers.begin() + iLayer );
2379 
2380         return OGRERR_NONE;
2381     }
2382     catch( const std::exception &ex)
2383     {
2384         CPLError(CE_Failure, CPLE_AppDefined, "%s: %s",
2385                  "DeleteLayer()", ex.what());
2386         return OGRERR_FAILURE;
2387     }
2388 }
2389 
2390 /************************************************************************/
2391 /*                           TestCapability()                           */
2392 /************************************************************************/
2393 
TestCapability(const char * pszCap)2394 int OGRMongoDBv3Dataset::TestCapability( const char * pszCap )
2395 
2396 {
2397     if( EQUAL(pszCap,ODsCCreateLayer)
2398         || EQUAL(pszCap,ODsCDeleteLayer)
2399         || EQUAL(pszCap,ODsCCreateGeomFieldAfterCreateLayer) )
2400         return eAccess == GA_Update;
2401     else
2402         return FALSE;
2403 }
2404 
2405 /************************************************************************/
2406 /*                    OGRMongoDBv3SingleFeatureLayer                    */
2407 /************************************************************************/
2408 
2409 class OGRMongoDBv3SingleFeatureLayer final: public OGRLayer
2410 {
2411     OGRFeatureDefn     *m_poFeatureDefn;
2412     CPLString           osVal;
2413     int                 iNextShapeId;
2414     public:
2415        explicit OGRMongoDBv3SingleFeatureLayer( const char *pszVal );
~OGRMongoDBv3SingleFeatureLayer()2416        ~OGRMongoDBv3SingleFeatureLayer() { m_poFeatureDefn->Release(); }
ResetReading()2417        void             ResetReading() override { iNextShapeId = 0; }
2418        OGRFeature      *GetNextFeature() override;
GetLayerDefn()2419        OGRFeatureDefn  *GetLayerDefn() override { return m_poFeatureDefn; }
TestCapability(const char *)2420        int              TestCapability( const char * ) override { return FALSE; }
2421 };
2422 
2423 /************************************************************************/
2424 /*                   OGRMongoDBv3SingleFeatureLayer()                   */
2425 /************************************************************************/
2426 
OGRMongoDBv3SingleFeatureLayer(const char * pszVal)2427 OGRMongoDBv3SingleFeatureLayer::OGRMongoDBv3SingleFeatureLayer( const char *pszVal )
2428 {
2429     m_poFeatureDefn = new OGRFeatureDefn( "RESULT" );
2430     m_poFeatureDefn->Reference();
2431     OGRFieldDefn oField( "_json", OFTString );
2432     m_poFeatureDefn->AddFieldDefn( &oField );
2433 
2434     iNextShapeId = 0;
2435     osVal = pszVal;
2436 }
2437 
2438 /************************************************************************/
2439 /*                           GetNextFeature()                           */
2440 /************************************************************************/
2441 
GetNextFeature()2442 OGRFeature * OGRMongoDBv3SingleFeatureLayer::GetNextFeature()
2443 {
2444     if (iNextShapeId != 0)
2445         return nullptr;
2446 
2447     OGRFeature* poFeature = new OGRFeature(m_poFeatureDefn);
2448     poFeature->SetField(0, osVal);
2449     poFeature->SetFID(iNextShapeId ++);
2450     return poFeature;
2451 }
2452 
2453 /************************************************************************/
2454 /*                             ExecuteSQL()                             */
2455 /************************************************************************/
2456 
ExecuteSQL(const char * pszSQLCommand,OGRGeometry * poSpatialFilter,const char * pszDialect)2457 OGRLayer* OGRMongoDBv3Dataset::ExecuteSQL( const char *pszSQLCommand,
2458                                             OGRGeometry *poSpatialFilter,
2459                                             const char *pszDialect )
2460 {
2461     for( const auto& poLayer: m_apoLayers )
2462     {
2463         poLayer->SyncToDisk();
2464     }
2465 
2466 /* -------------------------------------------------------------------- */
2467 /*      Special case DELLAYER: command.                                 */
2468 /* -------------------------------------------------------------------- */
2469     if( STARTS_WITH_CI(pszSQLCommand, "DELLAYER:") )
2470     {
2471         const char *pszLayerName = pszSQLCommand + 9;
2472 
2473         while( *pszLayerName == ' ' )
2474             pszLayerName++;
2475 
2476         for( int iLayer = 0; iLayer < static_cast<int>(m_apoLayers.size()); iLayer++ )
2477         {
2478             if( EQUAL(m_apoLayers[iLayer]->GetName(),
2479                       pszLayerName ))
2480             {
2481                 DeleteLayer( iLayer );
2482                 break;
2483             }
2484         }
2485         return nullptr;
2486     }
2487 
2488 /* -------------------------------------------------------------------- */
2489 /*      Special case WRITE_OGR_METADATA command.                        */
2490 /* -------------------------------------------------------------------- */
2491     if( STARTS_WITH_CI(pszSQLCommand, "WRITE_OGR_METADATA ") )
2492     {
2493         if( eAccess != GA_Update )
2494         {
2495             CPLError(CE_Failure, CPLE_AppDefined, "Dataset opened in read-only mode");
2496             return nullptr;
2497         }
2498         const char* pszLayerName = pszSQLCommand + strlen("WRITE_OGR_METADATA ");
2499         auto poLayer = static_cast<OGRMongoDBv3Layer*>(GetLayerByName(pszLayerName));
2500         if( poLayer == nullptr )
2501         {
2502             CPLError(CE_Failure, CPLE_AppDefined, "Layer %s not found", pszLayerName);
2503             return nullptr;
2504         }
2505         poLayer->GetLayerDefn(); // force schema discovery
2506         poLayer->m_bLayerMetadataUpdatable = true;
2507         poLayer->m_bUpdateLayerMetadata = true;
2508         poLayer->SyncToDisk();
2509 
2510         return nullptr;
2511     }
2512 
2513     if( pszDialect != nullptr && EQUAL(pszDialect, "MONGODB") )
2514     {
2515         if( m_osDatabase.empty() )
2516         {
2517             CPLError(CE_Failure, CPLE_AppDefined,
2518                     "Cannot run ExecuteSQL() when dataset opened without explicit database");
2519             return nullptr;
2520         }
2521         try
2522         {
2523             auto ret = m_oConn[m_osDatabase].run_command(bsoncxx::from_json(pszSQLCommand));
2524             return new OGRMongoDBv3SingleFeatureLayer(bsoncxx::to_json(ret).c_str());
2525         }
2526         catch( const std::exception &ex)
2527         {
2528             CPLError(CE_Failure, CPLE_AppDefined,
2529                      "Command failed: %s", ex.what());
2530             return nullptr;
2531         }
2532     }
2533     else
2534     {
2535         return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
2536     }
2537 }
2538 
2539 /************************************************************************/
2540 /*                          ReleaseResultSet()                          */
2541 /************************************************************************/
2542 
ReleaseResultSet(OGRLayer * poLayer)2543 void OGRMongoDBv3Dataset::ReleaseResultSet( OGRLayer * poLayer )
2544 {
2545     delete poLayer;
2546 }
2547 
2548 /************************************************************************/
2549 /*                   OGRMongoDBv3DriverIdentify()                       */
2550 /************************************************************************/
2551 
OGRMongoDBv3DriverIdentify(GDALOpenInfo * poOpenInfo)2552 static int OGRMongoDBv3DriverIdentify( GDALOpenInfo* poOpenInfo )
2553 
2554 {
2555     return STARTS_WITH_CI(poOpenInfo->pszFilename, "MongoDBv3:") ||
2556            STARTS_WITH_CI(poOpenInfo->pszFilename, "mongodb+srv:") ||
2557            (STARTS_WITH_CI(poOpenInfo->pszFilename, "mongodb:") &&
2558             GDALGetDriverByName("MONGODB") == nullptr);
2559 }
2560 
2561 /************************************************************************/
2562 /*                     OGRMongoDBv3DriverOpen()                         */
2563 /************************************************************************/
2564 
OGRMongoDBv3DriverOpen(GDALOpenInfo * poOpenInfo)2565 static GDALDataset* OGRMongoDBv3DriverOpen( GDALOpenInfo* poOpenInfo )
2566 
2567 {
2568     if( !OGRMongoDBv3DriverIdentify(poOpenInfo) )
2569         return nullptr;
2570 
2571     {
2572         static std::mutex oMutex;
2573         std::lock_guard<std::mutex> oLock(oMutex);
2574         if( g_pInst == nullptr )
2575         {
2576             if( !g_bCanInstantiateMongo )
2577             {
2578                 CPLError(CE_Failure, CPLE_AppDefined,
2579                          "MongoDB client has been previously shut down and "
2580                          "can no longer be reinitialized");
2581                 return nullptr;
2582             }
2583 
2584 #ifdef use_logger
2585             class logger final : public mongocxx::logger
2586             {
2587             public:
2588                 explicit logger() {}
2589 
2590                 void operator()(mongocxx::log_level level,
2591                                 bsoncxx::stdx::string_view /*domain*/,
2592                                 bsoncxx::stdx::string_view message) noexcept override {
2593                     if (level >= mongocxx::log_level::k_trace)
2594                         return;
2595                     std::string tmp(message);
2596                     CPLDebug("MongoDBv3", "%s", tmp.c_str());
2597                 }
2598             };
2599 #endif
2600             g_pInst = new mongocxx::instance(
2601 #ifdef use_logger
2602                 bsoncxx::stdx::make_unique<logger>()
2603 #endif
2604             );
2605         }
2606     }
2607 
2608     auto poDS = new OGRMongoDBv3Dataset();
2609     if( !poDS->Open(poOpenInfo) )
2610     {
2611         delete poDS;
2612         poDS = nullptr;
2613     }
2614 
2615     return poDS;
2616 }
2617 
2618 /************************************************************************/
2619 /*                        OGRMongoDBv3DriverUnload()                    */
2620 /************************************************************************/
2621 
2622 extern "C" int GDALIsInGlobalDestructor();
2623 
OGRMongoDBv3DriverUnload(GDALDriver *)2624 static void OGRMongoDBv3DriverUnload( GDALDriver* )
2625 {
2626     if( g_pInst != nullptr && !GDALIsInGlobalDestructor() )
2627     {
2628         delete g_pInst;
2629         g_pInst = nullptr;
2630         g_bCanInstantiateMongo = false;
2631     }
2632 }
2633 /************************************************************************/
2634 /*                       RegisterOGRMongoDBv3()                         */
2635 /************************************************************************/
2636 
RegisterOGRMongoDBv3()2637 void RegisterOGRMongoDBv3()
2638 {
2639     if( GDALGetDriverByName( "MongoDBv3" ) != nullptr )
2640         return;
2641 
2642     GDALDriver *poDriver = new GDALDriver();
2643 
2644     poDriver->SetDescription( "MongoDBv3" );
2645     poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" );
2646     poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "MongoDB (using libmongocxx v3 client)" );
2647     poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "drivers/vector/mongodbv3.html" );
2648 
2649     poDriver->SetMetadataItem( GDAL_DMD_CONNECTION_PREFIX, "MongoDBv3:" );
2650 
2651     poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST,
2652 "<LayerCreationOptionList>"
2653 "  <Option name='OVERWRITE' type='boolean' description='Whether to overwrite an existing collection with the layer name to be created' default='NO'/>"
2654 "  <Option name='GEOMETRY_NAME' type='string' description='Name of geometry column.' default='geometry'/>"
2655 "  <Option name='SPATIAL_INDEX' type='boolean' description='Whether to create a spatial index' default='YES'/>"
2656 "  <Option name='FID' type='string' description='Field name, with integer values, to use as FID' default='ogc_fid'/>"
2657 "  <Option name='WRITE_OGR_METADATA' type='boolean' description='Whether to create a description of layer fields in the _ogr_metadata collection' default='YES'/>"
2658 "  <Option name='DOT_AS_NESTED_FIELD' type='boolean' description='Whether to consider dot character in field name as sub-document' default='YES'/>"
2659 "  <Option name='IGNORE_SOURCE_ID' type='boolean' description='Whether to ignore _id field in features passed to CreateFeature()' default='NO'/>"
2660 "</LayerCreationOptionList>");
2661 
2662     poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST,
2663 "<OpenOptionList>"
2664 "  <Option name='URI' type='string' description='Connection URI' />"
2665 "  <Option name='HOST' type='string' description='Server hostname' />"
2666 "  <Option name='PORT' type='integer' description='Server port' />"
2667 "  <Option name='DBNAME' type='string' description='Database name' />"
2668 "  <Option name='USER' type='string' description='User name' />"
2669 "  <Option name='PASSWORD' type='string' description='User password' />"
2670 "  <Option name='SSL_PEM_KEY_FILE' type='string' description='SSL PEM certificate/key filename' />"
2671 "  <Option name='SSL_PEM_KEY_PASSWORD' type='string' description='SSL PEM key password' />"
2672 "  <Option name='SSL_CA_FILE' type='string' description='SSL Certification Authority filename' />"
2673 "  <Option name='SSL_CRL_FILE' type='string' description='SSL Certification Revocation List filename' />"
2674 "  <Option name='SSL_ALLOW_INVALID_CERTIFICATES' type='boolean' description='Whether to allow connections to servers with invalid certificates' default='NO'/>"
2675 "  <Option name='BATCH_SIZE' type='integer' description='Number of features to retrieve per batch'/>"
2676 "  <Option name='FEATURE_COUNT_TO_ESTABLISH_FEATURE_DEFN' type='integer' description='Number of features to retrieve to establish feature definition. -1 = unlimited' default='100'/>"
2677 "  <Option name='JSON_FIELD' type='boolean' description='Whether to include a field with the full document as JSON' default='NO'/>"
2678 "  <Option name='FLATTEN_NESTED_ATTRIBUTES' type='boolean' description='Whether to recursively explore nested objects and produce flatten OGR attributes' default='YES'/>"
2679 "  <Option name='FID' type='string' description='Field name, with integer values, to use as FID' default='ogc_fid'/>"
2680 "  <Option name='USE_OGR_METADATA' type='boolean' description='Whether to use the _ogr_metadata collection to read layer metadata' default='YES'/>"
2681 "  <Option name='BULK_INSERT' type='boolean' description='Whether to use bulk insert for feature creation' default='YES'/>"
2682 "</OpenOptionList>");
2683 
2684     poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime Time IntegerList Integer64List RealList StringList Binary" );
2685     poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean" );
2686 
2687     poDriver->pfnOpen = OGRMongoDBv3DriverOpen;
2688     poDriver->pfnIdentify = OGRMongoDBv3DriverIdentify;
2689     poDriver->pfnUnloadDriver = OGRMongoDBv3DriverUnload;
2690 
2691     GetGDALDriverManager()->RegisterDriver( poDriver );
2692 }
2693