github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/documents/mongodb.go (about) 1 package documents 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "go.mongodb.org/mongo-driver/bson" 9 "go.mongodb.org/mongo-driver/bson/primitive" 10 "go.mongodb.org/mongo-driver/mongo" 11 "go.mongodb.org/mongo-driver/mongo/options" 12 13 // "github.com/mdaxf/iac/com" 14 "github.com/mdaxf/iac/logger" 15 // "github.com/mdaxf/iac/framework/documentdb/mongodb" 16 ) 17 18 type DocDB struct { 19 MongoDBClient *mongo.Client 20 MongoDBDatabase *mongo.Database 21 MongoDBCollection_TC *mongo.Collection 22 /* 23 */ 24 DatabaseType string 25 DatabaseConnection string 26 DatabaseName string 27 iLog logger.Log 28 monitoring bool 29 } 30 31 /* 32 var DatabaseType = "mongodb" 33 var DatabaseConnection = "mongodb://localhost:27017" 34 var DatabaseName = "IAC_CFG" 35 */ 36 37 // InitMongoDB initializes a MongoDB connection and returns a DocDB object. 38 // It takes two parameters: DatabaseConnection (the MongoDB connection string) and DatabaseName (the name of the database). 39 // It returns a pointer to a DocDB object and an error if any. 40 // The function logs the performance duration and recovers from any panics. 41 42 func InitMongoDB(DatabaseConnection string, DatabaseName string) (*DocDB, error) { 43 iLog := logger.Log{ModuleName: logger.Framework, User: "System", ControllerName: "MongoDB Connection"} 44 startTime := time.Now() 45 defer func() { 46 elapsed := time.Since(startTime) 47 iLog.PerformanceWithDuration("documents.InitMongoDB", elapsed) 48 }() 49 defer func() { 50 if err := recover(); err != nil { 51 iLog.Error(fmt.Sprintf("There is error to documents.InitMongoDB with error: %s", err)) 52 return 53 } 54 }() 55 56 doc := &DocDB{ 57 DatabaseConnection: DatabaseConnection, 58 DatabaseName: DatabaseName, 59 iLog: iLog, 60 monitoring: false, 61 } 62 63 _, err := doc.ConnectMongoDB() 64 65 if err != nil { 66 iLog.Error(fmt.Sprintf("There is error to connect to MongoDB with error: %s", err)) 67 return doc, err 68 } 69 70 if doc.monitoring == false { 71 go func() { 72 doc.MonitorAndReconnect() 73 }() 74 75 } 76 77 return doc, nil 78 } 79 80 // ConnectMongoDB establishes a connection to the MongoDB database. 81 // It returns a pointer to the DocDB struct and an error if any. 82 func (doc *DocDB) ConnectMongoDB() (*DocDB, error) { 83 // Measure the execution time of the function 84 startTime := time.Now() 85 defer func() { 86 elapsed := time.Since(startTime) 87 doc.iLog.PerformanceWithDuration("documents.ConnectMongoDB", elapsed) 88 }() 89 90 // Recover from any panics and log the error 91 defer func() { 92 if err := recover(); err != nil { 93 doc.iLog.Error(fmt.Sprintf("There is error to documents.ConnectMongoDB with error: %s", err)) 94 return 95 } 96 }() 97 /* 98 myMongodb := &mongodb.MyDocDB{ 99 DatabaseConnection: doc.DatabaseConnection, 100 DatabaseName: doc.DatabaseName, 101 } 102 103 err := myMongodb.Connect() 104 105 if err != nil { 106 doc.iLog.Error(fmt.Sprintf("There os error to connect to MongoDB %s %s", doc.DatabaseConnection, doc.DatabaseName)) 107 return nil, err 108 } 109 110 doc.MongoDBClient = myMongodb.MongoDBClient 111 doc.MongoDBDatabase = myMongodb.MongoDBDatabase 112 113 com.IACDocDBConn = &com.DocDB{ 114 DatabaseConnection: myMongodb.DatabaseConnection, 115 DatabaseName: myMongodb.DatabaseName, 116 MongoDBClient: myMongodb.MongoDBClient, 117 MongoDBDatabase: myMongodb.MongoDBDatabase} 118 119 return doc, nil */ 120 // Log the database connection details 121 doc.iLog.Info(fmt.Sprintf("Connect Database: %s %s", doc.DatabaseType, doc.DatabaseConnection)) 122 123 var err error 124 125 // Create a new MongoDB client 126 doc.MongoDBClient, err = mongo.NewClient(options.Client().ApplyURI(doc.DatabaseConnection)) 127 if err != nil { 128 doc.iLog.Critical(fmt.Sprintf("failed to connect mongodb with error: %s", err)) 129 return doc, err 130 } 131 132 // Create a context with a timeout 133 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 134 defer cancel() 135 136 // Connect to the MongoDB server 137 err = doc.MongoDBClient.Connect(ctx) 138 if err != nil { 139 doc.iLog.Critical(fmt.Sprintf("failed to connect mongodb with error: %s", err)) 140 return doc, err 141 } 142 143 // Set the MongoDB database 144 doc.MongoDBDatabase = doc.MongoDBClient.Database(doc.DatabaseName) 145 146 // if doc.monitoring == false { 147 // doc.monitorAndReconnect() 148 // } 149 150 err = doc.MongoDBClient.Ping(context.Background(), nil) 151 if err != nil { 152 doc.iLog.Critical(fmt.Sprintf("failed to connect mongodb with error: %s", err)) 153 return doc, err 154 } 155 return doc, err 156 } 157 158 func (doc *DocDB) MonitorAndReconnect() { 159 // Function execution logging 160 // iLog := logger.Log{ModuleName: logger.Framework, User: "System", ControllerName: "MongoDB.monitorAndReconnect"} 161 startTime := time.Now() 162 defer func() { 163 elapsed := time.Since(startTime) 164 doc.iLog.PerformanceWithDuration("MongoDB.monitorAndReconnect", elapsed) 165 }() 166 167 // Recover from any panics and log the error 168 defer func() { 169 if err := recover(); err != nil { 170 doc.iLog.Error(fmt.Sprintf("monitorAndReconnect defer error: %s", err)) 171 // ctx.JSON(http.StatusBadRequest, gin.H{"error": err}) 172 } 173 }() 174 doc.monitoring = true 175 for { 176 177 err := doc.MongoDBClient.Ping(context.Background(), nil) 178 if err != nil { 179 doc.iLog.Error(fmt.Sprintf("MongoDB connection lost with ping error %v, reconnecting...", err)) 180 181 _, err := doc.ConnectMongoDB() 182 183 if err != nil { 184 doc.iLog.Error(fmt.Sprintf("Failed to reconnect to MongoDB %s with error:%v", doc.DatabaseConnection, err)) 185 time.Sleep(5 * time.Second) // Wait before retrying 186 continue 187 } else { 188 time.Sleep(1 * time.Second) 189 doc.iLog.Debug(fmt.Sprintf("MongoDB reconnected successfully")) 190 continue 191 } 192 } else { 193 time.Sleep(1 * time.Second) // Check connection every 60 seconds 194 continue 195 } 196 } 197 198 } 199 200 // QueryCollection queries a MongoDB collection with the specified filter and projection. 201 // It returns an array of documents that match the filter, along with any error that occurred. 202 // The function logs the performance duration and recovers from any panics. 203 // The function uses the MongoDB Go driver to query the collection. 204 func (doc *DocDB) QueryCollection(collectionname string, filter bson.M, projection bson.M) ([]bson.M, error) { 205 startTime := time.Now() 206 defer func() { 207 elapsed := time.Since(startTime) 208 doc.iLog.PerformanceWithDuration("documents.QueryCollection", elapsed) 209 }() 210 211 defer func() { 212 if err := recover(); err != nil { 213 doc.iLog.Error(fmt.Sprintf("There is error to documents.QueryCollection with error: %s", err)) 214 return 215 } 216 }() 217 218 MongoDBCollection := doc.MongoDBDatabase.Collection(collectionname) 219 220 options := options.Find() 221 options.SetProjection(projection) 222 223 cursor, err := MongoDBCollection.Find(context.TODO(), filter, options) 224 225 if err != nil { 226 doc.iLog.Critical(fmt.Sprintf("failed to get data from collection with error: %s", err)) 227 } 228 229 defer cursor.Close(context.Background()) 230 231 var results []bson.M 232 233 for cursor.Next(context.Background()) { 234 var result bson.M 235 err := cursor.Decode(&result) 236 if err != nil { 237 doc.iLog.Error(fmt.Sprintf("failed to decode data from collection with error: %s", err)) 238 return nil, err 239 } 240 results = append(results, result) 241 } 242 243 if err := cursor.Err(); err != nil { 244 doc.iLog.Error(fmt.Sprintf("failed to get data from collection with error: %s", err)) 245 return nil, err 246 } 247 248 return results, nil 249 } 250 251 func (doc *DocDB) GetItembyUUID(collectionname string, uuid string) (bson.M, error) { 252 startTime := time.Now() 253 defer func() { 254 elapsed := time.Since(startTime) 255 doc.iLog.PerformanceWithDuration("documents.GetDefaultItembyName", elapsed) 256 }() 257 258 defer func() { 259 if err := recover(); err != nil { 260 doc.iLog.Error(fmt.Sprintf("There is error to documents.GetDefaultItembyName with error: %s", err)) 261 return 262 } 263 }() 264 265 MongoDBCollection := doc.MongoDBDatabase.Collection(collectionname) 266 267 filter := bson.M{"uuid": uuid} 268 269 doc.iLog.Debug(fmt.Sprintf("GetDefaultItembyName: %s from collection:%s", filter, collectionname)) 270 //var result bson.Raw 271 var result bson.M 272 err := MongoDBCollection.FindOne(context.Background(), filter).Decode(&result) 273 274 if err != nil { 275 doc.iLog.Error(fmt.Sprintf("failed to get data from collection with error: %s", err)) 276 } 277 doc.iLog.Debug(fmt.Sprintf("GetDefaultItembyName: %s", result)) 278 279 return result, err 280 } 281 282 // GetDefaultItembyName retrieves the default item from the specified collection by name. 283 // It takes the collection name and the name of the item as input parameters. 284 // It returns the retrieved item as a bson.M object and an error if any. 285 // The function logs the performance duration and recovers from any panics. 286 // The function uses the MongoDB Go driver to query the collection. 287 func (doc *DocDB) GetDefaultItembyName(collectionname string, name string) (bson.M, error) { 288 startTime := time.Now() 289 defer func() { 290 elapsed := time.Since(startTime) 291 doc.iLog.PerformanceWithDuration("documents.GetDefaultItembyName", elapsed) 292 }() 293 294 defer func() { 295 if err := recover(); err != nil { 296 doc.iLog.Error(fmt.Sprintf("There is error to documents.GetDefaultItembyName with error: %s", err)) 297 return 298 } 299 }() 300 301 MongoDBCollection := doc.MongoDBDatabase.Collection(collectionname) 302 303 filter := bson.M{"name": name, "isdefault": true} 304 305 doc.iLog.Debug(fmt.Sprintf("GetDefaultItembyName: %s from collection:%s", filter, collectionname)) 306 //var result bson.Raw 307 var result bson.M 308 err := MongoDBCollection.FindOne(context.Background(), filter).Decode(&result) 309 310 if err != nil { 311 doc.iLog.Error(fmt.Sprintf("failed to get data from collection with error: %s", err)) 312 } 313 doc.iLog.Debug(fmt.Sprintf("GetDefaultItembyName: %s", result)) 314 315 return result, err 316 } 317 318 // GetItembyID retrieves an item from the specified collection by its ID. 319 // It takes the collection name and the ID as parameters. 320 // It returns the item as a bson.M object and an error if any. 321 // The function logs the performance duration and recovers from any panics. 322 // The function uses the MongoDB Go driver to query the collection. 323 324 func (doc *DocDB) GetItembyID(collectionname string, id string) (bson.M, error) { 325 startTime := time.Now() 326 defer func() { 327 elapsed := time.Since(startTime) 328 doc.iLog.PerformanceWithDuration("documents.GetItembyID", elapsed) 329 }() 330 331 defer func() { 332 if err := recover(); err != nil { 333 doc.iLog.Error(fmt.Sprintf("There is error to documents.GetItembyID with error: %s", err)) 334 return 335 } 336 }() 337 338 MongoDBCollection := doc.MongoDBDatabase.Collection(collectionname) 339 340 objectid, err := primitive.ObjectIDFromHex(id) 341 if err != nil { 342 doc.iLog.Error(fmt.Sprintf("failed to convert id to objectid with error: %s", err)) 343 } 344 345 filter := bson.M{"_id": objectid} 346 347 doc.iLog.Debug(fmt.Sprintf("GetItembyID: %s from collection:%s", filter, collectionname)) 348 //var result bson.Raw 349 var result bson.M 350 err = MongoDBCollection.FindOne(context.Background(), filter).Decode(&result) 351 352 if err != nil { 353 doc.iLog.Error(fmt.Sprintf("failed to get data from collection with error: %s", err)) 354 } 355 doc.iLog.Debug(fmt.Sprintf("GetItembyID: %s", result)) 356 /* 357 jsonBytes, err := bson.MarshalExtJSON(result, true, false) 358 if err != nil { 359 doc.iLog.Error(fmt.Sprintf("failed to convert data to json with error: %s", err)) 360 } 361 jsonString := string(jsonBytes) 362 doc.iLog.Debug(fmt.Sprintf("GetItembyID result: %s", jsonString)) 363 */ 364 return result, err 365 } 366 367 // GetItembyName retrieves an item from the specified collection by its name. 368 // It takes the collection name and the name as parameters. 369 // It returns the item as a bson.M object and an error if any. 370 // The function logs the performance duration and recovers from any panics. 371 // The function uses the MongoDB Go driver to query the collection. 372 func (doc *DocDB) UpdateCollection(collectionname string, filter bson.M, update bson.M, idata interface{}) error { 373 startTime := time.Now() 374 defer func() { 375 elapsed := time.Since(startTime) 376 doc.iLog.PerformanceWithDuration("documents.UpdateCollection", elapsed) 377 }() 378 379 defer func() { 380 if err := recover(); err != nil { 381 doc.iLog.Error(fmt.Sprintf("There is error to documents.UpdateCollection with error: %s", err)) 382 return 383 } 384 }() 385 386 MongoDBCollection := doc.MongoDBDatabase.Collection(collectionname) 387 388 if update == nil && idata != nil { 389 390 data, err := doc.convertToBsonM(idata) 391 if err != nil { 392 doc.iLog.Error(fmt.Sprintf("failed to update data from collection with error: %s", err)) 393 return err 394 } 395 _, err = MongoDBCollection.ReplaceOne(context.Background(), filter, data) 396 if err != nil { 397 doc.iLog.Error(fmt.Sprintf("failed to update data from collection with error: %s", err)) 398 } 399 return err 400 } else { 401 _, err := MongoDBCollection.UpdateOne(context.Background(), filter, update) 402 403 if err != nil { 404 doc.iLog.Critical(fmt.Sprintf("failed to update data from collection with error: %s", err)) 405 } 406 return err 407 } 408 409 } 410 411 // InsertCollection inserts a new item into the specified collection. 412 // It takes the collection name and the item as parameters. 413 // It returns the result of the insert operation and an error if any. 414 // The function logs the performance duration and recovers from any panics. 415 // The function uses the MongoDB Go driver to insert the item into the collection. 416 417 func (doc *DocDB) InsertCollection(collectionname string, idata interface{}) (*mongo.InsertOneResult, error) { 418 startTime := time.Now() 419 defer func() { 420 elapsed := time.Since(startTime) 421 doc.iLog.PerformanceWithDuration("documents.InsertCollection", elapsed) 422 }() 423 424 defer func() { 425 if err := recover(); err != nil { 426 doc.iLog.Error(fmt.Sprintf("There is error to documents.InsertCollection with error: %s", err)) 427 return 428 } 429 }() 430 MongoDBCollection := doc.MongoDBDatabase.Collection(collectionname) 431 432 data, err := doc.convertToBsonM(idata) 433 if err != nil { 434 doc.iLog.Error(fmt.Sprintf("failed to update data from collection with error: %s", err)) 435 return nil, err 436 } 437 438 insertResult, err := MongoDBCollection.InsertOne(context.Background(), data) 439 440 if err != nil { 441 doc.iLog.Error(fmt.Sprintf("failed to insert data from collection with error: %s", err)) 442 } 443 444 return insertResult, err 445 } 446 447 // DeleteItemFromCollection deletes an item from the specified collection by its ID. 448 // It takes the collection name and the ID as parameters. 449 // It returns an error if any. 450 // The function logs the performance duration and recovers from any panics. 451 // The function uses the MongoDB Go driver to delete the item from the collection. 452 func (doc *DocDB) DeleteItemFromCollection(collectionname string, documentid string) error { 453 startTime := time.Now() 454 defer func() { 455 elapsed := time.Since(startTime) 456 doc.iLog.PerformanceWithDuration("documents.DeleteItemFromCollection", elapsed) 457 }() 458 459 defer func() { 460 if err := recover(); err != nil { 461 doc.iLog.Error(fmt.Sprintf("There is error to documents.DeleteItemFromCollection with error: %s", err)) 462 return 463 } 464 }() 465 466 doc.iLog.Debug(fmt.Sprintf("Delete the item %s from collection %s", documentid, collectionname)) 467 468 MongoDBCollection := doc.MongoDBDatabase.Collection(collectionname) 469 470 objectid, err := primitive.ObjectIDFromHex(documentid) 471 if err != nil { 472 doc.iLog.Error(fmt.Sprintf("failed to convert id to objectid with error: %s", err)) 473 return err 474 } 475 476 filter := bson.M{"_id": objectid} 477 478 _, err = MongoDBCollection.DeleteOne(context.Background(), filter) 479 480 if err != nil { 481 doc.iLog.Error(fmt.Sprintf("Delete the item %s from collection %s error %s!", documentid, collectionname, err)) 482 return err 483 } 484 return nil 485 } 486 487 // DeleteCollection deletes a collection from the MongoDB database. 488 // It takes the collection name as a parameter. 489 // It returns an error if any. 490 // The function logs the performance duration and recovers from any panics. 491 // The function uses the MongoDB Go driver to delete the collection. 492 493 func (doc *DocDB) convertToBsonM(data interface{}) (bson.M, error) { 494 startTime := time.Now() 495 defer func() { 496 elapsed := time.Since(startTime) 497 doc.iLog.PerformanceWithDuration("documents.convertToBsonM", elapsed) 498 }() 499 /* 500 defer func() { 501 if err := recover(); err != nil { 502 doc.iLog.Error(fmt.Sprintf("There is error to documents.convertToBsonM with error: %s", err)) 503 return 504 } 505 }() 506 */dataBytes, err := bson.Marshal(data) 507 if err != nil { 508 doc.iLog.Error(fmt.Sprintf("failed to convert data to bson.M with error: %s", err)) 509 return nil, err 510 } 511 var dataBsonM bson.M 512 err = bson.Unmarshal(dataBytes, &dataBsonM) 513 if err != nil { 514 doc.iLog.Error(fmt.Sprintf("failed to convert data to bson.M with error: %s", err)) 515 return nil, err 516 } 517 return dataBsonM, nil 518 }