initiate repo

This commit is contained in:
renaldybrada
2026-01-27 11:41:51 +07:00
commit ccf12a95b1
33 changed files with 8209 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+918
View File
@@ -0,0 +1,918 @@
package main
import (
"context"
"database/sql"
"fmt"
"log"
"time"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq" // PostgreSQL driver
"yourpackage/utils"
)
func main() {
// Inisialisasi koneksi database
db, err := sqlx.Connect("postgres", "user=postgres dbname=testdb sslmode=disable")
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
defer db.Close()
// Inisialisasi QueryBuilder
qb := utils.NewQueryBuilder(utils.DBTypePostgreSQL)
// Contoh penggunaan
simpleQueryExample(db, qb)
complexQueryExample(db, qb)
nestedJoinExample(db, qb)
multiJoinExample(db, qb)
commonQueriesExample(db, qb)
jsonQueryExample(db, qb)
windowFunctionExample(db, qb)
cteExample(db, qb)
unionExample(db, qb)
aggregateExample(db, qb)
}
func simpleQueryExample(db *sqlx.DB, qb *utils.QueryBuilder) {
fmt.Println("\n=== Simple Query Example ===")
// Query sederhana dengan filter
query := utils.DynamicQuery{
From: "users",
Fields: []utils.SelectField{
{Expression: "id", Alias: "user_id"},
{Expression: "name", Alias: "user_name"},
{Expression: "email"},
},
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "status", Operator: utils.OpEqual, Value: "active"},
{Column: "created_at", Operator: utils.OpGreaterThanEqual, Value: time.Now().AddDate(0, -1, 0)},
},
LogicOp: "AND",
},
},
Sort: []utils.SortField{
{Column: "name", Order: "ASC"},
},
Limit: 10,
}
var results []map[string]interface{}
err := qb.ExecuteQuery(context.Background(), db, query, &results)
if err != nil {
log.Printf("Error executing simple query: %v", err)
return
}
fmt.Printf("Found %d users\n", len(results))
for _, user := range results {
fmt.Printf("User: %+v\n", user)
}
}
func complexQueryExample(db *sqlx.DB, qb *utils.QueryBuilder) {
fmt.Println("\n=== Complex Query Example ===")
// Query dengan nested filter dan berbagai operator
query := utils.DynamicQuery{
From: "orders",
Fields: []utils.SelectField{
{Expression: "id", Alias: "order_id"},
{Expression: "customer_id"},
{Expression: "total_amount"},
{Expression: "order_date"},
{Expression: "status"},
},
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "status", Operator: utils.OpIn, Value: []string{"completed", "processing"}},
{Column: "total_amount", Operator: utils.OpGreaterThan, Value: 1000},
},
LogicOp: "AND",
},
{
Filters: []utils.DynamicFilter{
{Column: "order_date", Operator: utils.OpBetween, Value: []interface{}{time.Now().AddDate(0, -3, 0), time.Now()}},
{Column: "customer_id", Operator: utils.OpNotIn, Value: []int{1, 2, 3}},
},
LogicOp: "OR",
},
},
Sort: []utils.SortField{
{Column: "order_date", Order: "DESC"},
{Column: "total_amount", Order: "DESC"},
},
Limit: 20,
Offset: 10,
}
var results []map[string]interface{}
err := qb.ExecuteQuery(context.Background(), db, query, &results)
if err != nil {
log.Printf("Error executing complex query: %v", err)
return
}
fmt.Printf("Found %d orders\n", len(results))
for _, order := range results {
fmt.Printf("Order: %+v\n", order)
}
}
func nestedJoinExample(db *sqlx.DB, qb *utils.QueryBuilder) {
fmt.Println("\n=== Nested Join Example ===")
// Query dengan nested join
query := utils.DynamicQuery{
From: "customers",
Fields: []utils.SelectField{
{Expression: "customers.id", Alias: "customer_id"},
{Expression: "customers.name", Alias: "customer_name"},
{Expression: "orders.id", Alias: "order_id"},
{Expression: "orders.total_amount"},
{Expression: "order_items.product_id"},
{Expression: "order_items.quantity"},
{Expression: "products.name", Alias: "product_name"},
},
Joins: []utils.Join{
{
Type: "LEFT",
Table: "orders",
Alias: "orders",
OnConditions: utils.FilterGroup{
Filters: []utils.DynamicFilter{
{Column: "customers.id", Operator: utils.OpEqual, Value: "orders.customer_id"},
},
},
},
{
Type: "LEFT",
Table: "order_items",
Alias: "order_items",
OnConditions: utils.FilterGroup{
Filters: []utils.DynamicFilter{
{Column: "orders.id", Operator: utils.OpEqual, Value: "order_items.order_id"},
},
},
},
{
Type: "LEFT",
Table: "products",
Alias: "products",
OnConditions: utils.FilterGroup{
Filters: []utils.DynamicFilter{
{Column: "order_items.product_id", Operator: utils.OpEqual, Value: "products.id"},
},
},
},
},
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "customers.status", Operator: utils.OpEqual, Value: "active"},
{Column: "orders.status", Operator: utils.OpEqual, Value: "completed"},
},
LogicOp: "AND",
},
},
Sort: []utils.SortField{
{Column: "customers.name", Order: "ASC"},
{Column: "orders.id", Order: "DESC"},
},
Limit: 50,
}
var results []map[string]interface{}
err := qb.ExecuteQuery(context.Background(), db, query, &results)
if err != nil {
log.Printf("Error executing nested join query: %v", err)
return
}
fmt.Printf("Found %d customer-order-product records\n", len(results))
for _, record := range results {
fmt.Printf("Record: %+v\n", record)
}
}
func multiJoinExample(db *sqlx.DB, qb *utils.QueryBuilder) {
fmt.Println("\n=== Multi Join Example ===")
// Query dengan multiple join types
query := utils.DynamicQuery{
From: "employees",
Fields: []utils.SelectField{
{Expression: "employees.id", Alias: "employee_id"},
{Expression: "employees.name", Alias: "employee_name"},
{Expression: "departments.name", Alias: "department_name"},
{Expression: "projects.name", Alias: "project_name"},
{Expression: "tasks.title", Alias: "task_title"},
{Expression: "task_assignments.assigned_date"},
},
Joins: []utils.Join{
{
Type: "INNER",
Table: "departments",
Alias: "departments",
OnConditions: utils.FilterGroup{
Filters: []utils.DynamicFilter{
{Column: "employees.department_id", Operator: utils.OpEqual, Value: "departments.id"},
},
},
},
{
Type: "LEFT",
Table: "task_assignments",
Alias: "task_assignments",
OnConditions: utils.FilterGroup{
Filters: []utils.DynamicFilter{
{Column: "employees.id", Operator: utils.OpEqual, Value: "task_assignments.employee_id"},
},
},
},
{
Type: "LEFT",
Table: "tasks",
Alias: "tasks",
OnConditions: utils.FilterGroup{
Filters: []utils.DynamicFilter{
{Column: "task_assignments.task_id", Operator: utils.OpEqual, Value: "tasks.id"},
},
},
},
{
Type: "LEFT",
Table: "projects",
Alias: "projects",
OnConditions: utils.FilterGroup{
Filters: []utils.DynamicFilter{
{Column: "tasks.project_id", Operator: utils.OpEqual, Value: "projects.id"},
},
},
},
},
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "employees.status", Operator: utils.OpEqual, Value: "active"},
{Column: "departments.status", Operator: utils.OpEqual, Value: "active"},
},
LogicOp: "AND",
},
},
Sort: []utils.SortField{
{Column: "departments.name", Order: "ASC"},
{Column: "employees.name", Order: "ASC"},
{Column: "task_assignments.assigned_date", Order: "DESC"},
},
Limit: 100,
}
var results []map[string]interface{}
err := qb.ExecuteQuery(context.Background(), db, query, &results)
if err != nil {
log.Printf("Error executing multi join query: %v", err)
return
}
fmt.Printf("Found %d employee-task-project records\n", len(results))
for _, record := range results {
fmt.Printf("Record: %+v\n", record)
}
}
func commonQueriesExample(db *sqlx.DB, qb *utils.QueryBuilder) {
fmt.Println("\n=== Common Queries Example ===")
// 1. Query dengan LIKE/ILIKE
likeQuery := utils.DynamicQuery{
From: "products",
Fields: []utils.SelectField{
{Expression: "id"},
{Expression: "name"},
{Expression: "price"},
{Expression: "category"},
},
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "name", Operator: utils.OpILike, Value: "%laptop%"},
{Column: "category", Operator: utils.OpEqual, Value: "electronics"},
},
LogicOp: "AND",
},
},
Sort: []utils.SortField{
{Column: "price", Order: "ASC"},
},
Limit: 10,
}
var products []map[string]interface{}
err := qb.ExecuteQuery(context.Background(), db, likeQuery, &products)
if err != nil {
log.Printf("Error executing LIKE query: %v", err)
} else {
fmt.Printf("Found %d products matching 'laptop'\n", len(products))
}
// 2. Query dengan pagination
page := 2
pageSize := 20
paginationQuery := utils.DynamicQuery{
From: "orders",
Fields: []utils.SelectField{
{Expression: "id"},
{Expression: "customer_id"},
{Expression: "total_amount"},
{Expression: "order_date"},
},
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "status", Operator: utils.OpEqual, Value: "completed"},
},
},
},
Sort: []utils.SortField{
{Column: "order_date", Order: "DESC"},
},
Limit: pageSize,
Offset: (page - 1) * pageSize,
}
var orders []map[string]interface{}
err = qb.ExecuteQuery(context.Background(), db, paginationQuery, &orders)
if err != nil {
log.Printf("Error executing pagination query: %v", err)
} else {
fmt.Printf("Found %d orders on page %d\n", len(orders), page)
}
// 3. Query dengan NULL/NOT NULL
nullQuery := utils.DynamicQuery{
From: "customers",
Fields: []utils.SelectField{
{Expression: "id"},
{Expression: "name"},
{Expression: "email"},
{Expression: "phone"},
},
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "email", Operator: utils.OpNotNull},
{Column: "phone", Operator: utils.OpNull},
},
LogicOp: "AND",
},
},
Limit: 10,
}
var customers []map[string]interface{}
err = qb.ExecuteQuery(context.Background(), db, nullQuery, &customers)
if err != nil {
log.Printf("Error executing NULL query: %v", err)
} else {
fmt.Printf("Found %d customers with email but no phone\n", len(customers))
}
// 4. Query dengan BETWEEN
betweenQuery := utils.DynamicQuery{
From: "transactions",
Fields: []utils.SelectField{
{Expression: "id"},
{Expression: "account_id"},
{Expression: "amount"},
{Expression: "transaction_date"},
},
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "amount", Operator: utils.OpBetween, Value: []interface{}{100, 1000}},
{Column: "transaction_date", Operator: utils.OpBetween, Value: []interface{}{time.Now().AddDate(0, -1, 0), time.Now()}},
},
LogicOp: "AND",
},
},
Sort: []utils.SortField{
{Column: "transaction_date", Order: "DESC"},
},
Limit: 20,
}
var transactions []map[string]interface{}
err = qb.ExecuteQuery(context.Background(), db, betweenQuery, &transactions)
if err != nil {
log.Printf("Error executing BETWEEN query: %v", err)
} else {
fmt.Printf("Found %d transactions between $100 and $1000 in the last month\n", len(transactions))
}
}
func jsonQueryExample(db *sqlx.DB, qb *utils.QueryBuilder) {
fmt.Println("\n=== JSON Query Example ===")
// Query dengan operasi JSON
query := utils.DynamicQuery{
From: "products",
Fields: []utils.SelectField{
{Expression: "id"},
{Expression: "name"},
{Expression: "price"},
{Expression: "attributes"},
},
JsonOperations: []utils.JsonOperation{
{
Type: "extract",
Column: "attributes",
Path: "$.color",
Alias: "color",
},
{
Type: "extract",
Column: "attributes",
Path: "$.size",
Alias: "size",
},
},
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{
Column: "attributes",
Operator: utils.OpJsonContains,
Value: map[string]interface{}{"category": "electronics"},
Options: map[string]interface{}{"path": "$"},
},
},
},
},
Sort: []utils.SortField{
{Column: "name", Order: "ASC"},
},
Limit: 10,
}
var results []map[string]interface{}
err := qb.ExecuteQuery(context.Background(), db, query, &results)
if err != nil {
log.Printf("Error executing JSON query: %v", err)
return
}
fmt.Printf("Found %d products with JSON attributes\n", len(results))
for _, product := range results {
fmt.Printf("Product: %+v\n", product)
}
}
func windowFunctionExample(db *sqlx.DB, qb *utils.QueryBuilder) {
fmt.Println("\n=== Window Function Example ===")
// Query dengan window functions
query := utils.DynamicQuery{
From: "sales",
Fields: []utils.SelectField{
{Expression: "id"},
{Expression: "salesperson_id"},
{Expression: "amount"},
{Expression: "sale_date"},
},
WindowFunctions: []utils.WindowFunction{
{
Function: "ROW_NUMBER",
Over: "salesperson_id",
OrderBy: "amount DESC",
Alias: "sales_rank",
},
{
Function: "SUM",
Over: "salesperson_id",
OrderBy: "sale_date",
Frame: "ROWS UNBOUNDED PRECEDING",
Alias: "running_total",
},
},
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "sale_date", Operator: utils.OpGreaterThanEqual, Value: time.Now().AddDate(0, -6, 0)},
},
},
},
Sort: []utils.SortField{
{Column: "salesperson_id", Order: "ASC"},
{Column: "amount", Order: "DESC"},
},
Limit: 50,
}
var results []map[string]interface{}
err := qb.ExecuteQuery(context.Background(), db, query, &results)
if err != nil {
log.Printf("Error executing window function query: %v", err)
return
}
fmt.Printf("Found %d sales records with window functions\n", len(results))
for _, sale := range results {
fmt.Printf("Sale: %+v\n", sale)
}
}
func cteExample(db *sqlx.DB, qb *utils.QueryBuilder) {
fmt.Println("\n=== CTE Example ===")
// Query dengan CTE
query := utils.DynamicQuery{
CTEs: []utils.CTE{
{
Name: "monthly_sales",
Query: utils.DynamicQuery{
Fields: []utils.SelectField{
{Expression: "salesperson_id"},
{Expression: "EXTRACT(MONTH FROM sale_date) AS month"},
{Expression: "SUM(amount) AS total"},
},
From: "sales",
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "sale_date", Operator: utils.OpGreaterThanEqual, Value: time.Now().AddDate(-1, 0, 0)},
},
},
},
GroupBy: []string{"salesperson_id", "EXTRACT(MONTH FROM sale_date)"},
},
},
{
Name: "top_salespeople",
Query: utils.DynamicQuery{
Fields: []utils.SelectField{
{Expression: "salesperson_id"},
{Expression: "SUM(total) AS yearly_total"},
},
From: "monthly_sales",
GroupBy: []string{"salesperson_id"},
Having: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "SUM(total)", Operator: utils.OpGreaterThan, Value: 10000},
},
},
},
},
},
},
Fields: []utils.SelectField{
{Expression: "salespeople.id"},
{Expression: "salespeople.name"},
{Expression: "top_salespeople.yearly_total"},
},
From: "salespeople",
Joins: []utils.Join{
{
Type: "INNER",
Table: "top_salespeople",
Alias: "top_salespeople",
OnConditions: utils.FilterGroup{
Filters: []utils.DynamicFilter{
{Column: "salespeople.id", Operator: utils.OpEqual, Value: "top_salespeople.salesperson_id"},
},
},
},
},
Sort: []utils.SortField{
{Column: "top_salespeople.yearly_total", Order: "DESC"},
},
Limit: 10,
}
var results []map[string]interface{}
err := qb.ExecuteQuery(context.Background(), db, query, &results)
if err != nil {
log.Printf("Error executing CTE query: %v", err)
return
}
fmt.Printf("Found %d top salespeople\n", len(results))
for _, salesperson := range results {
fmt.Printf("Salesperson: %+v\n", salesperson)
}
}
func unionExample(db *sqlx.DB, qb *utils.QueryBuilder) {
fmt.Println("\n=== UNION Example ===")
// Query dengan UNION
query := utils.DynamicQuery{
Fields: []utils.SelectField{
{Expression: "id"},
{Expression: "name"},
{Expression: "email"},
{Expression: "'customer' AS user_type"},
},
From: "customers",
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "status", Operator: utils.OpEqual, Value: "active"},
},
},
},
Unions: []utils.Union{
{
Type: "UNION ALL",
Query: utils.DynamicQuery{
Fields: []utils.SelectField{
{Expression: "id"},
{Expression: "name"},
{Expression: "email"},
{Expression: "'employee' AS user_type"},
},
From: "employees",
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "status", Operator: utils.OpEqual, Value: "active"},
},
},
},
},
},
},
Sort: []utils.SortField{
{Column: "name", Order: "ASC"},
},
Limit: 20,
}
var results []map[string]interface{}
err := qb.ExecuteQuery(context.Background(), db, query, &results)
if err != nil {
log.Printf("Error executing UNION query: %v", err)
return
}
fmt.Printf("Found %d users (customers + employees)\n", len(results))
for _, user := range results {
fmt.Printf("User: %+v\n", user)
}
}
func aggregateExample(db *sqlx.DB, qb *utils.QueryBuilder) {
fmt.Println("\n=== Aggregate Example ===")
// Query dengan fungsi agregasi
query := utils.DynamicQuery{
Fields: []utils.SelectField{
{Expression: "category"},
{Expression: "COUNT(*) AS product_count"},
{Expression: "AVG(price) AS avg_price"},
{Expression: "MIN(price) AS min_price"},
{Expression: "MAX(price) AS max_price"},
{Expression: "SUM(stock_quantity) AS total_stock"},
},
From: "products",
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "status", Operator: utils.OpEqual, Value: "active"},
},
},
},
GroupBy: []string{"category"},
Having: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "COUNT(*)", Operator: utils.OpGreaterThan, Value: 5},
},
},
},
Sort: []utils.SortField{
{Column: "product_count", Order: "DESC"},
},
Limit: 10,
}
var results []map[string]interface{}
err := qb.ExecuteQuery(context.Background(), db, query, &results)
if err != nil {
log.Printf("Error executing aggregate query: %v", err)
return
}
fmt.Printf("Found %d product categories\n", len(results))
for _, category := range results {
fmt.Printf("Category: %+v\n", category)
}
}
func crudOperationsExample(db *sqlx.DB, qb *utils.QueryBuilder) {
fmt.Println("\n=== CRUD Operations Example ===")
// INSERT
insertData := utils.InsertData{
Columns: []string{"name", "email", "status", "created_at"},
Values: []interface{}{"John Doe", "john@example.com", "active", time.Now()},
JsonValues: map[string]interface{}{
"preferences": map[string]interface{}{
"theme": "dark",
"language": "en",
},
},
}
result, err := qb.ExecuteInsert(context.Background(), db, "customers", insertData, "id")
if err != nil {
log.Printf("Error executing INSERT: %v", err)
return
}
id, err := result.LastInsertId()
if err != nil {
log.Printf("Error getting inserted ID: %v", err)
return
}
fmt.Printf("Inserted customer with ID: %d\n", id)
// UPDATE
updateData := utils.UpdateData{
Columns: []string{"name", "status"},
Values: []interface{}{"John Smith", "inactive"},
JsonUpdates: map[string]utils.JsonUpdate{
"preferences": {
Path: "$.theme",
Value: "light",
},
},
}
filters := []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "id", Operator: utils.OpEqual, Value: id},
},
},
}
result, err = qb.ExecuteUpdate(context.Background(), db, "customers", updateData, filters, "updated_at")
if err != nil {
log.Printf("Error executing UPDATE: %v", err)
return
}
rowsAffected, err := result.RowsAffected()
if err != nil {
log.Printf("Error getting rows affected: %v", err)
return
}
fmt.Printf("Updated %d customer(s)\n", rowsAffected)
// DELETE
result, err = qb.ExecuteDelete(context.Background(), db, "customers", filters)
if err != nil {
log.Printf("Error executing DELETE: %v", err)
return
}
rowsAffected, err = result.RowsAffected()
if err != nil {
log.Printf("Error getting rows affected: %v", err)
return
}
fmt.Printf("Deleted %d customer(s)\n", rowsAffected)
}
func mongoExample() {
fmt.Println("\n=== MongoDB Example ===")
// Inisialisasi koneksi MongoDB
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
log.Fatalf("Failed to connect to MongoDB: %v", err)
}
defer client.Disconnect(context.Background())
db := client.Database("testdb")
collection := db.Collection("users")
// Inisialisasi MongoQueryBuilder
mqb := utils.NewMongoQueryBuilder()
// Query sederhana
query := utils.DynamicQuery{
Fields: []utils.SelectField{
{Expression: "name"},
{Expression: "email"},
{Expression: "status"},
},
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "status", Operator: utils.OpEqual, Value: "active"},
{Column: "age", Operator: utils.OpGreaterThan, Value: 18},
},
LogicOp: "AND",
},
},
Sort: []utils.SortField{
{Column: "name", Order: "ASC"},
},
Limit: 10,
}
var results []map[string]interface{}
err = mqb.ExecuteFind(context.Background(), collection, query, &results)
if err != nil {
log.Printf("Error executing MongoDB query: %v", err)
return
}
fmt.Printf("Found %d users\n", len(results))
for _, user := range results {
fmt.Printf("User: %+v\n", user)
}
// Aggregation pipeline
aggQuery := utils.DynamicQuery{
Fields: []utils.SelectField{
{Expression: "department", Alias: "_id"},
{Expression: "COUNT(*)", Alias: "employee_count"},
{Expression: "AVG(salary)", Alias: "avg_salary"},
},
Filters: []utils.FilterGroup{
{
Filters: []utils.DynamicFilter{
{Column: "status", Operator: utils.OpEqual, Value: "active"},
},
},
},
GroupBy: []string{"department"},
Sort: []utils.SortField{
{Column: "employee_count", Order: "DESC"},
},
Limit: 10,
}
var aggResults []map[string]interface{}
err = mqb.ExecuteAggregate(context.Background(), collection, aggQuery, &aggResults)
if err != nil {
log.Printf("Error executing MongoDB aggregation: %v", err)
return
}
fmt.Printf("Found %d departments\n", len(aggResults))
for _, dept := range aggResults {
fmt.Printf("Department: %+v\n", dept)
}
}
func queryParserExample(db *sqlx.DB, qb *utils.QueryBuilder) {
fmt.Println("\n=== Query Parser Example ===")
// Inisialisasi QueryParser
qp := utils.NewQueryParser()
// Parse URL query parameters
values := url.Values{}
values.Add("fields", "id,name,email,status")
values.Add("filter[status][_eq]", "active")
values.Add("filter[created_at][_gte]", "2023-01-01")
values.Add("filter[age][_between]", "18,65")
values.Add("sort", "+name,-created_at")
values.Add("limit", "20")
values.Add("offset", "10")
// Parse query parameters into DynamicQuery
query, err := qp.ParseQuery(values, "users")
if err != nil {
log.Printf("Error parsing query: %v", err)
return
}
// Execute the parsed query
var results []map[string]interface{}
err = qb.ExecuteQuery(context.Background(), db, query, &results)
if err != nil {
log.Printf("Error executing parsed query: %v", err)
return
}
fmt.Printf("Found %d users using parsed query\n", len(results))
for _, user := range results {
fmt.Printf("User: %+v\n", user)
}
}
+943
View File
@@ -0,0 +1,943 @@
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
// =============================================================================