Files
satusehat-worker/pkg/utils/query/Document.doc
T
2026-04-14 01:21:54 +00:00

350 lines
9.7 KiB
Plaintext

# 📚 Manual Penggunaan Lengkap Pustaka Query Builder
Dokumen ini merupakan panduan komprehensif untuk menggunakan pustaka `Query Builder` (berada di `pkg/utils/query`). Pustaka ini dirancang sebagai abstraksi query dinamis yang aman dari *SQL Injection*, mendukung berbagai dialek SQL (PostgreSQL, MySQL, SQL Server, SQLite), MongoDB, serta kompatibel penuh dengan operasi transaksi.
---
## 📋 Daftar Isi
1. Inisialisasi & Konfigurasi
2. Fungsi Helper (Membuat Query Cepat)
3. Penggunaan Sederhana (CRUD Dasar SQL)
4. Penggunaan Lanjutan (Advanced SQL)
5. Transaksi (Transactions)
6. Dukungan MongoDB
7. Query Parser (API URL Parameter)
8. Daftar Operator Filter
---
## 1. Inisialisasi & Konfigurasi
Pustaka ini menyediakan beberapa cara untuk inisialisasi, baik menggunakan *Builder* langsung maupun melalui *Query Manager* yang mengumpulkan fungsi SQL, MongoDB, dan Parser di satu tempat.
```go
import "your_project/pkg/utils/query"
// A. Menggunakan Manager (Direkomendasikan)
qm := query.NewQueryManager(query.DBTypePostgreSQL)
sqlBuilder := qm.GetSQLBuilder()
mongoBuilder := qm.GetMongoBuilder()
parser := qm.GetQueryParser()
// B. Menggunakan Instansiasi Spesifik Dialek
pgBuilder := query.ForPostgreSQL()
mysqlBuilder := query.ForMySQL()
mongoBuilder := query.ForMongoDB()
// C. Menggunakan Konfigurasi Keamanan (Sangat Disarankan)
sqlBuilder.
SetSecurityOptions(true, 1000). // Enable security & set max rows
SetAllowedTables([]string{"users"}). // Whitelist tabel
SetAllowedColumns([]string{"id", "name"}).// Whitelist kolom
SetQueryLogging(true). // Aktifkan log query
SetQueryTimeout(30 * time.Second) // Timeout query
```
---
## 2. Penggunaan Sederhana (CRUD Dasar)
*Query Builder* menggunakan tipe `sqlx.ExtContext` untuk eksekutornya, yang berarti Anda dapat melemparkan `*sqlx.DB` (koneksi normal) maupun `*sqlx.Tx` (transaksi) ke dalamnya.
### A. SELECT (Mengambil Data)
```go
var users []User
// Definisi struktur query
q := query.DynamicQuery{
From: "users",
Fields: []query.SelectField{
{Expression: "id"},
{Expression: "name"},
},
Filters: []query.FilterGroup{{
LogicOp: "AND",
Filters: []query.DynamicFilter{
query.CreateEqualFilter("status", "active"),
query.CreateFilter("age", query.OpGreaterThan, 18),
},
}},
Sort: []query.SortField{
query.CreateDescSort("created_at"),
},
Limit: 10,
Offset: 0,
}
// Eksekusi query (db adalah *sqlx.DB)
err := sqlBuilder.ExecuteQuery(ctx, db, q, &users)
```
### B. INSERT (Menambah Data)
```go
insertData := query.InsertData{
Columns: []string{"name", "email", "status"},
Values: []interface{}{"John Doe", "[email protected]", "active"},
}
// Eksekusi Insert (mengembalikan sql.Result)
// Opsional: tambahkan kolom di akhir parameter untuk klausa RETURNING (hanya di Postgres)
result, err := sqlBuilder.ExecuteInsert(ctx, db, "users", insertData, "id")
```
### C. UPDATE (Mengubah Data)
```go
updateData := query.UpdateData{
Columns: []string{"status"},
Values: []interface{}{"inactive"},
}
filters := []query.FilterGroup{{
Filters: []query.DynamicFilter{
query.CreateEqualFilter("id", 1),
},
}}
result, err := sqlBuilder.ExecuteUpdate(ctx, db, "users", updateData, filters)
```
### D. DELETE (Menghapus Data)
```go
filters := []query.FilterGroup{{
Filters: []query.DynamicFilter{
query.CreateEqualFilter("id", 1),
},
}}
result, err := sqlBuilder.ExecuteDelete(ctx, db, "users", filters)
```
### E. COUNT (Menghitung Jumlah Baris)
```go
q := query.DynamicQuery{
From: "users",
Filters: []query.FilterGroup{{
Filters: []query.DynamicFilter{
query.CreateEqualFilter("status", "active"),
},
}},
}
count, err := sqlBuilder.ExecuteCount(ctx, db, q)
```
---
## 3. Penggunaan Bertingkat / Lanjutan
### A. JOIN Tables
Mendukung relasi tabel (Inner, Left, Right, Full, dan Lateral Joins).
```go
q := query.DynamicQuery{
From: "orders",
Aliases: "o",
Fields: []query.SelectField{
{Expression: "o.id"},
{Expression: "u.name", Alias: "customer_name"},
},
Joins: []query.Join{
{
Type: "LEFT",
Table: "users",
Alias: "u",
OnConditions: query.FilterGroup{
Filters: []query.DynamicFilter{
// Bandingkan kolom dengan kolom lainnya (tanpa di-treat sebagai string biasa)
{Column: "o.user_id", Operator: query.OpEqual, Value: "u.id"},
},
},
},
},
}
```
### B. Common Table Expressions (CTE) / Klausa WITH
Berguna untuk membuat sub-query *temporary* yang dapat digunakan dalam query utama.
```go
q := query.DynamicQuery{
CTEs: []query.CTE{
{
Name: "active_users",
Query: query.DynamicQuery{
From: "users",
Filters: []query.FilterGroup{{
Filters: []query.DynamicFilter{query.CreateEqualFilter("status", "active")},
}},
},
},
},
From: "active_users", // Query utama select dari CTE di atas
}
```
### C. Window Functions
Mendukung eksekusi fungsi analitik (cth: `ROW_NUMBER()`, `RANK()`).
```go
q := query.DynamicQuery{
From: "sales",
Fields: []query.SelectField{
{Expression: "employee_id"},
{Expression: "amount"},
},
WindowFunctions: []query.WindowFunction{
{
Function: "RANK",
Over: "PARTITION BY department_id",
OrderBy: "amount DESC",
Alias: "sales_rank",
},
},
}
```
### D. Operasi JSON
Mendukung *query* untuk kolom tipe JSON/JSONB pada tabel relasional.
```go
// Mencari dari data JSON
q := query.DynamicQuery{
From: "products",
Filters: []query.FilterGroup{{
Filters: []query.DynamicFilter{
{
Column: "attributes",
Operator: query.OpJsonEqual,
Value: "red",
Options: map[string]interface{}{"path": "color"}, // Akan di-parse: attributes->'color' = 'red'
},
},
}},
}
```
---
## 4. Penggunaan dengan Transaksi (Transaction)
`QueryBuilder` telah dirancang untuk mendukung *Transactions* secara mulus. Semua metode eksekusi (seperti `ExecuteInsert`, `ExecuteUpdate`, `ExecuteQuery`) menerima tipe parameter antarmuka `sqlx.ExtContext`.
Oleh karena itu, Anda cukup memberikan variabel `*sqlx.Tx` menggantikan `*sqlx.DB`!
**Contoh Transaksi Lengkap:**
```go
func CreateUserWithProfile(ctx context.Context, db *sqlx.DB, sqlBuilder query.QueryBuilder) error {
// 1. Mulai Transaksi
tx, err := db.BeginTxx(ctx, nil)
if err != nil {
return err
}
// Buat fungsi defer untuk auto-rollback jika terjadi panic / error tanpa komit
defer func() {
if p := recover(); p != nil {
tx.Rollback()
panic(p)
} else if err != nil {
tx.Rollback()
}
}()
// 2. Operasi PERTAMA dalam transaksi (Insert User)
userData := query.InsertData{
Columns: []string{"name", "email"},
Values: []interface{}{"Jane Doe", "[email protected]"},
}
// PERHATIKAN: Kita melemparkan "tx", bukan "db"
// Kita menggunakan klausa returning "id" untuk mendapatkan ID user yang baru
res, err := sqlBuilder.ExecuteInsert(ctx, tx, "users", userData, "id")
if err != nil {
return err // Akan trigger tx.Rollback() melalui defer
}
// (Jika pakai PostgreSQL, ambil ID melalui interface driver, atau pakai ExecuteQueryRow)
var newUserID int64 = 1 // Simulasi ID yang didapat
// 3. Operasi KEDUA dalam transaksi (Insert Profile)
profileData := query.InsertData{
Columns: []string{"user_id", "bio"},
Values: []interface{}{newUserID, "Hello I am Jane"},
}
_, err = sqlBuilder.ExecuteInsert(ctx, tx, "profiles", profileData)
if err != nil {
return err // Akan trigger tx.Rollback() melalui defer
}
// 4. Komit Transaksi Jika Semua Berhasil
err = tx.Commit()
return err
}
```
---
## 5. Dukungan MongoDB
Selain SQL, *Query Builder* juga menyediakan antarmuka khusus untuk MongoDB melalui `MongoQueryBuilder`. Objek `DynamicQuery` yang sama bisa digunakan untuk mem-parsing sintaks MongoDB (sehingga cocok dengan arsitektur multi-database).
```go
// Inisialisasi
mongoQb := query.CreateMongoQueryBuilder()
// Objek Dynamic Query (Sama seperti SQL)
q := query.DynamicQuery{
Filters: []query.FilterGroup{{
Filters: []query.DynamicFilter{
query.CreateEqualFilter("status", "active"),
},
}},
Limit: 10,
}
// Eksekusi Find
var results []bson.M
// Parameter kedua adalah objek *mongo.Collection
err := mongoQb.ExecuteFind(ctx, myMongoCollection, q, &results)
```
Mendukung operasi MongoDB lain seperti `ExecuteCount`, `ExecuteInsert`, `ExecuteUpdate`, `ExecuteDelete`, dan `ExecuteAggregate`.
---
## 6. Parser URL (Mengubah Parameter API menjadi Query)
Sangat bermanfaat untuk skenario REST API di mana _client_ memberikan filter, sort, dan paginasi melalui *query string* (misal: `?limit=10&sort=-created_at&filter[status][_eq]=active`).
```go
func GetUsersHandler(c *gin.Context) {
// Ambil parameter querystring dari request URL
urlParams := c.Request.URL.Query()
// Inisialisasi Parser
parser := query.CreateQueryParser().SetLimits(10, 100)
// Konversi URL Parameters menjadi objek DynamicQuery
dynamicQuery, err := parser.ParseQuery(urlParams, "users")
if err != nil {
// Handle error (bad request)
}
// Eksekusi query dengan SQL Builder
var users []User
sqlBuilder.ExecuteQuery(ctx, db, dynamicQuery, &users)
// Return response...
}
```