@dbcube/query-builder 1.0.49 → 1.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1645 -79
- package/package.json +5 -5
- package/pnpm-workspace.yaml +2 -2
- package/bun.lockb +0 -0
package/README.md
CHANGED
|
@@ -1,23 +1,85 @@
|
|
|
1
|
-
# query-builder
|
|
1
|
+
# @dbcube/query-builder
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Language/Lenguaje
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- [English](#english-documentation)
|
|
6
|
+
- [Español](#documentación-en-español)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## English documentation
|
|
11
|
+
|
|
12
|
+
## Table of contents 🚀
|
|
13
|
+
|
|
14
|
+
- [Introduction](#introduction)
|
|
15
|
+
- [Features](#features)
|
|
16
|
+
- [Installation](#installation)
|
|
17
|
+
- [Configuration](#configuration)
|
|
18
|
+
- [Basic Usage](#basic-usage)
|
|
19
|
+
- [Database Connection](#database-connection)
|
|
20
|
+
- [Table Operations](#table-operations)
|
|
21
|
+
- [CRUD Operations](#crud-operations)
|
|
22
|
+
- [Inserting Data](#inserting-data)
|
|
23
|
+
- [Selecting Data](#selecting-data)
|
|
24
|
+
- [Updating Data](#updating-data)
|
|
25
|
+
- [Deleting Data](#deleting-data)
|
|
26
|
+
- [Advanced Queries](#advanced-queries)
|
|
27
|
+
- [WHERE Query](#where-query)
|
|
28
|
+
- [OR WHERE Query](#or-where-query)
|
|
29
|
+
- [WHERE Condition Groups](#where-condition-groups)
|
|
30
|
+
- [BETWEEN Query](#between-query)
|
|
31
|
+
- [IN Query](#in-query)
|
|
32
|
+
- [IS NULL / IS NOT NULL Query](#is-null--is-not-null-query)
|
|
33
|
+
- [JOIN Query](#join-query)
|
|
34
|
+
- [LEFT JOIN Query](#left-join-query)
|
|
35
|
+
- [RIGHT JOIN Query](#right-join-query)
|
|
36
|
+
- [ORDER BY Query](#order-by-query)
|
|
37
|
+
- [LIMIT and OFFSET Query (Pagination)](#limit-and-offset-query-pagination)
|
|
38
|
+
- [GROUP BY Query](#group-by-query)
|
|
39
|
+
- [DISTINCT Query](#distinct-query)
|
|
40
|
+
- [Aggregate Functions](#aggregate-functions)
|
|
41
|
+
- [count](#count)
|
|
42
|
+
- [sum](#sum)
|
|
43
|
+
- [avg](#avg)
|
|
44
|
+
- [max](#max)
|
|
45
|
+
- [min](#min)
|
|
46
|
+
- [Finding Records](#finding-records)
|
|
47
|
+
- [find](#find)
|
|
48
|
+
- [first](#first)
|
|
49
|
+
- [Computed Fields and Triggers](#computed-fields-and-triggers)
|
|
50
|
+
- [Executing Raw SQL Queries](#executing-raw-sql-queries)
|
|
51
|
+
- [Error Handling](#error-handling)
|
|
52
|
+
- [Complete API](#complete-api)
|
|
53
|
+
- [Database Class](#database-class)
|
|
54
|
+
- [Table Class](#table-class)
|
|
55
|
+
- [Multi-Database Support](#multi-database-support)
|
|
56
|
+
- [Advanced Features](#advanced-features)
|
|
57
|
+
- [License](#license)
|
|
58
|
+
|
|
59
|
+
## Introduction
|
|
60
|
+
|
|
61
|
+
`@dbcube/query-builder` is a lightweight, flexible, and fluent Node.js library designed to build queries across multiple database engines, including MySQL, PostgreSQL, SQLite, and MongoDB, using JavaScript/TypeScript.
|
|
62
|
+
|
|
63
|
+
Its agnostic design allows you to generate data manipulation (DML) and data definition (DDL) operations with a clean, chainable syntax—without sacrificing power or expressiveness. It's designed to work seamlessly in both SQL and NoSQL environments, providing a consistent abstraction layer across different storage technologies while still leveraging the native capabilities of each engine.
|
|
64
|
+
|
|
65
|
+
**Key differentiator**: Unlike other query builders that focus on a single database type, DBCube Query Builder provides a unified API that works across SQL and NoSQL databases, making it perfect for modern polyglot architectures.
|
|
8
66
|
|
|
9
67
|
## Features
|
|
10
68
|
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
69
|
+
- **Multi-Database Support**: MySQL, PostgreSQL, SQLite, and MongoDB
|
|
70
|
+
- **Fluent API** for building SQL queries with chainable methods
|
|
71
|
+
- **Type-safe** query construction with TypeScript support
|
|
72
|
+
- **Complete CRUD Operations**: SELECT, INSERT, UPDATE, DELETE
|
|
14
73
|
- **Advanced WHERE conditions** (AND, OR, groups, BETWEEN, IN, NULL checks)
|
|
15
|
-
- **
|
|
74
|
+
- **JOIN Support**: INNER, LEFT, RIGHT joins
|
|
16
75
|
- **Aggregations**: COUNT, SUM, AVG, MAX, MIN
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
76
|
+
- **Query Modifiers**: ORDER BY, GROUP BY, DISTINCT, LIMIT/OFFSET
|
|
77
|
+
- **Computed Fields**: Dynamic field calculations
|
|
78
|
+
- **Triggers**: Before/after operation hooks
|
|
19
79
|
- **Promise-based asynchronous API**
|
|
20
|
-
- **
|
|
80
|
+
- **Connection pooling** through @dbcube/core
|
|
81
|
+
- **Error handling** with descriptive messages
|
|
82
|
+
- **Cross-platform compatibility** (Windows, macOS, Linux)
|
|
21
83
|
|
|
22
84
|
## Installation
|
|
23
85
|
|
|
@@ -25,110 +87,1614 @@ It’s designed to work seamlessly in both SQL and NoSQL environments, providing
|
|
|
25
87
|
npm install @dbcube/query-builder
|
|
26
88
|
```
|
|
27
89
|
|
|
28
|
-
##
|
|
90
|
+
## Configuration
|
|
91
|
+
|
|
92
|
+
DBCube Query Builder works with the DBCube ecosystem. Make sure you have the proper database configuration through @dbcube/core.
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// No explicit configuration needed - works through DBCube core
|
|
96
|
+
import { Database } from "@dbcube/query-builder";
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Basic Usage
|
|
100
|
+
|
|
101
|
+
### Database Connection
|
|
102
|
+
|
|
103
|
+
The connection is automatically managed through the DBCube core system.
|
|
29
104
|
|
|
30
105
|
```typescript
|
|
31
|
-
import Database from "@dbcube/query-builder";
|
|
106
|
+
import { Database } from "@dbcube/query-builder";
|
|
32
107
|
|
|
108
|
+
// Create a database instance
|
|
33
109
|
const db = new Database("my_database");
|
|
34
110
|
|
|
35
|
-
//
|
|
36
|
-
const
|
|
111
|
+
// Get a table reference
|
|
112
|
+
const usersTable = db.table("users");
|
|
113
|
+
```
|
|
37
114
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
115
|
+
## Table Operations
|
|
116
|
+
|
|
117
|
+
DBCube Query Builder focuses on data operations. Table creation and schema management are handled by other DBCube components.
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// Access table for operations
|
|
121
|
+
const usersTable = db.table("users");
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## CRUD Operations
|
|
125
|
+
|
|
126
|
+
### Inserting Data
|
|
127
|
+
|
|
128
|
+
Use the `insert` method to add new records to a table.
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
// Insert single record
|
|
132
|
+
const newUser = await usersTable.insert([
|
|
133
|
+
{ name: "Alice", email: "alice@example.com", age: 28 }
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
// Insert multiple records
|
|
137
|
+
const newUsers = await usersTable.insert([
|
|
138
|
+
{ name: "Alice", email: "alice@example.com", age: 28 },
|
|
139
|
+
{ name: "Bob", email: "bob@example.com", age: 32 },
|
|
140
|
+
{ name: "Charlie", email: "charlie@example.com", age: 35 }
|
|
141
|
+
]);
|
|
142
|
+
|
|
143
|
+
console.log(newUsers);
|
|
144
|
+
// Returns the inserted records with generated IDs
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Selecting Data
|
|
148
|
+
|
|
149
|
+
Use the `select` method to specify columns and `get()` to retrieve data.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// Select all columns
|
|
153
|
+
const allUsers = await usersTable.get();
|
|
154
|
+
|
|
155
|
+
// Select specific columns
|
|
156
|
+
const users = await usersTable
|
|
157
|
+
.select(["id", "name", "email"])
|
|
158
|
+
.get();
|
|
159
|
+
|
|
160
|
+
// Select with conditions
|
|
161
|
+
const activeUsers = await usersTable
|
|
162
|
+
.select(["name", "email"])
|
|
41
163
|
.where("status", "=", "active")
|
|
42
164
|
.orderBy("created_at", "DESC")
|
|
43
165
|
.limit(10)
|
|
44
166
|
.get();
|
|
45
167
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
168
|
+
console.log(users);
|
|
169
|
+
// [{ id: 1, name: 'Alice', email: 'alice@example.com' }, ...]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Updating Data
|
|
173
|
+
|
|
174
|
+
Use the `update` method to modify existing records. **Requires WHERE conditions**.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// Update single field
|
|
178
|
+
await usersTable
|
|
179
|
+
.where("id", "=", 1)
|
|
180
|
+
.update({ age: 29 });
|
|
181
|
+
|
|
182
|
+
// Update multiple fields
|
|
183
|
+
await usersTable
|
|
184
|
+
.where("email", "=", "alice@example.com")
|
|
185
|
+
.update({
|
|
186
|
+
name: "Alice Smith",
|
|
187
|
+
age: 29,
|
|
188
|
+
updated_at: new Date()
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Update with complex conditions
|
|
192
|
+
await usersTable
|
|
193
|
+
.where("age", ">", 30)
|
|
194
|
+
.where("status", "=", "inactive")
|
|
195
|
+
.update({ status: "archived" });
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Deleting Data
|
|
199
|
+
|
|
200
|
+
Use the `delete` method to remove records. **Requires WHERE conditions**.
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// Delete specific record
|
|
204
|
+
await usersTable
|
|
205
|
+
.where("id", "=", 2)
|
|
206
|
+
.delete();
|
|
207
|
+
|
|
208
|
+
// Delete with conditions
|
|
209
|
+
await usersTable
|
|
210
|
+
.where("status", "=", "deleted")
|
|
211
|
+
.where("created_at", "<", "2023-01-01")
|
|
212
|
+
.delete();
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Advanced Queries
|
|
216
|
+
|
|
217
|
+
### WHERE Query
|
|
218
|
+
|
|
219
|
+
Filter records using the `where` method with various operators.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
// Basic comparisons
|
|
223
|
+
const adultUsers = await usersTable.where("age", ">", 18).get();
|
|
224
|
+
const exactMatch = await usersTable.where("name", "=", "Alice").get();
|
|
225
|
+
const notEqual = await usersTable.where("status", "!=", "deleted").get();
|
|
226
|
+
|
|
227
|
+
// String operations
|
|
228
|
+
const emailUsers = await usersTable.where("email", "LIKE", "%@gmail.com").get();
|
|
229
|
+
|
|
230
|
+
console.log(adultUsers);
|
|
231
|
+
// [{ id: 1, name: 'Alice', age: 28 }, ...]
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### OR WHERE Query
|
|
235
|
+
|
|
236
|
+
Use `orWhere` to add OR conditions to your query.
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
const users = await usersTable
|
|
240
|
+
.where("age", ">", 25)
|
|
241
|
+
.orWhere("name", "=", "Alice")
|
|
242
|
+
.get();
|
|
243
|
+
|
|
244
|
+
// Complex OR conditions
|
|
245
|
+
const premiumUsers = await usersTable
|
|
246
|
+
.where("subscription", "=", "premium")
|
|
247
|
+
.orWhere("total_purchases", ">", 1000)
|
|
248
|
+
.orWhere("member_since", "<", "2020-01-01")
|
|
249
|
+
.get();
|
|
250
|
+
|
|
251
|
+
console.log(users);
|
|
252
|
+
// [{ id: 1, name: 'Alice', age: 28 }, ...]
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### WHERE Condition Groups
|
|
256
|
+
|
|
257
|
+
Group conditions using `whereGroup` for complex logic.
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
// (age > 25 OR name = 'Jane') AND status = 'active'
|
|
261
|
+
const users = await usersTable
|
|
262
|
+
.whereGroup((query) => {
|
|
263
|
+
query.where("age", ">", 25).orWhere("name", "=", "Jane");
|
|
264
|
+
})
|
|
265
|
+
.where("status", "=", "active")
|
|
266
|
+
.get();
|
|
267
|
+
|
|
268
|
+
// Nested groups
|
|
269
|
+
const complexQuery = await usersTable
|
|
270
|
+
.whereGroup((query) => {
|
|
271
|
+
query.where("country", "=", "US").orWhere("country", "=", "CA");
|
|
272
|
+
})
|
|
273
|
+
.where("active", "=", true)
|
|
274
|
+
.whereGroup((query) => {
|
|
275
|
+
query.where("age", ">=", 21).orWhere("verified", "=", true);
|
|
276
|
+
})
|
|
277
|
+
.get();
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### BETWEEN Query
|
|
281
|
+
|
|
282
|
+
Search for values within a range using `whereBetween`.
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
// Age between 25 and 35
|
|
286
|
+
const users = await usersTable.whereBetween("age", [25, 35]).get();
|
|
287
|
+
|
|
288
|
+
// Date ranges
|
|
289
|
+
const recentUsers = await usersTable
|
|
290
|
+
.whereBetween("created_at", ["2024-01-01", "2024-12-31"])
|
|
291
|
+
.get();
|
|
292
|
+
|
|
293
|
+
console.log(users);
|
|
294
|
+
// [{ id: 1, name: 'Alice', age: 28 }, { id: 2, name: 'Bob', age: 32 }]
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### IN Query
|
|
298
|
+
|
|
299
|
+
Search for values that match a set of values using `whereIn`.
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// Specific IDs
|
|
303
|
+
const users = await usersTable.whereIn("id", [1, 3, 5]).get();
|
|
304
|
+
|
|
305
|
+
// Multiple statuses
|
|
306
|
+
const filteredUsers = await usersTable
|
|
307
|
+
.whereIn("status", ["active", "pending", "verified"])
|
|
308
|
+
.get();
|
|
309
|
+
|
|
310
|
+
// String values
|
|
311
|
+
const emailDomains = await usersTable
|
|
312
|
+
.whereIn("email_domain", ["gmail.com", "yahoo.com", "hotmail.com"])
|
|
313
|
+
.get();
|
|
314
|
+
|
|
315
|
+
console.log(users);
|
|
316
|
+
// [{ id: 1, name: 'Alice', age: 28 }, { id: 3, name: 'Charlie', age: 35 }]
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### IS NULL / IS NOT NULL Query
|
|
320
|
+
|
|
321
|
+
Search for null or non-null values using `whereNull` and `whereNotNull`.
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// Users without email
|
|
325
|
+
const usersWithoutEmail = await usersTable.whereNull("email").get();
|
|
326
|
+
|
|
327
|
+
// Users with email
|
|
328
|
+
const usersWithEmail = await usersTable.whereNotNull("email").get();
|
|
329
|
+
|
|
330
|
+
// Combine with other conditions
|
|
331
|
+
const incompleteProfiles = await usersTable
|
|
332
|
+
.whereNull("phone")
|
|
333
|
+
.orWhere("avatar", "IS NULL")
|
|
334
|
+
.whereNotNull("email")
|
|
335
|
+
.get();
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### JOIN Query
|
|
339
|
+
|
|
340
|
+
Join tables using the `join` method for INNER JOIN.
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
// Basic INNER JOIN
|
|
344
|
+
const usersWithOrders = await usersTable
|
|
345
|
+
.join("orders", "users.id", "=", "orders.user_id")
|
|
346
|
+
.select(["users.name", "orders.order_id", "orders.total"])
|
|
347
|
+
.get();
|
|
348
|
+
|
|
349
|
+
// Multiple JOINs
|
|
350
|
+
const detailedOrders = await usersTable
|
|
351
|
+
.join("orders", "users.id", "=", "orders.user_id")
|
|
352
|
+
.join("order_items", "orders.id", "=", "order_items.order_id")
|
|
353
|
+
.join("products", "order_items.product_id", "=", "products.id")
|
|
354
|
+
.select(["users.name", "orders.order_id", "products.name AS product_name"])
|
|
355
|
+
.get();
|
|
356
|
+
|
|
357
|
+
console.log(usersWithOrders);
|
|
358
|
+
// [{ name: 'Alice', order_id: 101, total: 150.00 }, ...]
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### LEFT JOIN Query
|
|
362
|
+
|
|
363
|
+
Perform a left join using the `leftJoin` method.
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// Include users even if they have no orders
|
|
367
|
+
const usersWithOrders = await usersTable
|
|
368
|
+
.leftJoin("orders", "users.id", "=", "orders.user_id")
|
|
369
|
+
.select(["users.name", "orders.order_id"])
|
|
370
|
+
.get();
|
|
371
|
+
|
|
372
|
+
// Left join with aggregation
|
|
373
|
+
const usersOrderCount = await usersTable
|
|
374
|
+
.leftJoin("orders", "users.id", "=", "orders.user_id")
|
|
375
|
+
.select(["users.name"])
|
|
376
|
+
.count("orders.id")
|
|
377
|
+
.groupBy("users.id")
|
|
378
|
+
.get();
|
|
379
|
+
|
|
380
|
+
console.log(usersWithOrders);
|
|
381
|
+
// [{ name: 'Alice', order_id: 101 }, { name: 'Bob', order_id: null }, ...]
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### RIGHT JOIN Query
|
|
385
|
+
|
|
386
|
+
Perform a right join using the `rightJoin` method.
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
// Include orders even if user data is missing
|
|
390
|
+
const ordersWithUsers = await usersTable
|
|
391
|
+
.rightJoin("orders", "users.id", "=", "orders.user_id")
|
|
392
|
+
.select(["users.name", "orders.order_id"])
|
|
393
|
+
.get();
|
|
394
|
+
|
|
395
|
+
console.log(ordersWithUsers);
|
|
396
|
+
// [{ name: 'Alice', order_id: 101 }, { name: null, order_id: 102 }, ...]
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### ORDER BY Query
|
|
50
400
|
|
|
51
|
-
|
|
52
|
-
await db.table("users").where("id", "=", 1).update({ status: "inactive" });
|
|
401
|
+
Sort results using the `orderBy` method.
|
|
53
402
|
|
|
54
|
-
|
|
55
|
-
|
|
403
|
+
```typescript
|
|
404
|
+
// Single column sorting
|
|
405
|
+
const sortedUsers = await usersTable
|
|
406
|
+
.orderBy("name", "ASC")
|
|
407
|
+
.get();
|
|
408
|
+
|
|
409
|
+
// Multiple column sorting
|
|
410
|
+
const complexSort = await usersTable
|
|
411
|
+
.orderBy("country", "ASC")
|
|
412
|
+
.orderBy("age", "DESC")
|
|
413
|
+
.orderBy("name", "ASC")
|
|
414
|
+
.get();
|
|
415
|
+
|
|
416
|
+
// Sort with conditions
|
|
417
|
+
const recentActiveUsers = await usersTable
|
|
418
|
+
.where("status", "=", "active")
|
|
419
|
+
.orderBy("last_login", "DESC")
|
|
420
|
+
.limit(20)
|
|
421
|
+
.get();
|
|
422
|
+
|
|
423
|
+
console.log(sortedUsers);
|
|
424
|
+
// [{ id: 1, name: 'Alice', ... }, { id: 2, name: 'Bob', ... }]
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### LIMIT and OFFSET Query (Pagination)
|
|
428
|
+
|
|
429
|
+
Limit the number of results and implement pagination using `limit` and `page`.
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
// Simple limit
|
|
433
|
+
const firstTenUsers = await usersTable.limit(10).get();
|
|
434
|
+
|
|
435
|
+
// Pagination
|
|
436
|
+
const firstPage = await usersTable.limit(5).page(1).get();
|
|
437
|
+
const secondPage = await usersTable.limit(5).page(2).get();
|
|
438
|
+
|
|
439
|
+
// Pagination with sorting
|
|
440
|
+
const paginatedUsers = await usersTable
|
|
441
|
+
.orderBy("created_at", "DESC")
|
|
442
|
+
.limit(10)
|
|
443
|
+
.page(3) // Skip first 20 records (pages 1-2)
|
|
444
|
+
.get();
|
|
445
|
+
|
|
446
|
+
console.log(firstPage); // Records 1-5
|
|
447
|
+
console.log(secondPage); // Records 6-10
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### GROUP BY Query
|
|
451
|
+
|
|
452
|
+
Group results using the `groupBy` method.
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
// Simple grouping
|
|
456
|
+
const usersByAge = await usersTable
|
|
457
|
+
.select(["age"])
|
|
458
|
+
.count("*")
|
|
459
|
+
.groupBy("age")
|
|
460
|
+
.get();
|
|
461
|
+
|
|
462
|
+
// Multiple grouping columns
|
|
463
|
+
const usersByCountryAndAge = await usersTable
|
|
464
|
+
.select(["country", "age"])
|
|
465
|
+
.count("*")
|
|
466
|
+
.groupBy("country")
|
|
467
|
+
.groupBy("age")
|
|
468
|
+
.get();
|
|
469
|
+
|
|
470
|
+
console.log(usersByAge);
|
|
471
|
+
// [{ age: 28, count: 2 }, { age: 32, count: 1 }]
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### DISTINCT Query
|
|
475
|
+
|
|
476
|
+
Retrieve unique records using the `distinct` method.
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
// Distinct values
|
|
480
|
+
const uniqueCountries = await usersTable
|
|
481
|
+
.distinct()
|
|
482
|
+
.select(["country"])
|
|
483
|
+
.get();
|
|
484
|
+
|
|
485
|
+
// Distinct with conditions
|
|
486
|
+
const activeUserCountries = await usersTable
|
|
487
|
+
.distinct()
|
|
488
|
+
.select(["country"])
|
|
489
|
+
.where("status", "=", "active")
|
|
490
|
+
.get();
|
|
491
|
+
|
|
492
|
+
console.log(uniqueCountries);
|
|
493
|
+
// [{ country: 'USA' }, { country: 'Canada' }, { country: 'UK' }]
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
## Aggregate Functions
|
|
497
|
+
|
|
498
|
+
### count
|
|
499
|
+
|
|
500
|
+
Count the number of records.
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
// Count all records
|
|
504
|
+
const totalUsers = await usersTable.count().first();
|
|
505
|
+
console.log(totalUsers); // { count: 150 }
|
|
506
|
+
|
|
507
|
+
// Count specific column
|
|
508
|
+
const usersWithEmail = await usersTable.count("email").first();
|
|
509
|
+
|
|
510
|
+
// Count with conditions
|
|
511
|
+
const activeUsers = await usersTable
|
|
512
|
+
.where("status", "=", "active")
|
|
513
|
+
.count()
|
|
514
|
+
.first();
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### sum
|
|
518
|
+
|
|
519
|
+
Calculate the sum of a column.
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
// Sum of ages
|
|
523
|
+
const totalAge = await usersTable.sum("age").first();
|
|
524
|
+
console.log(totalAge); // { sum: 4250 }
|
|
525
|
+
|
|
526
|
+
// Sum with conditions
|
|
527
|
+
const premiumRevenue = await usersTable
|
|
528
|
+
.join("orders", "users.id", "=", "orders.user_id")
|
|
529
|
+
.where("users.subscription", "=", "premium")
|
|
530
|
+
.sum("orders.total")
|
|
531
|
+
.first();
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### avg
|
|
535
|
+
|
|
536
|
+
Calculate the average of a column.
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
// Average age
|
|
540
|
+
const averageAge = await usersTable.avg("age").first();
|
|
541
|
+
console.log(averageAge); // { avg: 28.33 }
|
|
542
|
+
|
|
543
|
+
// Average with grouping
|
|
544
|
+
const avgAgeByCountry = await usersTable
|
|
545
|
+
.select(["country"])
|
|
546
|
+
.avg("age")
|
|
547
|
+
.groupBy("country")
|
|
548
|
+
.get();
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### max
|
|
552
|
+
|
|
553
|
+
Find the maximum value in a column.
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
// Oldest user
|
|
557
|
+
const maxAge = await usersTable.max("age").first();
|
|
558
|
+
console.log(maxAge); // { max: 65 }
|
|
559
|
+
|
|
560
|
+
// Most recent registration
|
|
561
|
+
const latestUser = await usersTable.max("created_at").first();
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### min
|
|
565
|
+
|
|
566
|
+
Find the minimum value in a column.
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
// Youngest user
|
|
570
|
+
const minAge = await usersTable.min("age").first();
|
|
571
|
+
console.log(minAge); // { min: 18 }
|
|
572
|
+
|
|
573
|
+
// Earliest registration
|
|
574
|
+
const firstUser = await usersTable.min("created_at").first();
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
## Finding Records
|
|
578
|
+
|
|
579
|
+
### find
|
|
580
|
+
|
|
581
|
+
Find a record by a specific column value (defaults to 'id').
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
// Find by ID (default)
|
|
585
|
+
const user = await usersTable.find(1);
|
|
586
|
+
console.log(user);
|
|
587
|
+
// { id: 1, name: 'Alice', email: 'alice@example.com', age: 28 }
|
|
588
|
+
|
|
589
|
+
// Find by specific column
|
|
590
|
+
const userByEmail = await usersTable.find("alice@example.com", "email");
|
|
591
|
+
|
|
592
|
+
// Find returns null if not found
|
|
593
|
+
const nonExistent = await usersTable.find(999);
|
|
594
|
+
console.log(nonExistent); // null
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### first
|
|
598
|
+
|
|
599
|
+
Get only the first record that meets the conditions.
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
// First user matching condition
|
|
603
|
+
const firstUser = await usersTable
|
|
604
|
+
.where("age", ">", 25)
|
|
605
|
+
.orderBy("created_at", "ASC")
|
|
606
|
+
.first();
|
|
607
|
+
|
|
608
|
+
// First user in general
|
|
609
|
+
const oldestAccount = await usersTable
|
|
610
|
+
.orderBy("created_at", "ASC")
|
|
611
|
+
.first();
|
|
612
|
+
|
|
613
|
+
console.log(firstUser);
|
|
614
|
+
// { id: 1, name: 'Alice', age: 28, ... } or null if no match
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
## Computed Fields and Triggers
|
|
618
|
+
|
|
619
|
+
DBCube Query Builder supports computed fields and triggers for advanced data processing.
|
|
620
|
+
|
|
621
|
+
```typescript
|
|
622
|
+
// Enable computed fields (processed automatically)
|
|
623
|
+
await db.useComputes();
|
|
624
|
+
|
|
625
|
+
// Enable triggers
|
|
626
|
+
await db.useTriggers();
|
|
627
|
+
|
|
628
|
+
// Triggers and computed fields are configured through other DBCube components
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
## Executing Raw SQL Queries
|
|
632
|
+
|
|
633
|
+
For complex queries that require raw SQL, use the underlying engine.
|
|
634
|
+
|
|
635
|
+
```typescript
|
|
636
|
+
// Access the underlying engine for raw queries
|
|
637
|
+
// (Implementation depends on your specific DBCube core setup)
|
|
638
|
+
|
|
639
|
+
// Note: Raw SQL queries bypass the query builder's abstraction layer
|
|
640
|
+
// and are database-specific
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### Error Handling
|
|
644
|
+
|
|
645
|
+
The library provides comprehensive error handling with descriptive messages.
|
|
646
|
+
|
|
647
|
+
```typescript
|
|
648
|
+
try {
|
|
649
|
+
// This will throw an error - UPDATE requires WHERE conditions
|
|
650
|
+
await usersTable.update({ name: "Updated" });
|
|
651
|
+
} catch (error) {
|
|
652
|
+
console.error(error.message);
|
|
653
|
+
// "You must specify at least one WHERE condition to perform an update."
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
try {
|
|
657
|
+
// This will throw an error - invalid data format
|
|
658
|
+
await usersTable.insert("invalid data");
|
|
659
|
+
} catch (error) {
|
|
660
|
+
console.error(error.message);
|
|
661
|
+
// "The insert method requires an array of objects with key-value pairs."
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Connection and database errors are also properly handled
|
|
665
|
+
try {
|
|
666
|
+
const result = await usersTable.get();
|
|
667
|
+
} catch (error) {
|
|
668
|
+
// Database connection or query execution errors
|
|
669
|
+
console.error("Database error:", error);
|
|
670
|
+
}
|
|
56
671
|
```
|
|
57
672
|
|
|
58
|
-
## API
|
|
673
|
+
## Complete API
|
|
59
674
|
|
|
60
|
-
### Database
|
|
675
|
+
### Database Class
|
|
61
676
|
|
|
62
677
|
#### `new Database(name: string)`
|
|
63
678
|
|
|
64
679
|
Creates a new database connection instance.
|
|
65
680
|
|
|
681
|
+
```typescript
|
|
682
|
+
const db = new Database("my_database");
|
|
683
|
+
```
|
|
684
|
+
|
|
66
685
|
#### `table(tableName: string): Table`
|
|
67
686
|
|
|
68
687
|
Returns a Table instance for building queries on the specified table.
|
|
69
688
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- `select(fields?: string[])`: Specify columns to select.
|
|
75
|
-
- `where(column, operator, value)`: Add a WHERE condition.
|
|
76
|
-
- `orWhere(column, operator, value)`: Add an OR WHERE condition.
|
|
77
|
-
- `whereGroup(callback)`: Grouped WHERE conditions.
|
|
78
|
-
- `whereBetween(column, [min, max])`: WHERE BETWEEN condition.
|
|
79
|
-
- `whereIn(column, values)`: WHERE IN condition.
|
|
80
|
-
- `whereNull(column)`: WHERE IS NULL condition.
|
|
81
|
-
- `whereNotNull(column)`: WHERE IS NOT NULL condition.
|
|
82
|
-
- `join(table, column1, operator, column2)`: INNER JOIN.
|
|
83
|
-
- `leftJoin(table, column1, operator, column2)`: LEFT JOIN.
|
|
84
|
-
- `rightJoin(table, column1, operator, column2)`: RIGHT JOIN.
|
|
85
|
-
- `orderBy(column, direction)`: ORDER BY clause.
|
|
86
|
-
- `groupBy(column)`: GROUP BY clause.
|
|
87
|
-
- `distinct()`: DISTINCT clause.
|
|
88
|
-
- `count(column?)`: COUNT aggregation.
|
|
89
|
-
- `sum(column)`: SUM aggregation.
|
|
90
|
-
- `avg(column)`: AVG aggregation.
|
|
91
|
-
- `max(column)`: MAX aggregation.
|
|
92
|
-
- `min(column)`: MIN aggregation.
|
|
93
|
-
- `limit(number)`: LIMIT clause.
|
|
94
|
-
- `page(number)`: Pagination (requires limit).
|
|
689
|
+
```typescript
|
|
690
|
+
const usersTable = db.table("users");
|
|
691
|
+
```
|
|
95
692
|
|
|
96
|
-
####
|
|
693
|
+
#### `useComputes(): Promise<void>`
|
|
694
|
+
|
|
695
|
+
Enables computed fields processing for the database.
|
|
97
696
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
- `insert(data)`: Insert one or more rows.
|
|
102
|
-
- `update(data)`: Update rows matching the conditions.
|
|
103
|
-
- `delete()`: Delete rows matching the conditions.
|
|
697
|
+
```typescript
|
|
698
|
+
await db.useComputes();
|
|
699
|
+
```
|
|
104
700
|
|
|
105
|
-
|
|
701
|
+
#### `useTriggers(): Promise<void>`
|
|
702
|
+
|
|
703
|
+
Enables trigger processing for the database.
|
|
106
704
|
|
|
107
705
|
```typescript
|
|
108
|
-
|
|
109
|
-
const results = await db
|
|
110
|
-
.table("orders")
|
|
111
|
-
.join("users", "orders.user_id", "=", "users.id")
|
|
112
|
-
.where("orders.status", "=", "completed")
|
|
113
|
-
.groupBy("users.country")
|
|
114
|
-
.sum("orders.total")
|
|
115
|
-
.orderBy("sum", "DESC")
|
|
116
|
-
.limit(5)
|
|
117
|
-
.get();
|
|
706
|
+
await db.useTriggers();
|
|
118
707
|
```
|
|
119
708
|
|
|
120
|
-
|
|
709
|
+
#### `connect(): Promise<void>`
|
|
121
710
|
|
|
122
|
-
|
|
711
|
+
Establishes database connection (handled automatically).
|
|
123
712
|
|
|
124
|
-
|
|
713
|
+
#### `disconnect(): Promise<void>`
|
|
714
|
+
|
|
715
|
+
Closes database connection.
|
|
716
|
+
|
|
717
|
+
### Table Class
|
|
125
718
|
|
|
126
|
-
|
|
719
|
+
#### Query Building Methods
|
|
127
720
|
|
|
128
|
-
|
|
721
|
+
**`select(fields?: string[]): Table`**
|
|
722
|
+
Specify columns to select.
|
|
129
723
|
|
|
130
|
-
|
|
724
|
+
```typescript
|
|
725
|
+
usersTable.select(["id", "name", "email"]);
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
**`where(column, operator, value): Table`**
|
|
729
|
+
Add a WHERE condition.
|
|
730
|
+
|
|
731
|
+
```typescript
|
|
732
|
+
usersTable.where("age", ">", 25);
|
|
733
|
+
usersTable.where("email", "IS NOT NULL");
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
**`orWhere(column, operator, value): Table`**
|
|
737
|
+
Add an OR WHERE condition.
|
|
738
|
+
|
|
739
|
+
```typescript
|
|
740
|
+
usersTable.orWhere("name", "=", "Jane");
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
**`whereGroup(callback): Table`**
|
|
744
|
+
Grouped WHERE conditions.
|
|
745
|
+
|
|
746
|
+
```typescript
|
|
747
|
+
usersTable.whereGroup((query) => {
|
|
748
|
+
query.where("age", ">", 25).orWhere("name", "=", "Jane");
|
|
749
|
+
});
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
**`whereBetween(column, [min, max]): Table`**
|
|
753
|
+
WHERE BETWEEN condition.
|
|
754
|
+
|
|
755
|
+
```typescript
|
|
756
|
+
usersTable.whereBetween("age", [25, 35]);
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
**`whereIn(column, values): Table`**
|
|
760
|
+
WHERE IN condition.
|
|
761
|
+
|
|
762
|
+
```typescript
|
|
763
|
+
usersTable.whereIn("id", [1, 3, 5]);
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
**`whereNull(column): Table`** / **`whereNotNull(column): Table`**
|
|
767
|
+
NULL checks.
|
|
768
|
+
|
|
769
|
+
```typescript
|
|
770
|
+
usersTable.whereNull("deleted_at");
|
|
771
|
+
usersTable.whereNotNull("email");
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
**`join(table, column1, operator, column2): Table`**
|
|
775
|
+
INNER JOIN.
|
|
776
|
+
|
|
777
|
+
```typescript
|
|
778
|
+
usersTable.join("orders", "users.id", "=", "orders.user_id");
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
**`leftJoin(table, column1, operator, column2): Table`**
|
|
782
|
+
LEFT JOIN.
|
|
783
|
+
|
|
784
|
+
**`rightJoin(table, column1, operator, column2): Table`**
|
|
785
|
+
RIGHT JOIN.
|
|
786
|
+
|
|
787
|
+
**`orderBy(column, direction): Table`**
|
|
788
|
+
ORDER BY clause.
|
|
789
|
+
|
|
790
|
+
```typescript
|
|
791
|
+
usersTable.orderBy("name", "ASC");
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
**`groupBy(column): Table`**
|
|
795
|
+
GROUP BY clause.
|
|
796
|
+
|
|
797
|
+
**`distinct(): Table`**
|
|
798
|
+
DISTINCT clause.
|
|
799
|
+
|
|
800
|
+
**`limit(number): Table`**
|
|
801
|
+
LIMIT clause.
|
|
802
|
+
|
|
803
|
+
**`page(number): Table`**
|
|
804
|
+
Pagination (requires limit).
|
|
805
|
+
|
|
806
|
+
#### Aggregation Methods
|
|
807
|
+
|
|
808
|
+
**`count(column?): Table`**
|
|
809
|
+
COUNT aggregation.
|
|
810
|
+
|
|
811
|
+
**`sum(column): Table`**
|
|
812
|
+
SUM aggregation.
|
|
813
|
+
|
|
814
|
+
**`avg(column): Table`**
|
|
815
|
+
AVG aggregation.
|
|
816
|
+
|
|
817
|
+
**`max(column): Table`**
|
|
818
|
+
MAX aggregation.
|
|
819
|
+
|
|
820
|
+
**`min(column): Table`**
|
|
821
|
+
MIN aggregation.
|
|
822
|
+
|
|
823
|
+
#### Execution Methods
|
|
824
|
+
|
|
825
|
+
**`get(): Promise<DatabaseRecord[]>`**
|
|
826
|
+
Execute and return all matching rows.
|
|
827
|
+
|
|
828
|
+
**`first(): Promise<DatabaseRecord | null>`**
|
|
829
|
+
Execute and return the first matching row.
|
|
830
|
+
|
|
831
|
+
**`find(value, column?): Promise<DatabaseRecord | null>`**
|
|
832
|
+
Find a row by column value (default: id).
|
|
833
|
+
|
|
834
|
+
**`insert(data): Promise<DatabaseRecord[]>`**
|
|
835
|
+
Insert one or more rows.
|
|
836
|
+
|
|
837
|
+
**`update(data): Promise<any>`**
|
|
838
|
+
Update rows matching the conditions.
|
|
839
|
+
|
|
840
|
+
**`delete(): Promise<any>`**
|
|
841
|
+
Delete rows matching the conditions.
|
|
842
|
+
|
|
843
|
+
## Multi-Database Support
|
|
844
|
+
|
|
845
|
+
DBCube Query Builder works with multiple database engines:
|
|
846
|
+
|
|
847
|
+
```typescript
|
|
848
|
+
// MySQL
|
|
849
|
+
const mysqlDb = new Database("mysql_database");
|
|
850
|
+
|
|
851
|
+
// PostgreSQL
|
|
852
|
+
const postgresDb = new Database("postgres_database");
|
|
853
|
+
|
|
854
|
+
// SQLite
|
|
855
|
+
const sqliteDb = new Database("sqlite_database");
|
|
856
|
+
|
|
857
|
+
// MongoDB
|
|
858
|
+
const mongoDb = new Database("mongo_database");
|
|
859
|
+
|
|
860
|
+
// Same API works across all databases
|
|
861
|
+
const users = await mysqlDb.table("users").get();
|
|
862
|
+
const posts = await postgresDb.table("posts").get();
|
|
863
|
+
const logs = await sqliteDb.table("logs").get();
|
|
864
|
+
const analytics = await mongoDb.table("analytics").get();
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
## Advanced Features
|
|
868
|
+
|
|
869
|
+
### Complex Query Example
|
|
870
|
+
|
|
871
|
+
```typescript
|
|
872
|
+
// Complex business query
|
|
873
|
+
const monthlyReport = await db.table("orders")
|
|
874
|
+
.join("users", "orders.user_id", "=", "users.id")
|
|
875
|
+
.join("order_items", "orders.id", "=", "order_items.order_id")
|
|
876
|
+
.join("products", "order_items.product_id", "=", "products.id")
|
|
877
|
+
.select([
|
|
878
|
+
"users.country",
|
|
879
|
+
"products.category"
|
|
880
|
+
])
|
|
881
|
+
.sum("order_items.quantity * order_items.price")
|
|
882
|
+
.where("orders.status", "=", "completed")
|
|
883
|
+
.whereBetween("orders.created_at", ["2024-01-01", "2024-01-31"])
|
|
884
|
+
.groupBy("users.country")
|
|
885
|
+
.groupBy("products.category")
|
|
886
|
+
.orderBy("sum", "DESC")
|
|
887
|
+
.limit(10)
|
|
888
|
+
.get();
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
### Method Chaining
|
|
892
|
+
|
|
893
|
+
All query builder methods return the Table instance, enabling fluent method chaining:
|
|
894
|
+
|
|
895
|
+
```typescript
|
|
896
|
+
const result = await db
|
|
897
|
+
.table("users")
|
|
898
|
+
.select(["name", "email", "country"])
|
|
899
|
+
.where("active", "=", true)
|
|
900
|
+
.whereNotNull("email_verified_at")
|
|
901
|
+
.whereGroup((query) => {
|
|
902
|
+
query.where("subscription", "=", "premium")
|
|
903
|
+
.orWhere("total_orders", ">", 10);
|
|
904
|
+
})
|
|
905
|
+
.join("user_profiles", "users.id", "=", "user_profiles.user_id")
|
|
906
|
+
.orderBy("users.created_at", "DESC")
|
|
907
|
+
.limit(50)
|
|
908
|
+
.page(1)
|
|
909
|
+
.get();
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
## License
|
|
913
|
+
|
|
914
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
915
|
+
|
|
916
|
+
---
|
|
917
|
+
|
|
918
|
+
## Documentación en español
|
|
919
|
+
|
|
920
|
+
## Tabla de contenidos 🚀
|
|
921
|
+
|
|
922
|
+
- [Introducción](#introducción)
|
|
923
|
+
- [Características](#características-1)
|
|
924
|
+
- [Instalación](#instalación-1)
|
|
925
|
+
- [Configuración](#configuración-1)
|
|
926
|
+
- [Uso básico](#uso-básico)
|
|
927
|
+
- [Conexión a la base de datos](#conexión-a-la-base-de-datos)
|
|
928
|
+
- [Operaciones de tabla](#operaciones-de-tabla)
|
|
929
|
+
- [Operaciones CRUD](#operaciones-crud-1)
|
|
930
|
+
- [Insertar datos](#insertar-datos)
|
|
931
|
+
- [Seleccionar datos](#seleccionar-datos)
|
|
932
|
+
- [Actualizar datos](#actualizar-datos)
|
|
933
|
+
- [Eliminar datos](#eliminar-datos)
|
|
934
|
+
- [Consultas avanzadas](#consultas-avanzadas-1)
|
|
935
|
+
- [Consulta con WHERE](#consulta-con-where)
|
|
936
|
+
- [Consulta con OR WHERE](#consulta-con-or-where)
|
|
937
|
+
- [Consulta con grupos de condiciones WHERE](#consulta-con-grupos-de-condiciones-where)
|
|
938
|
+
- [Consulta con BETWEEN](#consulta-con-between)
|
|
939
|
+
- [Consulta con IN](#consulta-con-in)
|
|
940
|
+
- [Consulta con IS NULL / IS NOT NULL](#consulta-con-is-null--is-not-null)
|
|
941
|
+
- [Consulta con JOIN](#consulta-con-join)
|
|
942
|
+
- [Consulta con LEFT JOIN](#consulta-con-left-join)
|
|
943
|
+
- [Consulta con RIGHT JOIN](#consulta-con-right-join)
|
|
944
|
+
- [Consulta con ORDER BY](#consulta-con-order-by)
|
|
945
|
+
- [Consulta con LIMIT y OFFSET](#consulta-con-limit-y-offset-paginación)
|
|
946
|
+
- [Consulta con GROUP BY](#consulta-con-group-by)
|
|
947
|
+
- [Consulta con DISTINCT](#consulta-con-distinct)
|
|
948
|
+
- [Funciones de agregación](#funciones-de-agregación-1)
|
|
949
|
+
- [count](#count-1)
|
|
950
|
+
- [sum](#sum-1)
|
|
951
|
+
- [avg](#avg-1)
|
|
952
|
+
- [max](#max-1)
|
|
953
|
+
- [min](#min-1)
|
|
954
|
+
- [Buscar registros](#buscar-registros)
|
|
955
|
+
- [find](#find-1)
|
|
956
|
+
- [first](#first-1)
|
|
957
|
+
- [Campos calculados y triggers](#campos-calculados-y-triggers)
|
|
958
|
+
- [Ejecutar consultas SQL crudas](#ejecutar-consultas-sql-crudas)
|
|
959
|
+
- [Manejo de errores](#manejo-de-errores)
|
|
960
|
+
- [API completa](#api-completa-1)
|
|
961
|
+
- [Clase Database](#clase-database)
|
|
962
|
+
- [Clase Table](#clase-table)
|
|
963
|
+
- [Soporte multi-base de datos](#soporte-multi-base-de-datos)
|
|
964
|
+
- [Características avanzadas](#características-avanzadas)
|
|
965
|
+
- [Licencia](#licencia)
|
|
966
|
+
|
|
967
|
+
## Introducción
|
|
968
|
+
|
|
969
|
+
`@dbcube/query-builder` es una biblioteca de Node.js ligera, flexible y fluida diseñada para construir consultas a través de múltiples motores de base de datos, incluyendo MySQL, PostgreSQL, SQLite y MongoDB, usando JavaScript/TypeScript.
|
|
970
|
+
|
|
971
|
+
Su diseño agnóstico te permite generar operaciones de manipulación de datos (DML) y definición de datos (DDL) con una sintaxis limpia y encadenable, sin sacrificar potencia o expresividad. Está diseñada para trabajar perfectamente en entornos SQL y NoSQL, proporcionando una capa de abstracción consistente a través de diferentes tecnologías de almacenamiento mientras aprovecha las capacidades nativas de cada motor.
|
|
972
|
+
|
|
973
|
+
**Diferenciador clave**: A diferencia de otros constructores de consultas que se enfocan en un solo tipo de base de datos, DBCube Query Builder proporciona una API unificada que funciona a través de bases de datos SQL y NoSQL, haciéndola perfecta para arquitecturas políglotas modernas.
|
|
974
|
+
|
|
975
|
+
## Características
|
|
976
|
+
|
|
977
|
+
- **Soporte multi-base de datos**: MySQL, PostgreSQL, SQLite y MongoDB
|
|
978
|
+
- **API fluida** para construir consultas SQL con métodos encadenables
|
|
979
|
+
- **Construcción de consultas type-safe** con soporte TypeScript
|
|
980
|
+
- **Operaciones CRUD completas**: SELECT, INSERT, UPDATE, DELETE
|
|
981
|
+
- **Condiciones WHERE avanzadas** (AND, OR, grupos, BETWEEN, IN, verificaciones NULL)
|
|
982
|
+
- **Soporte JOIN**: INNER, LEFT, RIGHT joins
|
|
983
|
+
- **Agregaciones**: COUNT, SUM, AVG, MAX, MIN
|
|
984
|
+
- **Modificadores de consulta**: ORDER BY, GROUP BY, DISTINCT, LIMIT/OFFSET
|
|
985
|
+
- **Campos calculados**: Cálculos dinámicos de campos
|
|
986
|
+
- **Triggers**: Hooks antes/después de operaciones
|
|
987
|
+
- **API asíncrona basada en promesas**
|
|
988
|
+
- **Connection pooling** a través de @dbcube/core
|
|
989
|
+
- **Manejo de errores** con mensajes descriptivos
|
|
990
|
+
- **Compatibilidad multiplataforma** (Windows, macOS, Linux)
|
|
991
|
+
|
|
992
|
+
## Instalación
|
|
993
|
+
|
|
994
|
+
```bash
|
|
995
|
+
npm install @dbcube/query-builder
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
## Configuración
|
|
999
|
+
|
|
1000
|
+
DBCube Query Builder funciona con el ecosistema DBCube. Asegúrate de tener la configuración adecuada de base de datos a través de @dbcube/core.
|
|
1001
|
+
|
|
1002
|
+
```typescript
|
|
1003
|
+
// No se necesita configuración explícita - funciona a través de DBCube core
|
|
1004
|
+
import { Database } from "@dbcube/query-builder";
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
## Uso básico
|
|
1008
|
+
|
|
1009
|
+
### Conexión a la base de datos
|
|
1010
|
+
|
|
1011
|
+
La conexión se gestiona automáticamente a través del sistema core de DBCube.
|
|
1012
|
+
|
|
1013
|
+
```typescript
|
|
1014
|
+
import { Database } from "@dbcube/query-builder";
|
|
1015
|
+
|
|
1016
|
+
// Crear una instancia de base de datos
|
|
1017
|
+
const db = new Database("mi_base_de_datos");
|
|
1018
|
+
|
|
1019
|
+
// Obtener una referencia de tabla
|
|
1020
|
+
const tablaUsuarios = db.table("usuarios");
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
## Operaciones de tabla
|
|
1024
|
+
|
|
1025
|
+
DBCube Query Builder se enfoca en operaciones de datos. La creación de tablas y gestión de esquemas se maneja por otros componentes de DBCube.
|
|
1026
|
+
|
|
1027
|
+
```typescript
|
|
1028
|
+
// Acceder a tabla para operaciones
|
|
1029
|
+
const tablaUsuarios = db.table("usuarios");
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
## Operaciones CRUD
|
|
1033
|
+
|
|
1034
|
+
### Insertar datos
|
|
1035
|
+
|
|
1036
|
+
Utiliza el método `insert` para añadir nuevos registros a una tabla.
|
|
1037
|
+
|
|
1038
|
+
```typescript
|
|
1039
|
+
// Insertar un solo registro
|
|
1040
|
+
const nuevoUsuario = await tablaUsuarios.insert([
|
|
1041
|
+
{ nombre: "Alicia", email: "alicia@ejemplo.com", edad: 28 }
|
|
1042
|
+
]);
|
|
1043
|
+
|
|
1044
|
+
// Insertar múltiples registros
|
|
1045
|
+
const nuevosUsuarios = await tablaUsuarios.insert([
|
|
1046
|
+
{ nombre: "Alicia", email: "alicia@ejemplo.com", edad: 28 },
|
|
1047
|
+
{ nombre: "Roberto", email: "roberto@ejemplo.com", edad: 32 },
|
|
1048
|
+
{ nombre: "Carlos", email: "carlos@ejemplo.com", edad: 35 }
|
|
1049
|
+
]);
|
|
1050
|
+
|
|
1051
|
+
console.log(nuevosUsuarios);
|
|
1052
|
+
// Devuelve los registros insertados con IDs generados
|
|
1053
|
+
```
|
|
1054
|
+
|
|
1055
|
+
### Seleccionar datos
|
|
1056
|
+
|
|
1057
|
+
Utiliza el método `select` para especificar columnas y `get()` para recuperar datos.
|
|
1058
|
+
|
|
1059
|
+
```typescript
|
|
1060
|
+
// Seleccionar todas las columnas
|
|
1061
|
+
const todosUsuarios = await tablaUsuarios.get();
|
|
1062
|
+
|
|
1063
|
+
// Seleccionar columnas específicas
|
|
1064
|
+
const usuarios = await tablaUsuarios
|
|
1065
|
+
.select(["id", "nombre", "email"])
|
|
1066
|
+
.get();
|
|
1067
|
+
|
|
1068
|
+
// Seleccionar con condiciones
|
|
1069
|
+
const usuariosActivos = await tablaUsuarios
|
|
1070
|
+
.select(["nombre", "email"])
|
|
1071
|
+
.where("estado", "=", "activo")
|
|
1072
|
+
.orderBy("fecha_creacion", "DESC")
|
|
1073
|
+
.limit(10)
|
|
1074
|
+
.get();
|
|
1075
|
+
|
|
1076
|
+
console.log(usuarios);
|
|
1077
|
+
// [{ id: 1, nombre: 'Alicia', email: 'alicia@ejemplo.com' }, ...]
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
### Actualizar datos
|
|
1081
|
+
|
|
1082
|
+
Utiliza el método `update` para modificar registros existentes. **Requiere condiciones WHERE**.
|
|
1083
|
+
|
|
1084
|
+
```typescript
|
|
1085
|
+
// Actualizar un campo
|
|
1086
|
+
await tablaUsuarios
|
|
1087
|
+
.where("id", "=", 1)
|
|
1088
|
+
.update({ edad: 29 });
|
|
1089
|
+
|
|
1090
|
+
// Actualizar múltiples campos
|
|
1091
|
+
await tablaUsuarios
|
|
1092
|
+
.where("email", "=", "alicia@ejemplo.com")
|
|
1093
|
+
.update({
|
|
1094
|
+
nombre: "Alicia García",
|
|
1095
|
+
edad: 29,
|
|
1096
|
+
actualizado_en: new Date()
|
|
1097
|
+
});
|
|
1098
|
+
|
|
1099
|
+
// Actualizar con condiciones complejas
|
|
1100
|
+
await tablaUsuarios
|
|
1101
|
+
.where("edad", ">", 30)
|
|
1102
|
+
.where("estado", "=", "inactivo")
|
|
1103
|
+
.update({ estado: "archivado" });
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
### Eliminar datos
|
|
1107
|
+
|
|
1108
|
+
Utiliza el método `delete` para eliminar registros. **Requiere condiciones WHERE**.
|
|
1109
|
+
|
|
1110
|
+
```typescript
|
|
1111
|
+
// Eliminar registro específico
|
|
1112
|
+
await tablaUsuarios
|
|
1113
|
+
.where("id", "=", 2)
|
|
1114
|
+
.delete();
|
|
1115
|
+
|
|
1116
|
+
// Eliminar con condiciones
|
|
1117
|
+
await tablaUsuarios
|
|
1118
|
+
.where("estado", "=", "eliminado")
|
|
1119
|
+
.where("fecha_creacion", "<", "2023-01-01")
|
|
1120
|
+
.delete();
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
## Consultas avanzadas
|
|
1124
|
+
|
|
1125
|
+
### Consulta con WHERE
|
|
1126
|
+
|
|
1127
|
+
Filtra registros utilizando el método `where` con varios operadores.
|
|
1128
|
+
|
|
1129
|
+
```typescript
|
|
1130
|
+
// Comparaciones básicas
|
|
1131
|
+
const usuariosAdultos = await tablaUsuarios.where("edad", ">", 18).get();
|
|
1132
|
+
const coincidenciaExacta = await tablaUsuarios.where("nombre", "=", "Alicia").get();
|
|
1133
|
+
const noIgual = await tablaUsuarios.where("estado", "!=", "eliminado").get();
|
|
1134
|
+
|
|
1135
|
+
// Operaciones con strings
|
|
1136
|
+
const usuariosGmail = await tablaUsuarios.where("email", "LIKE", "%@gmail.com").get();
|
|
1137
|
+
|
|
1138
|
+
console.log(usuariosAdultos);
|
|
1139
|
+
// [{ id: 1, nombre: 'Alicia', edad: 28 }, ...]
|
|
1140
|
+
```
|
|
1141
|
+
|
|
1142
|
+
### Consulta con OR WHERE
|
|
1143
|
+
|
|
1144
|
+
Utiliza `orWhere` para añadir condiciones OR a tu consulta.
|
|
1145
|
+
|
|
1146
|
+
```typescript
|
|
1147
|
+
const usuarios = await tablaUsuarios
|
|
1148
|
+
.where("edad", ">", 25)
|
|
1149
|
+
.orWhere("nombre", "=", "Alicia")
|
|
1150
|
+
.get();
|
|
1151
|
+
|
|
1152
|
+
// Condiciones OR complejas
|
|
1153
|
+
const usuariosPremium = await tablaUsuarios
|
|
1154
|
+
.where("suscripcion", "=", "premium")
|
|
1155
|
+
.orWhere("compras_totales", ">", 1000)
|
|
1156
|
+
.orWhere("miembro_desde", "<", "2020-01-01")
|
|
1157
|
+
.get();
|
|
1158
|
+
|
|
1159
|
+
console.log(usuarios);
|
|
1160
|
+
// [{ id: 1, nombre: 'Alicia', edad: 28 }, ...]
|
|
1161
|
+
```
|
|
1162
|
+
|
|
1163
|
+
### Consulta con grupos de condiciones WHERE
|
|
1164
|
+
|
|
1165
|
+
Agrupa condiciones utilizando `whereGroup` para lógica compleja.
|
|
1166
|
+
|
|
1167
|
+
```typescript
|
|
1168
|
+
// (edad > 25 OR nombre = 'Juana') AND estado = 'activo'
|
|
1169
|
+
const usuarios = await tablaUsuarios
|
|
1170
|
+
.whereGroup((query) => {
|
|
1171
|
+
query.where("edad", ">", 25).orWhere("nombre", "=", "Juana");
|
|
1172
|
+
})
|
|
1173
|
+
.where("estado", "=", "activo")
|
|
1174
|
+
.get();
|
|
1175
|
+
|
|
1176
|
+
// Grupos anidados
|
|
1177
|
+
const consultaCompleja = await tablaUsuarios
|
|
1178
|
+
.whereGroup((query) => {
|
|
1179
|
+
query.where("pais", "=", "ES").orWhere("pais", "=", "MX");
|
|
1180
|
+
})
|
|
1181
|
+
.where("activo", "=", true)
|
|
1182
|
+
.whereGroup((query) => {
|
|
1183
|
+
query.where("edad", ">=", 21).orWhere("verificado", "=", true);
|
|
1184
|
+
})
|
|
1185
|
+
.get();
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
### Consulta con BETWEEN
|
|
1189
|
+
|
|
1190
|
+
Busca valores dentro de un rango utilizando `whereBetween`.
|
|
1191
|
+
|
|
1192
|
+
```typescript
|
|
1193
|
+
// Edad entre 25 y 35
|
|
1194
|
+
const usuarios = await tablaUsuarios.whereBetween("edad", [25, 35]).get();
|
|
1195
|
+
|
|
1196
|
+
// Rangos de fechas
|
|
1197
|
+
const usuariosRecientes = await tablaUsuarios
|
|
1198
|
+
.whereBetween("fecha_creacion", ["2024-01-01", "2024-12-31"])
|
|
1199
|
+
.get();
|
|
1200
|
+
|
|
1201
|
+
console.log(usuarios);
|
|
1202
|
+
// [{ id: 1, nombre: 'Alicia', edad: 28 }, { id: 2, nombre: 'Roberto', edad: 32 }]
|
|
1203
|
+
```
|
|
1204
|
+
|
|
1205
|
+
### Consulta con IN
|
|
1206
|
+
|
|
1207
|
+
Busca valores que coincidan con un conjunto de valores utilizando `whereIn`.
|
|
1208
|
+
|
|
1209
|
+
```typescript
|
|
1210
|
+
// IDs específicos
|
|
1211
|
+
const usuarios = await tablaUsuarios.whereIn("id", [1, 3, 5]).get();
|
|
1212
|
+
|
|
1213
|
+
// Múltiples estados
|
|
1214
|
+
const usuariosFiltrados = await tablaUsuarios
|
|
1215
|
+
.whereIn("estado", ["activo", "pendiente", "verificado"])
|
|
1216
|
+
.get();
|
|
1217
|
+
|
|
1218
|
+
// Valores string
|
|
1219
|
+
const dominiosEmail = await tablaUsuarios
|
|
1220
|
+
.whereIn("dominio_email", ["gmail.com", "yahoo.com", "hotmail.com"])
|
|
1221
|
+
.get();
|
|
1222
|
+
|
|
1223
|
+
console.log(usuarios);
|
|
1224
|
+
// [{ id: 1, nombre: 'Alicia', edad: 28 }, { id: 3, nombre: 'Carlos', edad: 35 }]
|
|
1225
|
+
```
|
|
1226
|
+
|
|
1227
|
+
### Consulta con IS NULL / IS NOT NULL
|
|
1228
|
+
|
|
1229
|
+
Busca valores nulos o no nulos utilizando `whereNull` y `whereNotNull`.
|
|
1230
|
+
|
|
1231
|
+
```typescript
|
|
1232
|
+
// Usuarios sin email
|
|
1233
|
+
const usuariosSinEmail = await tablaUsuarios.whereNull("email").get();
|
|
1234
|
+
|
|
1235
|
+
// Usuarios con email
|
|
1236
|
+
const usuariosConEmail = await tablaUsuarios.whereNotNull("email").get();
|
|
1237
|
+
|
|
1238
|
+
// Combinar con otras condiciones
|
|
1239
|
+
const perfilesIncompletos = await tablaUsuarios
|
|
1240
|
+
.whereNull("telefono")
|
|
1241
|
+
.orWhere("avatar", "IS NULL")
|
|
1242
|
+
.whereNotNull("email")
|
|
1243
|
+
.get();
|
|
1244
|
+
```
|
|
1245
|
+
|
|
1246
|
+
### Consulta con JOIN
|
|
1247
|
+
|
|
1248
|
+
Une tablas utilizando el método `join` para INNER JOIN.
|
|
1249
|
+
|
|
1250
|
+
```typescript
|
|
1251
|
+
// INNER JOIN básico
|
|
1252
|
+
const usuariosConPedidos = await tablaUsuarios
|
|
1253
|
+
.join("pedidos", "usuarios.id", "=", "pedidos.usuario_id")
|
|
1254
|
+
.select(["usuarios.nombre", "pedidos.pedido_id", "pedidos.total"])
|
|
1255
|
+
.get();
|
|
1256
|
+
|
|
1257
|
+
// Múltiples JOINs
|
|
1258
|
+
const pedidosDetallados = await tablaUsuarios
|
|
1259
|
+
.join("pedidos", "usuarios.id", "=", "pedidos.usuario_id")
|
|
1260
|
+
.join("items_pedido", "pedidos.id", "=", "items_pedido.pedido_id")
|
|
1261
|
+
.join("productos", "items_pedido.producto_id", "=", "productos.id")
|
|
1262
|
+
.select(["usuarios.nombre", "pedidos.pedido_id", "productos.nombre AS nombre_producto"])
|
|
1263
|
+
.get();
|
|
1264
|
+
|
|
1265
|
+
console.log(usuariosConPedidos);
|
|
1266
|
+
// [{ nombre: 'Alicia', pedido_id: 101, total: 150.00 }, ...]
|
|
1267
|
+
```
|
|
1268
|
+
|
|
1269
|
+
### Consulta con LEFT JOIN
|
|
1270
|
+
|
|
1271
|
+
Realiza un left join utilizando el método `leftJoin`.
|
|
1272
|
+
|
|
1273
|
+
```typescript
|
|
1274
|
+
// Incluir usuarios aunque no tengan pedidos
|
|
1275
|
+
const usuariosConPedidos = await tablaUsuarios
|
|
1276
|
+
.leftJoin("pedidos", "usuarios.id", "=", "pedidos.usuario_id")
|
|
1277
|
+
.select(["usuarios.nombre", "pedidos.pedido_id"])
|
|
1278
|
+
.get();
|
|
1279
|
+
|
|
1280
|
+
// Left join con agregación
|
|
1281
|
+
const contadorPedidosUsuarios = await tablaUsuarios
|
|
1282
|
+
.leftJoin("pedidos", "usuarios.id", "=", "pedidos.usuario_id")
|
|
1283
|
+
.select(["usuarios.nombre"])
|
|
1284
|
+
.count("pedidos.id")
|
|
1285
|
+
.groupBy("usuarios.id")
|
|
1286
|
+
.get();
|
|
1287
|
+
|
|
1288
|
+
console.log(usuariosConPedidos);
|
|
1289
|
+
// [{ nombre: 'Alicia', pedido_id: 101 }, { nombre: 'Roberto', pedido_id: null }, ...]
|
|
1290
|
+
```
|
|
1291
|
+
|
|
1292
|
+
### Consulta con RIGHT JOIN
|
|
1293
|
+
|
|
1294
|
+
Realiza un right join utilizando el método `rightJoin`.
|
|
1295
|
+
|
|
1296
|
+
```typescript
|
|
1297
|
+
// Incluir pedidos aunque falten datos del usuario
|
|
1298
|
+
const pedidosConUsuarios = await tablaUsuarios
|
|
1299
|
+
.rightJoin("pedidos", "usuarios.id", "=", "pedidos.usuario_id")
|
|
1300
|
+
.select(["usuarios.nombre", "pedidos.pedido_id"])
|
|
1301
|
+
.get();
|
|
1302
|
+
|
|
1303
|
+
console.log(pedidosConUsuarios);
|
|
1304
|
+
// [{ nombre: 'Alicia', pedido_id: 101 }, { nombre: null, pedido_id: 102 }, ...]
|
|
1305
|
+
```
|
|
1306
|
+
|
|
1307
|
+
### Consulta con ORDER BY
|
|
1308
|
+
|
|
1309
|
+
Ordena resultados utilizando el método `orderBy`.
|
|
1310
|
+
|
|
1311
|
+
```typescript
|
|
1312
|
+
// Ordenamiento de una columna
|
|
1313
|
+
const usuariosOrdenados = await tablaUsuarios
|
|
1314
|
+
.orderBy("nombre", "ASC")
|
|
1315
|
+
.get();
|
|
1316
|
+
|
|
1317
|
+
// Ordenamiento de múltiples columnas
|
|
1318
|
+
const ordenComplejo = await tablaUsuarios
|
|
1319
|
+
.orderBy("pais", "ASC")
|
|
1320
|
+
.orderBy("edad", "DESC")
|
|
1321
|
+
.orderBy("nombre", "ASC")
|
|
1322
|
+
.get();
|
|
1323
|
+
|
|
1324
|
+
// Ordenar con condiciones
|
|
1325
|
+
const usuariosActivosRecientes = await tablaUsuarios
|
|
1326
|
+
.where("estado", "=", "activo")
|
|
1327
|
+
.orderBy("ultimo_acceso", "DESC")
|
|
1328
|
+
.limit(20)
|
|
1329
|
+
.get();
|
|
1330
|
+
|
|
1331
|
+
console.log(usuariosOrdenados);
|
|
1332
|
+
// [{ id: 1, nombre: 'Alicia', ... }, { id: 2, nombre: 'Roberto', ... }]
|
|
1333
|
+
```
|
|
1334
|
+
|
|
1335
|
+
### Consulta con LIMIT y OFFSET (paginación)
|
|
1336
|
+
|
|
1337
|
+
Limita el número de resultados e implementa paginación utilizando `limit` y `page`.
|
|
1338
|
+
|
|
1339
|
+
```typescript
|
|
1340
|
+
// Límite simple
|
|
1341
|
+
const primerosDiezUsuarios = await tablaUsuarios.limit(10).get();
|
|
1342
|
+
|
|
1343
|
+
// Paginación
|
|
1344
|
+
const primeraPagina = await tablaUsuarios.limit(5).page(1).get();
|
|
1345
|
+
const segundaPagina = await tablaUsuarios.limit(5).page(2).get();
|
|
1346
|
+
|
|
1347
|
+
// Paginación con ordenamiento
|
|
1348
|
+
const usuariosPaginados = await tablaUsuarios
|
|
1349
|
+
.orderBy("fecha_creacion", "DESC")
|
|
1350
|
+
.limit(10)
|
|
1351
|
+
.page(3) // Omitir los primeros 20 registros (páginas 1-2)
|
|
1352
|
+
.get();
|
|
1353
|
+
|
|
1354
|
+
console.log(primeraPagina); // Registros 1-5
|
|
1355
|
+
console.log(segundaPagina); // Registros 6-10
|
|
1356
|
+
```
|
|
1357
|
+
|
|
1358
|
+
### Consulta con GROUP BY
|
|
1359
|
+
|
|
1360
|
+
Agrupa resultados utilizando el método `groupBy`.
|
|
1361
|
+
|
|
1362
|
+
```typescript
|
|
1363
|
+
// Agrupamiento simple
|
|
1364
|
+
const usuariosPorEdad = await tablaUsuarios
|
|
1365
|
+
.select(["edad"])
|
|
1366
|
+
.count("*")
|
|
1367
|
+
.groupBy("edad")
|
|
1368
|
+
.get();
|
|
1369
|
+
|
|
1370
|
+
// Múltiples columnas de agrupamiento
|
|
1371
|
+
const usuariosPorPaisYEdad = await tablaUsuarios
|
|
1372
|
+
.select(["pais", "edad"])
|
|
1373
|
+
.count("*")
|
|
1374
|
+
.groupBy("pais")
|
|
1375
|
+
.groupBy("edad")
|
|
1376
|
+
.get();
|
|
1377
|
+
|
|
1378
|
+
console.log(usuariosPorEdad);
|
|
1379
|
+
// [{ edad: 28, count: 2 }, { edad: 32, count: 1 }]
|
|
1380
|
+
```
|
|
1381
|
+
|
|
1382
|
+
### Consulta con DISTINCT
|
|
1383
|
+
|
|
1384
|
+
Recupera registros únicos utilizando el método `distinct`.
|
|
1385
|
+
|
|
1386
|
+
```typescript
|
|
1387
|
+
// Valores únicos
|
|
1388
|
+
const paisesUnicos = await tablaUsuarios
|
|
1389
|
+
.distinct()
|
|
1390
|
+
.select(["pais"])
|
|
1391
|
+
.get();
|
|
1392
|
+
|
|
1393
|
+
// Distinct con condiciones
|
|
1394
|
+
const paisesUsuariosActivos = await tablaUsuarios
|
|
1395
|
+
.distinct()
|
|
1396
|
+
.select(["pais"])
|
|
1397
|
+
.where("estado", "=", "activo")
|
|
1398
|
+
.get();
|
|
1399
|
+
|
|
1400
|
+
console.log(paisesUnicos);
|
|
1401
|
+
// [{ pais: 'España' }, { pais: 'México' }, { pais: 'Argentina' }]
|
|
1402
|
+
```
|
|
1403
|
+
|
|
1404
|
+
## Funciones de agregación
|
|
1405
|
+
|
|
1406
|
+
### count
|
|
1407
|
+
|
|
1408
|
+
Cuenta el número de registros.
|
|
1409
|
+
|
|
1410
|
+
```typescript
|
|
1411
|
+
// Contar todos los registros
|
|
1412
|
+
const totalUsuarios = await tablaUsuarios.count().first();
|
|
1413
|
+
console.log(totalUsuarios); // { count: 150 }
|
|
1414
|
+
|
|
1415
|
+
// Contar columna específica
|
|
1416
|
+
const usuariosConEmail = await tablaUsuarios.count("email").first();
|
|
1417
|
+
|
|
1418
|
+
// Contar con condiciones
|
|
1419
|
+
const usuariosActivos = await tablaUsuarios
|
|
1420
|
+
.where("estado", "=", "activo")
|
|
1421
|
+
.count()
|
|
1422
|
+
.first();
|
|
1423
|
+
```
|
|
1424
|
+
|
|
1425
|
+
### sum
|
|
1426
|
+
|
|
1427
|
+
Calcula la suma de una columna.
|
|
1428
|
+
|
|
1429
|
+
```typescript
|
|
1430
|
+
// Suma de edades
|
|
1431
|
+
const edadTotal = await tablaUsuarios.sum("edad").first();
|
|
1432
|
+
console.log(edadTotal); // { sum: 4250 }
|
|
1433
|
+
|
|
1434
|
+
// Suma con condiciones
|
|
1435
|
+
const ingresosPremium = await tablaUsuarios
|
|
1436
|
+
.join("pedidos", "usuarios.id", "=", "pedidos.usuario_id")
|
|
1437
|
+
.where("usuarios.suscripcion", "=", "premium")
|
|
1438
|
+
.sum("pedidos.total")
|
|
1439
|
+
.first();
|
|
1440
|
+
```
|
|
1441
|
+
|
|
1442
|
+
### avg
|
|
1443
|
+
|
|
1444
|
+
Calcula el promedio de una columna.
|
|
1445
|
+
|
|
1446
|
+
```typescript
|
|
1447
|
+
// Edad promedio
|
|
1448
|
+
const edadPromedio = await tablaUsuarios.avg("edad").first();
|
|
1449
|
+
console.log(edadPromedio); // { avg: 28.33 }
|
|
1450
|
+
|
|
1451
|
+
// Promedio con agrupamiento
|
|
1452
|
+
const edadPromedioPorPais = await tablaUsuarios
|
|
1453
|
+
.select(["pais"])
|
|
1454
|
+
.avg("edad")
|
|
1455
|
+
.groupBy("pais")
|
|
1456
|
+
.get();
|
|
1457
|
+
```
|
|
1458
|
+
|
|
1459
|
+
### max
|
|
1460
|
+
|
|
1461
|
+
Encuentra el valor máximo en una columna.
|
|
1462
|
+
|
|
1463
|
+
```typescript
|
|
1464
|
+
// Usuario más viejo
|
|
1465
|
+
const edadMaxima = await tablaUsuarios.max("edad").first();
|
|
1466
|
+
console.log(edadMaxima); // { max: 65 }
|
|
1467
|
+
|
|
1468
|
+
// Registro más reciente
|
|
1469
|
+
const usuarioMasReciente = await tablaUsuarios.max("fecha_creacion").first();
|
|
1470
|
+
```
|
|
1471
|
+
|
|
1472
|
+
### min
|
|
1473
|
+
|
|
1474
|
+
Encuentra el valor mínimo en una columna.
|
|
1475
|
+
|
|
1476
|
+
```typescript
|
|
1477
|
+
// Usuario más joven
|
|
1478
|
+
const edadMinima = await tablaUsuarios.min("edad").first();
|
|
1479
|
+
console.log(edadMinima); // { min: 18 }
|
|
1480
|
+
|
|
1481
|
+
// Primer registro
|
|
1482
|
+
const primerUsuario = await tablaUsuarios.min("fecha_creacion").first();
|
|
1483
|
+
```
|
|
1484
|
+
|
|
1485
|
+
## Buscar registros
|
|
1486
|
+
|
|
1487
|
+
### find
|
|
1488
|
+
|
|
1489
|
+
Encuentra un registro por un valor específico de columna (por defecto 'id').
|
|
1490
|
+
|
|
1491
|
+
```typescript
|
|
1492
|
+
// Buscar por ID (por defecto)
|
|
1493
|
+
const usuario = await tablaUsuarios.find(1);
|
|
1494
|
+
console.log(usuario);
|
|
1495
|
+
// { id: 1, nombre: 'Alicia', email: 'alicia@ejemplo.com', edad: 28 }
|
|
1496
|
+
|
|
1497
|
+
// Buscar por columna específica
|
|
1498
|
+
const usuarioPorEmail = await tablaUsuarios.find("alicia@ejemplo.com", "email");
|
|
1499
|
+
|
|
1500
|
+
// Find devuelve null si no se encuentra
|
|
1501
|
+
const noExistente = await tablaUsuarios.find(999);
|
|
1502
|
+
console.log(noExistente); // null
|
|
1503
|
+
```
|
|
1504
|
+
|
|
1505
|
+
### first
|
|
1506
|
+
|
|
1507
|
+
Obtiene solo el primer registro que cumple con las condiciones.
|
|
1508
|
+
|
|
1509
|
+
```typescript
|
|
1510
|
+
// Primer usuario que cumple la condición
|
|
1511
|
+
const primerUsuario = await tablaUsuarios
|
|
1512
|
+
.where("edad", ">", 25)
|
|
1513
|
+
.orderBy("fecha_creacion", "ASC")
|
|
1514
|
+
.first();
|
|
1515
|
+
|
|
1516
|
+
// Primer usuario en general
|
|
1517
|
+
const cuentaMasAntigua = await tablaUsuarios
|
|
1518
|
+
.orderBy("fecha_creacion", "ASC")
|
|
1519
|
+
.first();
|
|
1520
|
+
|
|
1521
|
+
console.log(primerUsuario);
|
|
1522
|
+
// { id: 1, nombre: 'Alicia', edad: 28, ... } o null si no hay coincidencia
|
|
1523
|
+
```
|
|
1524
|
+
|
|
1525
|
+
## Campos calculados y triggers
|
|
1526
|
+
|
|
1527
|
+
DBCube Query Builder soporta campos calculados y triggers para procesamiento avanzado de datos.
|
|
1528
|
+
|
|
1529
|
+
```typescript
|
|
1530
|
+
// Habilitar campos calculados (procesados automáticamente)
|
|
1531
|
+
await db.useComputes();
|
|
1532
|
+
|
|
1533
|
+
// Habilitar triggers
|
|
1534
|
+
await db.useTriggers();
|
|
1535
|
+
|
|
1536
|
+
// Los triggers y campos calculados se configuran a través de otros componentes DBCube
|
|
1537
|
+
```
|
|
1538
|
+
|
|
1539
|
+
## Ejecutar consultas SQL crudas
|
|
1540
|
+
|
|
1541
|
+
Para consultas complejas que requieren SQL crudo, utiliza el motor subyacente.
|
|
1542
|
+
|
|
1543
|
+
```typescript
|
|
1544
|
+
// Acceder al motor subyacente para consultas crudas
|
|
1545
|
+
// (La implementación depende de tu configuración específica de DBCube core)
|
|
1546
|
+
|
|
1547
|
+
// Nota: Las consultas SQL crudas evitan la capa de abstracción del query builder
|
|
1548
|
+
// y son específicas de la base de datos
|
|
1549
|
+
```
|
|
1550
|
+
|
|
1551
|
+
### Manejo de errores
|
|
1552
|
+
|
|
1553
|
+
La biblioteca proporciona manejo comprehensivo de errores con mensajes descriptivos.
|
|
1554
|
+
|
|
1555
|
+
```typescript
|
|
1556
|
+
try {
|
|
1557
|
+
// Esto arrojará un error - UPDATE requiere condiciones WHERE
|
|
1558
|
+
await tablaUsuarios.update({ nombre: "Actualizado" });
|
|
1559
|
+
} catch (error) {
|
|
1560
|
+
console.error(error.message);
|
|
1561
|
+
// "Debes especificar al menos una condición WHERE para realizar una actualización."
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
try {
|
|
1565
|
+
// Esto arrojará un error - formato de datos inválido
|
|
1566
|
+
await tablaUsuarios.insert("datos inválidos");
|
|
1567
|
+
} catch (error) {
|
|
1568
|
+
console.error(error.message);
|
|
1569
|
+
// "El método insert requiere un array de objetos con pares clave-valor."
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
// Los errores de conexión y base de datos también se manejan apropiadamente
|
|
1573
|
+
try {
|
|
1574
|
+
const resultado = await tablaUsuarios.get();
|
|
1575
|
+
} catch (error) {
|
|
1576
|
+
// Errores de conexión a base de datos o ejecución de consultas
|
|
1577
|
+
console.error("Error de base de datos:", error);
|
|
1578
|
+
}
|
|
1579
|
+
```
|
|
1580
|
+
|
|
1581
|
+
## API completa
|
|
1582
|
+
|
|
1583
|
+
### Clase Database
|
|
1584
|
+
|
|
1585
|
+
#### `new Database(name: string)`
|
|
1586
|
+
|
|
1587
|
+
Crea una nueva instancia de conexión a base de datos.
|
|
1588
|
+
|
|
1589
|
+
```typescript
|
|
1590
|
+
const db = new Database("mi_base_de_datos");
|
|
1591
|
+
```
|
|
1592
|
+
|
|
1593
|
+
#### `table(tableName: string): Table`
|
|
1594
|
+
|
|
1595
|
+
Devuelve una instancia Table para construir consultas en la tabla especificada.
|
|
1596
|
+
|
|
1597
|
+
```typescript
|
|
1598
|
+
const tablaUsuarios = db.table("usuarios");
|
|
1599
|
+
```
|
|
1600
|
+
|
|
1601
|
+
#### `useComputes(): Promise<void>`
|
|
1602
|
+
|
|
1603
|
+
Habilita el procesamiento de campos calculados para la base de datos.
|
|
1604
|
+
|
|
1605
|
+
```typescript
|
|
1606
|
+
await db.useComputes();
|
|
1607
|
+
```
|
|
1608
|
+
|
|
1609
|
+
#### `useTriggers(): Promise<void>`
|
|
1610
|
+
|
|
1611
|
+
Habilita el procesamiento de triggers para la base de datos.
|
|
1612
|
+
|
|
1613
|
+
```typescript
|
|
1614
|
+
await db.useTriggers();
|
|
1615
|
+
```
|
|
1616
|
+
|
|
1617
|
+
#### `connect(): Promise<void>`
|
|
1618
|
+
|
|
1619
|
+
Establece conexión a base de datos (manejado automáticamente).
|
|
1620
|
+
|
|
1621
|
+
#### `disconnect(): Promise<void>`
|
|
1622
|
+
|
|
1623
|
+
Cierra conexión a base de datos.
|
|
1624
|
+
|
|
1625
|
+
### Clase Table
|
|
1626
|
+
|
|
1627
|
+
Los métodos de la clase Table siguen la misma API que se documentó en la sección en inglés, con la funcionalidad idéntica.
|
|
1628
|
+
|
|
1629
|
+
## Soporte multi-base de datos
|
|
1630
|
+
|
|
1631
|
+
DBCube Query Builder funciona con múltiples motores de base de datos:
|
|
1632
|
+
|
|
1633
|
+
```typescript
|
|
1634
|
+
// MySQL
|
|
1635
|
+
const mysqlDb = new Database("base_datos_mysql");
|
|
1636
|
+
|
|
1637
|
+
// PostgreSQL
|
|
1638
|
+
const postgresDb = new Database("base_datos_postgres");
|
|
1639
|
+
|
|
1640
|
+
// SQLite
|
|
1641
|
+
const sqliteDb = new Database("base_datos_sqlite");
|
|
1642
|
+
|
|
1643
|
+
// MongoDB
|
|
1644
|
+
const mongoDb = new Database("base_datos_mongo");
|
|
1645
|
+
|
|
1646
|
+
// La misma API funciona a través de todas las bases de datos
|
|
1647
|
+
const usuarios = await mysqlDb.table("usuarios").get();
|
|
1648
|
+
const posts = await postgresDb.table("posts").get();
|
|
1649
|
+
const logs = await sqliteDb.table("logs").get();
|
|
1650
|
+
const analiticas = await mongoDb.table("analiticas").get();
|
|
1651
|
+
```
|
|
1652
|
+
|
|
1653
|
+
## Características avanzadas
|
|
1654
|
+
|
|
1655
|
+
### Ejemplo de consulta compleja
|
|
1656
|
+
|
|
1657
|
+
```typescript
|
|
1658
|
+
// Consulta de negocio compleja
|
|
1659
|
+
const reporteMensual = await db.table("pedidos")
|
|
1660
|
+
.join("usuarios", "pedidos.usuario_id", "=", "usuarios.id")
|
|
1661
|
+
.join("items_pedido", "pedidos.id", "=", "items_pedido.pedido_id")
|
|
1662
|
+
.join("productos", "items_pedido.producto_id", "=", "productos.id")
|
|
1663
|
+
.select([
|
|
1664
|
+
"usuarios.pais",
|
|
1665
|
+
"productos.categoria"
|
|
1666
|
+
])
|
|
1667
|
+
.sum("items_pedido.cantidad * items_pedido.precio")
|
|
1668
|
+
.where("pedidos.estado", "=", "completado")
|
|
1669
|
+
.whereBetween("pedidos.fecha_creacion", ["2024-01-01", "2024-01-31"])
|
|
1670
|
+
.groupBy("usuarios.pais")
|
|
1671
|
+
.groupBy("productos.categoria")
|
|
1672
|
+
.orderBy("sum", "DESC")
|
|
1673
|
+
.limit(10)
|
|
1674
|
+
.get();
|
|
1675
|
+
```
|
|
1676
|
+
|
|
1677
|
+
### Encadenamiento de métodos
|
|
1678
|
+
|
|
1679
|
+
Todos los métodos del query builder devuelven la instancia Table, habilitando el encadenamiento fluido de métodos:
|
|
1680
|
+
|
|
1681
|
+
```typescript
|
|
1682
|
+
const resultado = await db
|
|
1683
|
+
.table("usuarios")
|
|
1684
|
+
.select(["nombre", "email", "pais"])
|
|
1685
|
+
.where("activo", "=", true)
|
|
1686
|
+
.whereNotNull("email_verificado_en")
|
|
1687
|
+
.whereGroup((query) => {
|
|
1688
|
+
query.where("suscripcion", "=", "premium")
|
|
1689
|
+
.orWhere("pedidos_totales", ">", 10);
|
|
1690
|
+
})
|
|
1691
|
+
.join("perfiles_usuario", "usuarios.id", "=", "perfiles_usuario.usuario_id")
|
|
1692
|
+
.orderBy("usuarios.fecha_creacion", "DESC")
|
|
1693
|
+
.limit(50)
|
|
1694
|
+
.page(1)
|
|
1695
|
+
.get();
|
|
1696
|
+
```
|
|
131
1697
|
|
|
132
|
-
##
|
|
1698
|
+
## Licencia
|
|
133
1699
|
|
|
134
|
-
|
|
1700
|
+
Este proyecto está licenciado bajo la Licencia MIT - consulta el archivo LICENSE para más detalles.
|