package main import ( "context" "fmt" "log" "net/url" "time" "api-service/internal/config" "api-service/internal/database" "api-service/internal/utils/query" "api-service/internal/validation" "github.com/jmoiron/sqlx" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" ) // This file provides comprehensive examples of using the query builder library // for performing various database operations including CRUD, transactions, joins, etc. // Each example function demonstrates how to build queries, print them, and execute them. // ============================================================================= // DEFINISI MODEL (CONTOH) // ============================================================================= // User adalah contoh struct untuk tabel 'users'. type User struct { ID int `db:"id" bson:"_id,omitempty"` Name string `db:"name" bson:"name"` Email string `db:"email" bson:"email"` Status string `db:"status" bson:"status"` CreatedAt time.Time `db:"created_at" bson:"created_at"` } // Post adalah contoh struct untuk tabel 'posts'. type Post struct { ID int `db:"id" bson:"_id,omitempty"` UserID int `db:"user_id" bson:"user_id"` Title string `db:"title" bson:"title"` Content string `db:"content" bson:"content"` CreatedAt time.Time `db:"created_at" bson:"created_at"` } // Employee adalah contoh struct untuk tabel 'employees' dengan kolom JSON. type Employee struct { ID int `db:"id" bson:"_id,omitempty"` Name string `db:"name" bson:"name"` Department string `db:"department" bson:"department"` Salary float64 `db:"salary" bson:"salary"` Metadata map[string]interface{} `db:"metadata" bson:"metadata"` // Kolom JSON/JSONB } // ============================================================================= // FUNGSI UTAMA // ============================================================================= func main() { cfg := setupConfig() dbService := database.New(cfg) fmt.Println("============================================================") fmt.Println(" CONTOH 1: QUERY DASAR (SELECT, INSERT, UPDATE, DELETE)") fmt.Println("============================================================") basicCRUDExample(dbService) fmt.Println("\n============================================================") fmt.Println(" CONTOH 2: TRANSAKSI SQL (POSTGRESQL)") fmt.Println("============================================================") sqlTransactionExample(dbService) fmt.Println("\n============================================================") fmt.Println(" CONTOH 3: TRANSAKSI MONGODB") fmt.Println("============================================================") mongoTransactionExample(dbService) fmt.Println("\n============================================================") fmt.Println(" CONTOH 4: QUERY DENGAN FILTER DAN PAGINASI") fmt.Println("============================================================") filterAndPaginationExample(dbService) fmt.Println("\n============================================================") fmt.Println(" CONTOH 5: QUERY DENGAN JOIN") fmt.Println("============================================================") joinExample(dbService) fmt.Println("\n============================================================") fmt.Println(" CONTOH 6: QUERY DENGAN CTE (COMMON TABLE EXPRESSION)") fmt.Println("============================================================") cteExample(dbService) fmt.Println("\n============================================================") fmt.Println(" CONTOH 7: QUERY DENGAN WINDOW FUNCTION") fmt.Println("============================================================") windowFunctionExample(dbService) fmt.Println("\n============================================================") fmt.Println(" CONTOH 8: VALIDASI DATA DINAMIS") fmt.Println("============================================================") validationExample(dbService) fmt.Println("\n============================================================") fmt.Println(" CONTOH 9: OPERASI JSON") fmt.Println("============================================================") jsonQueryExample(dbService) fmt.Println("\n============================================================") fmt.Println(" CONTOH 10: QUERY MONGODB (CRUD & AGGREGATION)") fmt.Println("============================================================") mongodbExample(dbService) fmt.Println("\n============================================================") fmt.Println(" CONTOH 11: PENGGUNAAN READ REPLICA") fmt.Println("============================================================") readReplicaExample(dbService) fmt.Println("\n============================================================") fmt.Println(" CONTOH 12: HEALTH CHECK DATABASE") fmt.Println("============================================================") healthCheckExample(dbService) fmt.Println("\n============================================================") fmt.Println(" CONTOH 13: PARSING QUERY DARI URL") fmt.Println("============================================================") urlQueryParsingExample(dbService) } func setupConfig() *config.Config { return &config.Config{ Databases: map[string]config.DatabaseConfig{ "main": { Type: "postgres", Host: "localhost", Port: 5432, Username: "user", Password: "password", Database: "company_db", SSLMode: "disable", MaxOpenConns: 25, MaxIdleConns: 5, ConnMaxLifetime: time.Hour, }, }, "mongodb": config.DatabaseConfig{ Type: "mongodb", Host: "localhost", Port: 27017, Database: "company_db", Username: "user", Password: "password", }, } } // ============================================================================= // CONTOH 1: QUERY DASAR (CRUD) // ============================================================================= // basicCRUDExample demonstrates basic Create, Read, Update, Delete operations using the query builder. // It shows how to build SQL queries, print them, and execute them while displaying results. // Expected output: Prints INSERT SQL and result (new ID), SELECT SQL and user data, UPDATE SQL and affected rows, DELETE SQL and affected rows. // Example raw queries: // INSERT: INSERT INTO users (name, email, status) VALUES ($1, $2, $3) RETURNING id // SELECT: SELECT * FROM users WHERE id = $1 // UPDATE: UPDATE users SET status = $1 WHERE id = $2 // DELETE: DELETE FROM users WHERE id = $1 func basicCRUDExample(dbService database.Service) { ctx := context.Background() db, err := dbService.GetSQLXDB("main") if err != nil { log.Printf("Gagal mendapatkan koneksi DB: %v", err) return } qb := query.NewQueryBuilder(query.DBTypePostgreSQL) // --- INSERT --- fmt.Println("\n--- Operasi INSERT ---") insertData := query.InsertData{ Columns: []string{"name", "email", "status"}, Values: []interface{}{"Alice", "alice@example.com", "active"}, } sql, args, err := qb.BuildInsertQuery("users", insertData, "id") if err != nil { log.Printf("Error building INSERT: %v", err) return } fmt.Printf("Generated INSERT SQL: %s\nArgs: %v\n", sql, args) result, err := qb.ExecuteInsert(ctx, db, "users", insertData, "id") if err != nil { log.Printf("Error INSERT: %v", err) return } newID, _ := result.LastInsertId() fmt.Printf("-> INSERT: Berhasil menambah user dengan ID: %d\n", newID) // --- SELECT (Single Row) --- fmt.Println("\n--- Operasi SELECT ---") var user User selectQuery := query.DynamicQuery{ Fields: []query.SelectField{{Expression: "*"}}, From: "users", Filters: []query.FilterGroup{{ Filters: []query.DynamicFilter{{Column: "id", Operator: query.OpEqual, Value: newID}}, }}, } sql, args, err = qb.BuildQuery(selectQuery) if err != nil { log.Printf("Error building SELECT: %v", err) return } fmt.Printf("Generated SELECT SQL: %s\nArgs: %v\n", sql, args) err = qb.ExecuteQueryRow(ctx, db, selectQuery, &user) if err != nil { log.Printf("Error SELECT single row: %v", err) return } fmt.Printf("-> SELECT (Single Row): Berhasil mengambil user: %+v\n", user) // --- UPDATE --- fmt.Println("\n--- Operasi UPDATE ---") updateData := query.UpdateData{ Columns: []string{"status"}, Values: []interface{}{"inactive"}, } updateFilter := []query.FilterGroup{{ Filters: []query.DynamicFilter{{Column: "id", Operator: query.OpEqual, Value: newID}}, }} sql, args, err = qb.BuildUpdateQuery("users", updateData, updateFilter) if err != nil { log.Printf("Error building UPDATE: %v", err) return } fmt.Printf("Generated UPDATE SQL: %s\nArgs: %v\n", sql, args) _, err = qb.ExecuteUpdate(ctx, db, "users", updateData, updateFilter) if err != nil { log.Printf("Error UPDATE: %v", err) return } fmt.Printf("-> UPDATE: Berhasil memperbarui status user dengan ID: %d\n", newID) // --- DELETE --- fmt.Println("\n--- Operasi DELETE ---") deleteFilter := []query.FilterGroup{{ Filters: []query.DynamicFilter{{Column: "id", Operator: query.OpEqual, Value: newID}}, }} sql, args, err = qb.BuildDeleteQuery("users", deleteFilter) if err != nil { log.Printf("Error building DELETE: %v", err) return } fmt.Printf("Generated DELETE SQL: %s\nArgs: %v\n", sql, args) _, err = qb.ExecuteDelete(ctx, db, "users", deleteFilter) if err != nil { log.Printf("Error DELETE: %v", err) return } fmt.Printf("-> DELETE: Berhasil menghapus user dengan ID: %d\n", newID) } // ============================================================================= // CONTOH 2: TRANSAKSI SQL (POSTGRESQL) // ============================================================================= // sqlTransactionExample demonstrates how to perform atomic transactions involving updates // across multiple tables using the Query Builder. It builds and prints SQL queries before execution. // Expected output: Prints UPDATE SQL for salaries and employees, transaction commit/rollback status, and validation results. // Example raw queries: // UPDATE salaries: UPDATE salaries SET salary = $1 WHERE employee_id = $2 // UPDATE employees: UPDATE employees SET last_name = $1 WHERE employee_id = $2 func sqlTransactionExample(dbService database.Service) { ctx := context.Background() employeeID := 123 newSalary := 75000 newLastName := "Doe" db, err := dbService.GetSQLXDB("main") if err != nil { log.Fatalf("Gagal mendapatkan koneksi database SQL: %v", err) } qb := query.NewQueryBuilder(query.DBTypePostgreSQL) tx, err := db.BeginTxx(ctx, nil) if err != nil { log.Fatalf("Gagal memulai transaksi SQL: %v", err) } defer func() { if p := recover(); p != nil { fmt.Println("Terjadi panic, melakukan rollback transaksi...") _ = tx.Rollback() panic(p) } else if err != nil { fmt.Printf("Transaksi dibatalkan (ROLLBACK) karena error: %v\n", err) _ = tx.Rollback() } else { fmt.Println("Tidak ada error, melakukan COMMIT transaksi...") err = tx.Commit() if err != nil { log.Printf("Gagal melakukan COMMIT transaksi: %v", err) } } }() fmt.Printf("Memulai transaksi untuk employee_id: %d\n", employeeID) // --- Operasi 1: Update gaji di tabel 'salaries' --- fmt.Println("\n--- Operasi 1: UPDATE salaries ---") salariesUpdateData := query.UpdateData{ Columns: []string{"salary"}, Values: []interface{}{newSalary}, } salariesFilter := []query.FilterGroup{ { Filters: []query.DynamicFilter{ {Column: "employee_id", Operator: query.OpEqual, Value: employeeID}, }, }, } sql, args, err := qb.BuildUpdateQuery("salaries", salariesUpdateData, salariesFilter) if err != nil { log.Printf("Error building UPDATE salaries: %v", err) return } fmt.Printf("Generated UPDATE salaries SQL: %s\nArgs: %v\n", sql, args) salariesResult, err := qb.ExecuteUpdate(ctx, tx, "salaries", salariesUpdateData, salariesFilter) if err != nil { return } salariesRowsAffected, _ := salariesResult.RowsAffected() fmt.Printf("-> UPDATE salaries: %d baris terpengaruh.\n", salariesRowsAffected) // --- Operasi 2: Update informasi di tabel 'employees' --- fmt.Println("\n--- Operasi 2: UPDATE employees ---") employeesUpdateData := query.UpdateData{ Columns: []string{"last_name"}, Values: []interface{}{newLastName}, } employeesFilter := []query.FilterGroup{ { Filters: []query.DynamicFilter{ {Column: "employee_id", Operator: query.OpEqual, Value: employeeID}, }, }, } sql, args, err = qb.BuildUpdateQuery("employees", employeesUpdateData, employeesFilter) if err != nil { log.Printf("Error building UPDATE employees: %v", err) return } fmt.Printf("Generated UPDATE employees SQL: %s\nArgs: %v\n", sql, args) employeesResult, err := qb.ExecuteUpdate(ctx, tx, "employees", employeesUpdateData, employeesFilter) if err != nil { return } employeesRowsAffected, _ := employeesResult.RowsAffected() fmt.Printf("-> UPDATE employees: %d baris terpengaruh.\n", employeesRowsAffected) // --- Validasi Akhir Transaksi --- if salariesRowsAffected == 1 && employeesRowsAffected == 1 { fmt.Println("-> Validasi BERHASIL: Kedua tabel berhasil diperbarui.") } else { err = fmt.Errorf("validasi GAGAL: diharapkan 1 baris terupdate di setiap tabel, tetapi mendapat %d (salaries) dan %d (employees)", salariesRowsAffected, employeesRowsAffected) return } } // ============================================================================= // CONTOH 3: TRANSAKSI MONGODB // ============================================================================= // mongoTransactionExample demonstrates MongoDB transactions using the query builder. // It prints the filters and update operations before executing them in a transaction. // Expected output: Prints MongoDB filters and update operations for salaries and employees, transaction commit/abort status, and validation results. // Example raw queries: // MongoDB filters: {"employee_id": 123} // MongoDB updates: {"$set": {"salary": 75000}}, {"$set": {"last_name": "Doe"}} func mongoTransactionExample(dbService database.Service) { ctx := context.Background() employeeID := 123 newSalary := 75000 newLastName := "Doe" client, err := dbService.GetMongoClient("mongodb") if err != nil { log.Fatalf("Gagal mendapatkan klien MongoDB: %v", err) } salariesCollection := client.Database("company_db").Collection("salaries") employeesCollection := client.Database("company_db").Collection("employees") session, err := client.StartSession() if err != nil { log.Fatalf("Gagal memulai sesi MongoDB: %v", err) } defer session.EndSession(ctx) fmt.Printf("Memulai transaksi MongoDB untuk employee_id: %d\n", employeeID) _, err = session.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) { // --- Operasi 1: Update gaji di koleksi 'salaries' --- fmt.Println("\n--- Operasi 1: UPDATE salaries ---") salariesFilter := bson.M{"employee_id": employeeID} salariesUpdate := bson.M{"$set": bson.M{"salary": newSalary}} fmt.Printf("-> MongoDB Update Salaries Filter: %#v\n", salariesFilter) fmt.Printf("-> MongoDB Update Salaries Operation: %#v\n", salariesUpdate) salariesResult, err := salariesCollection.UpdateOne(sessCtx, salariesFilter, salariesUpdate) if err != nil { return nil, fmt.Errorf("gagal update koleksi salaries: %w", err) } fmt.Printf("-> UPDATE salaries: %d dokumen cocok (matched).\n", salariesResult.MatchedCount) // --- Operasi 2: Update informasi di koleksi 'employees' --- fmt.Println("\n--- Operasi 2: UPDATE employees ---") employeesFilter := bson.M{"employee_id": employeeID} employeesUpdate := bson.M{"$set": bson.M{"last_name": newLastName}} fmt.Printf("-> MongoDB Update Employees Filter: %#v\n", employeesFilter) fmt.Printf("-> MongoDB Update Employees Operation: %#v\n", employeesUpdate) employeesResult, err := employeesCollection.UpdateOne(sessCtx, employeesFilter, employeesUpdate) if err != nil { return nil, fmt.Errorf("gagal update koleksi employees: %w", err) } fmt.Printf("-> UPDATE employees: %d dokumen cocok (matched).\n", employeesResult.MatchedCount) // --- Validasi Akhir Transaksi --- if salariesResult.MatchedCount == 1 && employeesResult.MatchedCount == 1 { fmt.Println("-> Validasi BERHASIL: Kedua koleksi berhasil diperbarui.") return nil, nil } return nil, fmt.Errorf("validasi GAGAL: diharapkan 1 dokumen terupdate di setiap koleksi, tetapi mendapat %d (salaries) dan %d (employees)", salariesResult.MatchedCount, employeesResult.MatchedCount) }) if err != nil { fmt.Printf("Transaksi MongoDB dibatalkan (ABORT) karena error: %v\n", err) } else { fmt.Println("Transaksi MongoDB berhasil di-commit.") } } // ============================================================================= // CONTOH 4: FILTER DAN PAGINASI // ============================================================================= // filterAndPaginationExample demonstrates querying with filters and pagination. // It builds and prints the SELECT query before executing it. // Expected output: Prints SELECT SQL with filters and pagination, and the number of active users found. // Example raw query: // SELECT id, name FROM users WHERE (status = $1 AND created_at > $2) ORDER BY name ASC LIMIT 5 OFFSET 10 func filterAndPaginationExample(dbService database.Service) { ctx := context.Background() db, err := dbService.GetSQLXDB("main") if err != nil { log.Printf("Gagal mendapatkan koneksi DB: %v", err) return } qb := query.NewQueryBuilder(query.DBTypePostgreSQL) query := query.DynamicQuery{ Fields: []query.SelectField{ {Expression: "id"}, {Expression: "name"}, }, From: "users", Filters: []query.FilterGroup{ { LogicOp: "AND", Filters: []query.DynamicFilter{ {Column: "status", Operator: query.OpEqual, Value: "active"}, {Column: "created_at", Operator: query.OpGreaterThan, Value: time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)}, }, }, }, Sort: []query.SortField{{Column: "name", Order: "ASC"}}, Limit: 5, Offset: 10, } var users []User sql, args, err := qb.BuildQuery(query) if err != nil { log.Printf("Error building SELECT: %v", err) return } fmt.Printf("Generated SELECT SQL: %s\nArgs: %v\n", sql, args) err = qb.ExecuteQuery(ctx, db, query, &users) if err != nil { log.Printf("Error query dengan filter: %v", err) return } fmt.Printf("-> Filter & Paginasi: Ditemukan %d user aktif (halaman 3).\n", len(users)) } // ============================================================================= // CONTOH 5: QUERY DENGAN JOIN // ============================================================================= // joinExample demonstrates querying with JOIN operations. // It builds and prints the JOIN query before executing it. // Expected output: Prints JOIN SQL query and the number of posts with author names found. // Example raw query: // SELECT p.id AS post_id, p.title, u.name AS author_name FROM posts p INNER JOIN users u ON p.user_id = u.id LIMIT 10 func joinExample(dbService database.Service) { ctx := context.Background() db, err := dbService.GetSQLXDB("main") if err != nil { log.Printf("Gagal mendapatkan koneksi DB: %v", err) return } qb := query.NewQueryBuilder(query.DBTypePostgreSQL) query := query.DynamicQuery{ Fields: []query.SelectField{ {Expression: "p.id", Alias: "post_id"}, {Expression: "p.title"}, {Expression: "u.name", Alias: "author_name"}, }, From: "posts", Aliases: "p", Joins: []query.Join{ { Type: "INNER", Table: "users", Alias: "u", OnConditions: query.FilterGroup{ Filters: []query.DynamicFilter{ {Column: "p.user_id", Operator: query.OpEqual, Value: "u.id"}, }, }, }, }, Limit: 10, } var results []struct { PostID int `db:"post_id"` Title string `db:"title"` AuthorName string `db:"author_name"` } sql, args, err := qb.BuildQuery(query) if err != nil { log.Printf("Error building JOIN: %v", err) return } fmt.Printf("Generated JOIN SQL: %s\nArgs: %v\n", sql, args) err = qb.ExecuteQuery(ctx, db, query, &results) if err != nil { log.Printf("Error query JOIN: %v", err) return } fmt.Printf("-> JOIN: Ditemukan %d post dengan nama penulis.\n", len(results)) } // ============================================================================= // CONTOH 6: QUERY DENGAN CTE // ============================================================================= // cteExample demonstrates querying with Common Table Expressions (CTE). // It builds and prints the CTE query before executing it. // Expected output: Prints CTE SQL query and the number of users with more than 5 posts. func cteExample(dbService database.Service) { ctx := context.Background() db, err := dbService.GetSQLXDB("main") if err != nil { log.Printf("Gagal mendapatkan koneksi DB: %v", err) return } qb := query.NewQueryBuilder(query.DBTypePostgreSQL) query := query.DynamicQuery{ CTEs: []query.CTE{ { Name: "user_post_counts", Query: query.DynamicQuery{ Fields: []query.SelectField{ {Expression: "user_id"}, {Expression: "COUNT(*)", Alias: "post_count"}, }, From: "posts", GroupBy: []string{"user_id"}, }, }, }, Fields: []query.SelectField{ {Expression: "u.name"}, {Expression: "upc.post_count"}, }, From: "users u", Joins: []query.Join{ { Type: "INNER", Table: "user_post_counts", Alias: "upc", OnConditions: query.FilterGroup{ Filters: []query.DynamicFilter{ {Column: "u.id", Operator: query.OpEqual, Value: "upc.user_id"}, }, }, }, }, Filters: []query.FilterGroup{ { Filters: []query.DynamicFilter{ {Column: "upc.post_count", Operator: query.OpGreaterThan, Value: 5}, }, }, }, } var results []struct { Name string `db:"name"` PostCount int `db:"post_count"` } sql, args, err := qb.BuildQuery(query) if err != nil { log.Printf("Error building CTE: %v", err) return } fmt.Printf("Generated CTE SQL: %s\nArgs: %v\n", sql, args) err = qb.ExecuteQuery(ctx, db, query, &results) if err != nil { log.Printf("Error query CTE: %v", err) return } fmt.Printf("-> CTE: Ditemukan %d user dengan lebih dari 5 post.\n", len(results)) } // ============================================================================= // CONTOH 7: WINDOW FUNCTION // ============================================================================= // windowFunctionExample demonstrates querying with window functions. // It builds and prints the window function query before executing it. // Expected output: Prints window function SQL query and the number of employees with salary rankings. func windowFunctionExample(dbService database.Service) { ctx := context.Background() db, err := dbService.GetSQLXDB("main") if err != nil { log.Printf("Gagal mendapatkan koneksi DB: %v", err) return } qb := query.NewQueryBuilder(query.DBTypePostgreSQL) query := query.DynamicQuery{ Fields: []query.SelectField{ {Expression: "name"}, {Expression: "department"}, {Expression: "salary"}, }, From: "employees", WindowFunctions: []query.WindowFunction{ { Function: "RANK", Over: "department", OrderBy: "salary DESC", Alias: "salary_rank", }, }, Filters: []query.FilterGroup{ { Filters: []query.DynamicFilter{ {Column: "department", Operator: query.OpEqual, Value: "Engineering"}, }, }, }, } var results []struct { Name string `db:"name"` Department string `db:"department"` Salary float64 `db:"salary"` SalaryRank int `db:"salary_rank"` } sql, args, err := qb.BuildQuery(query) if err != nil { log.Printf("Error building Window Function: %v", err) return } fmt.Printf("Generated Window Function SQL: %s\nArgs: %v\n", sql, args) err = qb.ExecuteQuery(ctx, db, query, &results) if err != nil { log.Printf("Error query Window Function: %v", err) return } fmt.Printf("-> Window Function: Ditemukan %d employee di departemen Engineering dengan peringkat gaji.\n", len(results)) } // ============================================================================= // CONTOH 8: VALIDASI DATA DINAMIS // ============================================================================= // validationExample demonstrates dynamic data validation using the query builder. // It builds and prints the validation query before executing it. // Expected output: Prints validation SQL query and whether the email is duplicate or available. func validationExample(dbService database.Service) { ctx := context.Background() db, err := dbService.GetSQLXDB("main") if err != nil { log.Printf("Gagal mendapatkan koneksi DB: %v", err) return } qb := query.NewQueryBuilder(query.DBTypePostgreSQL) validator := validation.NewDynamicValidator(qb) userData := map[string]interface{}{"email": "test@example.com"} emailRule := validation.NewUniqueFieldRule("users", "email") // Build and print the validation query countQuery := query.DynamicQuery{ From: "users", Filters: []query.FilterGroup{{ Filters: []query.DynamicFilter{{Column: "email", Operator: query.OpEqual, Value: "test@example.com"}}, }}, } sql, args, err := qb.BuildCountQuery(countQuery) if err != nil { log.Printf("Error building validation query: %v", err) return } fmt.Printf("Generated Validation SQL: %s\nArgs: %v\n", sql, args) isDuplicate, err := validator.Validate(ctx, db, emailRule, userData) if err != nil { log.Printf("Error validasi: %v", err) return } if isDuplicate { fmt.Println("-> Validasi: Email 'test@example.com' sudah ada.") } else { fmt.Println("-> Validasi: Email 'test@example.com' tersedia.") } } // ============================================================================= // CONTOH 9: OPERASI JSON // ============================================================================= // jsonQueryExample demonstrates JSON operations in queries. // It builds and prints the JSON queries before executing them. // Expected output: Prints JSON SELECT and UPDATE SQL queries, number of employees found, and update success message. func jsonQueryExample(dbService database.Service) { ctx := context.Background() db, err := dbService.GetSQLXDB("main") if err != nil { log.Printf("Gagal mendapatkan koneksi DB: %v", err) return } qb := query.NewQueryBuilder(query.DBTypePostgreSQL) query := query.DynamicQuery{ Fields: []query.SelectField{{Expression: "*"}}, From: "employees", Filters: []query.FilterGroup{{ Filters: []query.DynamicFilter{ { Column: "metadata", Operator: query.OpJsonEqual, Value: "Engineering", Options: map[string]interface{}{"path": "department"}, }, }, }}, } var employees []Employee sql, args, err := qb.BuildQuery(query) if err != nil { log.Printf("Error building JSON query: %v", err) return } fmt.Printf("Generated JSON SELECT SQL: %s\nArgs: %v\n", sql, args) err = qb.ExecuteQuery(ctx, db, query, &employees) if err != nil { log.Printf("Error query JSON: %v", err) return } fmt.Printf("-> Operasi JSON: Ditemukan %d employee di departemen Engineering (dari metadata JSON).\n", len(employees)) updateData := query.UpdateData{ JsonUpdates: map[string]query.JsonUpdate{ "metadata": {Path: "role", Value: "Senior Developer"}, }, } filter := []query.FilterGroup{{Filters: []query.DynamicFilter{{Column: "id", Operator: query.OpEqual, Value: 1}}}} sql, args, err = qb.BuildUpdateQuery("employees", updateData, filter) if err != nil { log.Printf("Error building JSON update: %v", err) return } fmt.Printf("Generated JSON UPDATE SQL: %s\nArgs: %v\n", sql, args) _, err = qb.ExecuteUpdate(ctx, db, "employees", updateData, filter) if err != nil { log.Printf("Error update JSON: %v", err) return } fmt.Println("-> Operasi JSON: Berhasil memperbarui 'role' di metadata untuk employee ID 1.") } // ============================================================================= // CONTOH 10: QUERY MONGODB // ============================================================================= // mongodbExample demonstrates MongoDB queries using the query builder. // It prints the built filters and pipelines before executing them. // Expected output: Prints MongoDB find filter, number of active users, aggregation pipeline, and number of departments. func mongodbExample(dbService database.Service) { ctx := context.Background() client, err := dbService.GetMongoClient("mongodb") if err != nil { log.Printf("Gagal mendapatkan klien MongoDB: %v", err) return } collection := client.Database("company_db").Collection("users") mqb := query.NewMongoQueryBuilder() // --- FIND --- fmt.Println("\n--- Operasi FIND ---") findQuery := query.DynamicQuery{ Filters: []query.FilterGroup{{Filters: []query.DynamicFilter{{Column: "status", Operator: query.OpEqual, Value: "active"}}}}, Limit: 5, } filter, _, _ := mqb.BuildFindQuery(findQuery) fmt.Printf("-> MongoDB Find Filter: %#v\n", filter) var users []User err = mqb.ExecuteFind(ctx, collection, findQuery, &users) if err != nil { log.Printf("Error MongoDB Find: %v", err) return } fmt.Printf("-> MongoDB Find: Ditemukan %d user aktif.\n", len(users)) // --- AGGREGATION --- fmt.Println("\n--- Operasi AGGREGATION ---") aggQuery := query.DynamicQuery{ Fields: []query.SelectField{ {Expression: "department", Alias: "_id"}, {Expression: "COUNT(*)", Alias: "count"}, }, GroupBy: []string{"department"}, } pipeline, _ := mqb.BuildAggregateQuery(aggQuery) fmt.Printf("-> MongoDB Aggregation Pipeline: %#v\n", pipeline) var aggResults []struct { ID string `bson:"_id"` Count int `bson:"count"` } err = mqb.ExecuteAggregate(ctx, collection, aggQuery, &aggResults) if err != nil { log.Printf("Error MongoDB Aggregate: %v", err) return } fmt.Printf("-> MongoDB Aggregate: Ditemukan user di %d departemen.\n", len(aggResults)) } // ============================================================================= // CONTOH 11: PENGGUNAAN READ REPLICA // ============================================================================= // readReplicaExample demonstrates using read replicas for queries. // It builds and prints the count query before executing it on the read replica. // Expected output: Prints COUNT SQL query and the total number of users from the read replica. // Example raw query: // SELECT COUNT(*) FROM users func readReplicaExample(dbService database.Service) { ctx := context.Background() readDB, err := dbService.GetReadDB("main") if err != nil { log.Printf("Gagal mendapatkan read replica: %v", err) return } readxDB := sqlx.NewDb(readDB, "pgx") qb := query.NewQueryBuilder(query.DBTypePostgreSQL) countQuery := query.DynamicQuery{From: "users"} sql, args, err := qb.BuildCountQuery(countQuery) if err != nil { log.Printf("Error building count query: %v", err) return } fmt.Printf("Generated COUNT SQL: %s\nArgs: %v\n", sql, args) count, err := qb.ExecuteCount(ctx, readxDB, countQuery) if err != nil { log.Printf("Error query di read replica: %v", err) return } fmt.Printf("-> Read Replica: Total user (dari read replica): %d\n", count) } // ============================================================================= // CONTOH 12: HEALTH CHECK DATABASE // ============================================================================= // healthCheckExample demonstrates database health checks. // It prints the health status of all databases. // Expected output: Prints health status for each database (up/down with type or error). func healthCheckExample(dbService database.Service) { healthStatus := dbService.Health() fmt.Println("-> Health Check Status:") for dbName, status := range healthStatus { if status["status"] == "up" { fmt.Printf(" - Database %s: SEHAT (%s)\n", dbName, status["type"]) } else { fmt.Printf(" - Database %s: TIDAK SEHAT - %s\n", dbName, status["error"]) } } } // ============================================================================= // CONTOH 13: PARSING QUERY DARI URL // ============================================================================= // urlQueryParsingExample demonstrates parsing query parameters from URL. // It parses the URL query and prints the resulting dynamic query structure. // Expected output: Prints parsed fields, filters, sort, and limit from the URL query. func urlQueryParsingExample(dbService database.Service) { values := url.Values{} values.Set("fields", "id,name") values.Set("filter[status][_eq]", "active") values.Set("filter[age][_gt]", "25") values.Set("sort", "-name") values.Set("limit", "10") parser := query.NewQueryParser() dynamicQuery, err := parser.ParseQuery(values, "users") if err != nil { log.Printf("Error parsing URL query: %v", err) return } fmt.Println("-> Parsing URL Query:") fmt.Printf(" Fields: %v\n", dynamicQuery.Fields) fmt.Printf(" Filters: %+v\n", dynamicQuery.Filters) fmt.Printf(" Sort: %+v\n", dynamicQuery.Sort) fmt.Printf(" Limit: %d\n", dynamicQuery.Limit) } // ============================================================================= // AKHIR FILE // =============================================================================