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  }