sql-love 1.0.5 → 1.1.0

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 CHANGED
@@ -1,17 +1,17 @@
1
- <img alt="SQL Love Logo" src="https://github.com/kensnyder/sql-love/raw/main/assets/sql-love-logo.png?v=1.0.5" width="250" height="208" />
1
+ <img alt="SQL Love Logo" src="https://github.com/kensnyder/sql-love/raw/main/assets/sql-love-logo.png?v=1.1.0" width="250" height="208" />
2
2
 
3
3
  # sql-love
4
4
 
5
- [![NPM Link](https://badgen.net/npm/v/sql-love?v=1.0.5)](https://npmjs.com/package/sql-love)
6
- [![Language](https://badgen.net/static/language/TS?v=1.0.5)](https://github.com/search?q=repo:kensnyder/sql-love++language:TypeScript&type=code)
7
- [![Build Status](https://github.com/kensnyder/sql-love/actions/workflows/node.js.yml/badge.svg?v=1.0.5)](https://github.com/kensnyder/sql-love/actions)
8
- [![Code Coverage](https://codecov.io/gh/kensnyder/sql-love/branch/main/graph/badge.svg?v=1.0.5)](https://codecov.io/gh/kensnyder/sql-love)
9
- [![Gzipped Size](https://badgen.net/bundlephobia/minzip/sql-love?label=minzipped&v=1.0.5)](https://bundlephobia.com/package/sql-love@1.0.5)
10
- [![Dependency details](https://badgen.net/bundlephobia/dependency-count/sql-love?v=1.0.5)](https://www.npmjs.com/package/sql-love?activeTab=dependencies)
11
- [![Tree shakeable](https://badgen.net/bundlephobia/tree-shaking/sql-love?v=1.0.5)](https://www.npmjs.com/package/sql-love)
12
- [![ISC License](https://badgen.net/static/license/ISC/green?v=1.0.5)](https://opensource.org/licenses/ISC)
5
+ [![NPM Link](https://badgen.net/npm/v/sql-love?v=1.1.0)](https://npmjs.com/package/sql-love)
6
+ [![Language](https://badgen.net/static/language/TS?v=1.1.0)](https://github.com/search?q=repo:kensnyder/sql-love++language:TypeScript&type=code)
7
+ [![Build Status](https://github.com/kensnyder/sql-love/actions/workflows/node.js.yml/badge.svg?v=1.1.0)](https://github.com/kensnyder/sql-love/actions)
8
+ [![Code Coverage](https://codecov.io/gh/kensnyder/sql-love/branch/main/graph/badge.svg?v=1.1.0)](https://codecov.io/gh/kensnyder/sql-love)
9
+ ![GzippedSize](https://deno.bundlejs.com/?q=asql-love&badge&v=1.1.0)
10
+ ![Dependencies](https://badgen.net/static/dependencies/0/green)
11
+ [![Tree shakeable](https://badgen.net/bundlephobia/tree-shaking/sql-love?v=1.1.0)](https://www.npmjs.com/package/sql-love)
12
+ [![ISC License](https://badgen.net/static/license/ISC/green?v=1.1.0)](https://opensource.org/licenses/ISC)
13
13
 
14
- Classes for parsing and building SQL select queries in Node
14
+ Classes for parsing and building SQL select queries in Node/Bun/Deno.
15
15
 
16
16
  ## Installation
17
17
 
@@ -39,6 +39,7 @@ npm install sql-love
39
39
  - [toSafeJson functions](#tosafejson)
40
40
  - [How to contribute](./CONTRIBUTING.md)
41
41
  - [ISC license](./LICENSE.md)
42
+ - [Hello Bun](#hello-bun)
42
43
 
43
44
  ## SelectBuilder
44
45
 
@@ -189,15 +190,16 @@ Use the following methods to build queries. And see
189
190
  - `query.having(expression)` - Having an arbitrary expression
190
191
  - `query.having(columnValueRecord)` - Add multiple conditions
191
192
  - `query.orHaving(expressions)` - Multiple `having()`s joined by OR
192
- - `query.orderBy(column)` - Add ORDER BY clause
193
- - `query.sortField(column, mapNames)` - Add ORDER BY clause with mapNames
193
+ - `query.orderBy(column, direction)` - Add ORDER BY clause
194
+ - `query.sortField(column, mapNames, modifier)` - Add ORDER BY clause with mapNames
194
195
  - `query.limit(num)` - Limit by the given number
195
196
  - `query.offset(num)` - Specify an offset
196
197
  - `query.page(num)` - Automatically calculate offset based on limit and page
197
198
  - `query.columns(columnNames)` - Add column names to fetch (alias `select()`)
198
199
  - `query.column(columnName)` - Add a column name to fetch
199
- - `query.table(tableName)` - Specify the table in the FROM clause
200
- - `query.from(tableName)` - Same as above
200
+ - `query.table(tableName)` - Add a table to add to the FROM clause
201
+ - `query.tables(tableNames)` - Add an array of tables to add to the FROM clause
202
+ - `query.from(tableName)` - Alias for `table()`
201
203
  - `query.join(expression)` - Add a JOIN expression
202
204
  - `query.innerJoin(expression)` - Add an INNER JOIN expression
203
205
  - `query.leftJoin(expression)` - Add a LEFT JOIN expression
@@ -212,7 +214,13 @@ Use the following methods to build queries. And see
212
214
  ### More examples
213
215
 
214
216
  ```js
215
- // The following are equivalent
217
+ // building the FROM clause
218
+ query.column('id');
219
+ query.column('"Media"."postId"');
220
+ query.column('CONCAT(first_name, " ", last_name) AS full_name');
221
+ query.columns(['id', 'first_name', 'last_name']);
222
+
223
+ // Ways to test against NULL
216
224
  query.where('deleted_at', '=', null);
217
225
  query.where('deleted_at', '=', undefined);
218
226
  query.where('deleted_at', null);
@@ -221,15 +229,16 @@ query.where('deleted_at IS NULL');
221
229
  query.where({ deleted_at: null });
222
230
  query.where({ deleted_at: undefined });
223
231
 
224
- // the following demonstrate ways to use LIKE
232
+ // Using LIKE
225
233
  query.where('name', 'LIKE', 'son');
226
234
  query.where('name', 'LIKE ?', 'son');
227
235
  query.where('name', 'LIKE ?%', 'son');
228
236
  query.where('name', 'LIKE %?', 'son');
229
237
  query.where('name', 'LIKE %?%', 'son');
230
238
  query.where('name', 'LIKE', '%son%');
239
+ query.where('name', 'ILIKE %?', 'son');
231
240
 
232
- // The following demonstrate ways to use various operators
241
+ // Various operators
233
242
  query.where('price', '=', 100);
234
243
  query.where('price', '!=', 100);
235
244
  query.where('price', '<>', 100);
@@ -242,7 +251,7 @@ query.whereBetween('price', [100, null]); // price > 100
242
251
  query.whereBetween('price', [null, 200]); // price < 200
243
252
  query.whereBetween('price', [null, null]); // clause is ignored
244
253
 
245
- // The following demonstrate ways to use IN and NOT IN
254
+ // Using IN and NOT IN
246
255
  query.where('status', 'IN', ['pending', 'approved']);
247
256
  query.where('status', ['pending', 'approved']);
248
257
  query.where('status', '=', ['pending', 'approved']);
@@ -253,7 +262,7 @@ query.where({ 'status NOT IN': ['pending', 'approved'] });
253
262
  query.where({ 'status !=': ['pending', 'approved'] });
254
263
  query.where({ 'status <>': ['pending', 'approved'] });
255
264
 
256
- // the following demonstrates how to use question marks for binding
265
+ // Question marks for binding
257
266
  query.where(
258
267
  'users.id IN (SELECT user_id FROM roles WHERE customer_id IN (?, ?))',
259
268
  [1, 2]
@@ -264,7 +273,7 @@ query.where(
264
273
  [1]
265
274
  );
266
275
 
267
- // The following demonstrates using objects to specify multiple conditions
276
+ // Multiple conditions
268
277
  query.where({
269
278
  deleted_at: null,
270
279
  'price >': 100,
@@ -276,7 +285,7 @@ query.where({
276
285
 
277
286
  // Note: .having() supports the same signatures as .where()
278
287
 
279
- // The following demonstrate equivalent ways to use OR
288
+ // Ways to use OR
280
289
  query.orWhere([{ approved_at: null }, { denied_at: null }]);
281
290
  query.orWhere({ approved_at: null, denied_at: null });
282
291
  query.orWhere(['approved_at IS NULL', 'denied_at IS NULL']);
@@ -288,7 +297,7 @@ query.where('(approved_at IS NULL OR denied_at IS NULL)');
288
297
 
289
298
  // Note: .orHaving() supports the same signatures as .orWhere()
290
299
 
291
- // The following demonstrate joins
300
+ // Joins
292
301
  query.innerJoin('phone_numbers p ON p.user_id = u.id');
293
302
  query.leftJoin('phone_numbers p ON p.user_id = u.id AND p.type = ?', ['main']);
294
303
  query.outerJoin('phone_numbers p ON p.user_id = u.id AND p.type NOT IN(?, ?)', [
@@ -296,10 +305,40 @@ query.outerJoin('phone_numbers p ON p.user_id = u.id AND p.type NOT IN(?, ?)', [
296
305
  'cell',
297
306
  ]);
298
307
 
299
- // The following demonstrates pagination
300
- query.limit(10).page(3); // LIMIT 10 OFFSET 20
301
-
302
- // The following demonstrates cloning
308
+ // Grouping
309
+
310
+ // Sorting
311
+ query.orderBy('modified_at'); // ORDER BY modified_at
312
+ query.orderBy('modified_at', 'DESC'); // ORDER BY modified_at DESC
313
+ query.orderBy('-modified_at'); // ORDER BY modified_at DESC
314
+ query.orderBy('COUNT(*)'); // ORDER BY COUNT(*)
315
+ query.orderBy('COUNT(*)', 'DESC'); // ORDER BY COUNT(*) DESC
316
+ query.orderBy('COUNT(*) DESC'); // ORDER BY COUNT(*) DESC
317
+ query.orderBy('modified_at DESC NULLS LAST'); // ORDER BY modified_at DESC NULLS LAST
318
+ query.orderBy('modified_at USING >'); // ORDER BY modified_at USING >
319
+
320
+ // Sorting with support for hyphens and mapping
321
+ query.sortField('-modified_at'); // ORDER BY modified_at DESC
322
+ query.sortField('-created', { created: 'created_at' }); // ORDER BY created_at DESC
323
+ query.sortField(
324
+ '-created_at',
325
+ {
326
+ created_at: 'posts.created_timestamp',
327
+ },
328
+ 'NULLS LAST'
329
+ ); // ORDER BY posts.created_timestamp DESC NULLS LAST
330
+ query.sortField('-created', {
331
+ created: 'created_at NULLS LAST',
332
+ '-created': 'created_at DESC NULLS LAST',
333
+ }); // ORDER BY created_at DESC NULLS LAST
334
+ query.sortField('-name', {
335
+ name: 'name COLLATE "jp_JP"',
336
+ }); // ORDER BY name COLLATE "jp_JP" DESC
337
+
338
+ // Pagination
339
+ query.limit(10).page(3); // "LIMIT 10 OFFSET 20" (or for mssql, "OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY")
340
+
341
+ // Cloning for creating similar queries
303
342
  const query1 = new SelectBuilder();
304
343
  query1.where('email', 'john@example.com');
305
344
  const query2 = query1.getClone();
@@ -397,10 +436,10 @@ If you need to use SQL keywords in strings, use bindings.
397
436
 
398
437
  ```js
399
438
  // ❌ WILL NOT WORK
400
- new SelectBuilder(`SELECT id, CONCAT('where ', expr) FROM users`);
439
+ new SelectBuilder(`SELECT id, CONCAT('where ', column) FROM users`);
401
440
 
402
441
  // ✅ WORKING EQUIVALENT
403
- new SelectBuilder(`SELECT id, CONCAT(:prefix, expr) FROM users`, {
442
+ new SelectBuilder(`SELECT id, CONCAT(:prefix, column) FROM users`, {
404
443
  prefix: 'where ',
405
444
  });
406
445
  ```
@@ -497,7 +536,9 @@ const grouped = extractGrouped('dept', records);
497
536
  { id: 1, name: 'John', dept: 'Marketing' },
498
537
  { id: 3, name: 'Tim', dept: 'Marketing' },
499
538
  ],
500
- Finance: [{ id: 2, name: 'Jane', dept: 'Finance' }],
539
+ Finance: [
540
+ { id: 2, name: 'Jane', dept: 'Finance' }
541
+ ],
501
542
  }
502
543
  */
503
544
  ```
@@ -629,3 +670,8 @@ toSafeJsonString(records);
629
670
 
630
671
  // As you can see, BigInt values too big for Number are converted to strings
631
672
  ```
673
+
674
+ ## Hello Bun
675
+
676
+ This code repository uses [Bun](https://bun.sh) for a package manager, test
677
+ runner and builder.