Files
api_antrean/internal/utils/query/exemple.go.txt
T
2025-11-03 05:56:41 +00:00

944 lines
33 KiB
Plaintext

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
// =============================================================================