foxhound 2.0.18 → 2.0.20
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 +3 -2
- package/docs/README.md +32 -11
- package/docs/_cover.md +3 -2
- package/docs/_sidebar.md +21 -1
- package/docs/_topbar.md +7 -0
- package/docs/api/README.md +73 -0
- package/docs/api/addFilter.md +170 -0
- package/docs/api/addJoin.md +128 -0
- package/docs/api/addRecord.md +108 -0
- package/docs/api/addSort.md +109 -0
- package/docs/api/behaviorFlags.md +123 -0
- package/docs/api/buildQuery.md +146 -0
- package/docs/api/clone.md +115 -0
- package/docs/api/queryOverrides.md +82 -0
- package/docs/api/setCap.md +115 -0
- package/docs/api/setDataElements.md +95 -0
- package/docs/api/setDialect.md +78 -0
- package/docs/api/setDistinct.md +76 -0
- package/docs/api/setIDUser.md +80 -0
- package/docs/api/setScope.md +70 -0
- package/docs/architecture.md +134 -42
- package/docs/dialects/README.md +2 -0
- package/docs/dialects/postgresql.md +126 -0
- package/docs/quickstart.md +196 -0
- package/docs/retold-catalog.json +40 -1
- package/docs/retold-keyword-index.json +7996 -2630
- package/package.json +2 -2
- package/source/Foxhound-Dialects.js +4 -0
- package/source/dialects/DGraph/FoxHound-Dialect-DGraph.js +954 -0
- package/source/dialects/MongoDB/FoxHound-Dialect-MongoDB.js +902 -0
- package/source/dialects/PostgreSQL/FoxHound-Dialect-PostgreSQL.js +865 -0
- package/source/dialects/Solr/FoxHound-Dialect-Solr.js +895 -0
- package/test/FoxHound-Dialect-DGraph_tests.js +547 -0
- package/test/FoxHound-Dialect-MongoDB_tests.js +485 -0
- package/test/FoxHound-Dialect-PostgreSQL_tests.js +342 -0
- package/test/FoxHound-Dialect-Solr_tests.js +551 -0
package/README.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
> A fluent query generation DSL for Node.js and the browser
|
|
4
4
|
|
|
5
|
-
FoxHound is a database query builder that generates dialect-specific SQL from a single chainable API. It keeps your application code database-agnostic while producing safe, parameterized queries for MySQL, Microsoft SQL Server, SQLite, and ALASQL.
|
|
5
|
+
FoxHound is a database query builder that generates dialect-specific SQL from a single chainable API. It keeps your application code database-agnostic while producing safe, parameterized queries for MySQL, PostgreSQL, Microsoft SQL Server, SQLite, and ALASQL.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
9
|
- **Chainable API** — every configuration method returns the query object for fluent composition
|
|
10
|
-
- **Multiple Dialects** — generate SQL for MySQL, MSSQL, SQLite, ALASQL, or plain English from the same code
|
|
10
|
+
- **Multiple Dialects** — generate SQL for MySQL, PostgreSQL, MSSQL, SQLite, ALASQL, or plain English from the same code
|
|
11
11
|
- **Parameterized Queries** — user-supplied values are always bound as named parameters, preventing SQL injection
|
|
12
12
|
- **Schema-Aware** — automatic management of identity columns, timestamps, user stamps, and soft-delete tracking
|
|
13
13
|
- **Full CRUD + Count** — build CREATE, READ, UPDATE, DELETE, UNDELETE, and COUNT queries
|
|
@@ -70,6 +70,7 @@ Application Code
|
|
|
70
70
|
| Dialect | Target | Identifier Quoting | Parameter Prefix | Pagination |
|
|
71
71
|
|---------|--------|-------------------|-----------------|------------|
|
|
72
72
|
| `MySQL` | MySQL / MariaDB | `` `backticks` `` | `:name` | `LIMIT offset, count` |
|
|
73
|
+
| `PostgreSQL` | PostgreSQL 9.5+ | `"double quotes"` | `:name` | `LIMIT count OFFSET offset` |
|
|
73
74
|
| `MSSQL` | Microsoft SQL Server | `[brackets]` | `@name` | `OFFSET/FETCH` |
|
|
74
75
|
| `SQLite` | SQLite 3 | `` `backticks` `` | `:name` | `LIMIT count OFFSET offset` |
|
|
75
76
|
| `ALASQL` | ALASQL (in-memory) | `` `backticks` `` | `:name` | `LIMIT count FETCH offset` |
|
package/docs/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# FoxHound
|
|
2
2
|
|
|
3
|
+
> A fluent query generation DSL for Node.js and the browser
|
|
4
|
+
|
|
3
5
|
FoxHound is a fluent query generation DSL for Node.js and the browser. It produces dialect-specific SQL (or other query formats) from a single chainable API, keeping your application code database-agnostic while generating safe, parameterized queries.
|
|
4
6
|
|
|
5
7
|
## Features
|
|
6
8
|
|
|
7
9
|
- **Chainable API** — every configuration method returns the query object, so you can compose queries in a single expression
|
|
8
|
-
- **Multiple Dialects** — generate SQL for MySQL, Microsoft SQL Server, SQLite, ALASQL, or plain English, all from the same code
|
|
10
|
+
- **Multiple Dialects** — generate SQL for MySQL, PostgreSQL, Microsoft SQL Server, SQLite, ALASQL, or plain English, all from the same code
|
|
9
11
|
- **Parameterized Queries** — user-supplied values are always bound as named parameters, preventing SQL injection
|
|
10
12
|
- **Schema-Aware** — when a schema is provided, FoxHound automatically manages identity columns, timestamps, user stamps, and soft-delete tracking
|
|
11
13
|
- **Full CRUD + Count** — build CREATE, READ, UPDATE, DELETE, UNDELETE, and COUNT queries
|
|
@@ -22,30 +24,49 @@ $ npm install foxhound
|
|
|
22
24
|
|
|
23
25
|
```javascript
|
|
24
26
|
var libFable = require('fable');
|
|
25
|
-
var _Fable = new libFable({});
|
|
26
|
-
|
|
27
|
-
var tmpQuery = _Fable.Utility.waterfall.FoxHound;
|
|
28
|
-
// Or create a new query directly:
|
|
29
27
|
var libFoxHound = require('foxhound');
|
|
28
|
+
|
|
29
|
+
var _Fable = new libFable({});
|
|
30
30
|
var tmpQuery = libFoxHound.new(_Fable);
|
|
31
31
|
|
|
32
|
-
tmpQuery
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
tmpQuery
|
|
33
|
+
.setScope('Books')
|
|
34
|
+
.setDataElements(['Title', 'Author', 'PublishedYear'])
|
|
35
|
+
.addFilter('Genre', 'Science Fiction')
|
|
36
|
+
.addSort({Column: 'PublishedYear', Direction: 'Descending'})
|
|
37
|
+
.setCap(25)
|
|
38
|
+
.setDialect('MySQL')
|
|
39
|
+
.buildReadQuery();
|
|
36
40
|
|
|
37
41
|
console.log(tmpQuery.query.body);
|
|
38
|
-
// => SELECT `
|
|
42
|
+
// => SELECT `Title`, `Author`, `PublishedYear` FROM `Books`
|
|
43
|
+
// WHERE `Books`.`Genre` = :Genre_w0 ORDER BY PublishedYear DESC LIMIT 25;
|
|
44
|
+
|
|
45
|
+
console.log(tmpQuery.query.parameters);
|
|
46
|
+
// => { Genre_w0: 'Science Fiction' }
|
|
39
47
|
```
|
|
40
48
|
|
|
41
49
|
## How It Works
|
|
42
50
|
|
|
43
51
|
1. **Create a Query** — instantiate via `foxhound.new(fable)` or through a Fable service
|
|
44
52
|
2. **Configure** — chain methods to set scope (table), fields, filters, sorts, joins, and pagination
|
|
45
|
-
3. **Set a Dialect** — call `.setDialect('MySQL')` (or MSSQL, SQLite, ALASQL, English)
|
|
53
|
+
3. **Set a Dialect** — call `.setDialect('MySQL')` (or PostgreSQL, MSSQL, SQLite, ALASQL, English)
|
|
46
54
|
4. **Build** — call a build method such as `.buildReadQuery()` to generate the SQL
|
|
47
55
|
5. **Execute** — read the generated SQL from `.query.body` and bound parameters from `.query.parameters`
|
|
48
56
|
|
|
57
|
+
## Documentation
|
|
58
|
+
|
|
59
|
+
- [Quickstart](quickstart.md) — get up and running in five minutes
|
|
60
|
+
- [Architecture](architecture.md) — understand FoxHound's internal design
|
|
61
|
+
- [Filters](filters.md) — filter operators and logical grouping
|
|
62
|
+
- [Sorting](sorting.md) — ORDER BY clause generation
|
|
63
|
+
- [Joins](joins.md) — multi-table queries
|
|
64
|
+
- [Pagination](pagination.md) — LIMIT/OFFSET across dialects
|
|
65
|
+
- [Schema Integration](schema.md) — automatic column management
|
|
66
|
+
- [Dialects](dialects/README.md) — dialect-specific features and differences
|
|
67
|
+
- [API Reference](api/README.md) — complete function reference with code examples
|
|
68
|
+
- [Query Overrides](query-overrides.md) — custom SQL templates
|
|
69
|
+
|
|
49
70
|
## Related Packages
|
|
50
71
|
|
|
51
72
|
| Package | Purpose |
|
package/docs/_cover.md
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
- Chainable API that generates dialect-specific SQL
|
|
6
6
|
- Parameterized queries for safe, injection-free operation
|
|
7
7
|
- Schema-aware with automatic identity, timestamp, and soft-delete management
|
|
8
|
-
- Multiple dialects: MySQL, MSSQL, SQLite, ALASQL, and more
|
|
8
|
+
- Multiple dialects: MySQL, PostgreSQL, MSSQL, SQLite, ALASQL, and more
|
|
9
9
|
|
|
10
|
+
[Get Started](quickstart.md)
|
|
11
|
+
[API Reference](api/README.md)
|
|
10
12
|
[GitHub](https://github.com/stevenvelozo/foxhound)
|
|
11
|
-
[Get Started](#foxhound)
|
package/docs/_sidebar.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
- Getting Started
|
|
2
2
|
|
|
3
|
-
- [Introduction](
|
|
3
|
+
- [Introduction](README.md)
|
|
4
|
+
- [Quickstart](quickstart.md)
|
|
4
5
|
- [Architecture](architecture.md)
|
|
5
6
|
|
|
6
7
|
- Query Building
|
|
@@ -23,10 +24,29 @@
|
|
|
23
24
|
|
|
24
25
|
- [Dialects Overview](dialects/README.md)
|
|
25
26
|
- [MySQL](dialects/mysql.md)
|
|
27
|
+
- [PostgreSQL](dialects/postgresql.md)
|
|
26
28
|
- [MSSQL](dialects/mssql.md)
|
|
27
29
|
- [SQLite](dialects/sqlite.md)
|
|
28
30
|
- [ALASQL](dialects/alasql.md)
|
|
29
31
|
|
|
32
|
+
- API Reference
|
|
33
|
+
|
|
34
|
+
- [API Overview](api/README.md)
|
|
35
|
+
- [setScope](api/setScope.md)
|
|
36
|
+
- [setDialect](api/setDialect.md)
|
|
37
|
+
- [setDataElements](api/setDataElements.md)
|
|
38
|
+
- [addFilter / setFilter](api/addFilter.md)
|
|
39
|
+
- [addSort / setSort](api/addSort.md)
|
|
40
|
+
- [addJoin / setJoin](api/addJoin.md)
|
|
41
|
+
- [setCap / setBegin](api/setCap.md)
|
|
42
|
+
- [addRecord](api/addRecord.md)
|
|
43
|
+
- [setIDUser](api/setIDUser.md)
|
|
44
|
+
- [setDistinct](api/setDistinct.md)
|
|
45
|
+
- [Build Methods](api/buildQuery.md)
|
|
46
|
+
- [clone / reset](api/clone.md)
|
|
47
|
+
- [Behavior Flags](api/behaviorFlags.md)
|
|
48
|
+
- [Query Overrides](api/queryOverrides.md)
|
|
49
|
+
|
|
30
50
|
- Advanced
|
|
31
51
|
|
|
32
52
|
- [Schema Integration](schema.md)
|
package/docs/_topbar.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
Complete reference for every public method and property in FoxHound.
|
|
4
|
+
|
|
5
|
+
## Creating a Query
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
var libFable = require('fable');
|
|
9
|
+
var libFoxHound = require('foxhound');
|
|
10
|
+
|
|
11
|
+
var _Fable = new libFable({});
|
|
12
|
+
var tmpQuery = libFoxHound.new(_Fable);
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Method Categories
|
|
16
|
+
|
|
17
|
+
### Configuration
|
|
18
|
+
|
|
19
|
+
These methods configure the query and return `this` for chaining.
|
|
20
|
+
|
|
21
|
+
| Method | Purpose |
|
|
22
|
+
|--------|---------|
|
|
23
|
+
| [setScope(pScope)](api/setScope.md) | Set the target table or collection |
|
|
24
|
+
| [setDialect(pDialectName)](api/setDialect.md) | Set the SQL dialect for output |
|
|
25
|
+
| [setDataElements(pDataElements)](api/setDataElements.md) | Set which columns to select |
|
|
26
|
+
| [setDistinct(pDistinct)](api/setDistinct.md) | Enable DISTINCT results |
|
|
27
|
+
| [addFilter / setFilter](api/addFilter.md) | Add or set WHERE clause conditions |
|
|
28
|
+
| [addSort / setSort](api/addSort.md) | Add or set ORDER BY expressions |
|
|
29
|
+
| [addJoin / setJoin](api/addJoin.md) | Add or set JOIN expressions |
|
|
30
|
+
| [setCap / setBegin](api/setCap.md) | Set pagination (LIMIT / OFFSET) |
|
|
31
|
+
| [addRecord(pRecord)](api/addRecord.md) | Add a record for INSERT or UPDATE |
|
|
32
|
+
| [setIDUser(pIDUser)](api/setIDUser.md) | Set the user ID for audit columns |
|
|
33
|
+
| [Behavior Flags](api/behaviorFlags.md) | Disable auto-identity, timestamps, user stamps, delete tracking |
|
|
34
|
+
|
|
35
|
+
### Query Building
|
|
36
|
+
|
|
37
|
+
| Method | Purpose |
|
|
38
|
+
|--------|---------|
|
|
39
|
+
| [Build Methods](api/buildQuery.md) | buildCreateQuery, buildReadQuery, buildUpdateQuery, buildDeleteQuery, buildUndeleteQuery, buildCountQuery |
|
|
40
|
+
|
|
41
|
+
### Utility
|
|
42
|
+
|
|
43
|
+
| Method | Purpose |
|
|
44
|
+
|--------|---------|
|
|
45
|
+
| [clone / resetParameters / mergeParameters](api/clone.md) | Copy, reset, or merge query state |
|
|
46
|
+
| [Query Overrides](api/queryOverrides.md) | Custom SQL templates for Read and Count |
|
|
47
|
+
|
|
48
|
+
## Properties
|
|
49
|
+
|
|
50
|
+
| Property | Type | Access | Description |
|
|
51
|
+
|----------|------|--------|-------------|
|
|
52
|
+
| `query` | Object | get/set | The generated query (`body`, `schema`, `records`, `parameters`) |
|
|
53
|
+
| `result` | Object | get/set | Execution results (`executed`, `value`, `error`) |
|
|
54
|
+
| `parameters` | Object | get/set | Full parameters state |
|
|
55
|
+
| `dialect` | Object | get | Current dialect object |
|
|
56
|
+
| `uuid` | String | get | Unique identifier for this query |
|
|
57
|
+
| `logLevel` | Integer | get | Current log level |
|
|
58
|
+
| `indexHints` | Array | get/set | Index hints for the database engine |
|
|
59
|
+
|
|
60
|
+
## Chaining
|
|
61
|
+
|
|
62
|
+
All configuration methods return `this`, enabling fluent composition:
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
tmpQuery
|
|
66
|
+
.setScope('Books')
|
|
67
|
+
.setDataElements(['Title', 'Author'])
|
|
68
|
+
.addFilter('Genre', 'Fantasy')
|
|
69
|
+
.addSort('Title')
|
|
70
|
+
.setCap(25)
|
|
71
|
+
.setDialect('PostgreSQL')
|
|
72
|
+
.buildReadQuery();
|
|
73
|
+
```
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# addFilter / setFilter
|
|
2
|
+
|
|
3
|
+
Add or replace filter conditions for the WHERE clause.
|
|
4
|
+
|
|
5
|
+
## addFilter
|
|
6
|
+
|
|
7
|
+
Add a single filter expression to the existing filter array.
|
|
8
|
+
|
|
9
|
+
### Signature
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
tmpQuery.addFilter(pColumn, pValue, pOperator, pConnector, pParameter)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Parameters
|
|
16
|
+
|
|
17
|
+
| Parameter | Type | Default | Description |
|
|
18
|
+
|-----------|------|---------|-------------|
|
|
19
|
+
| `pColumn` | String | *(required)* | Column name |
|
|
20
|
+
| `pValue` | Any | *(required)* | Value to compare against |
|
|
21
|
+
| `pOperator` | String | `'='` | Comparison operator |
|
|
22
|
+
| `pConnector` | String | `'AND'` | Logical connector (`AND`, `OR`, `NONE`) |
|
|
23
|
+
| `pParameter` | String | `pColumn` | Parameter name for bound values |
|
|
24
|
+
|
|
25
|
+
### Returns
|
|
26
|
+
|
|
27
|
+
Returns `this` for chaining.
|
|
28
|
+
|
|
29
|
+
### Examples
|
|
30
|
+
|
|
31
|
+
#### Simple equality filter
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
tmpQuery.addFilter('Genre', 'Science Fiction');
|
|
35
|
+
// WHERE Genre = :Genre_w0
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
#### Greater than
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
tmpQuery.addFilter('PublishedYear', 2000, '>');
|
|
42
|
+
// WHERE PublishedYear > :PublishedYear_w0
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
#### OR connector
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
tmpQuery
|
|
49
|
+
.addFilter('Genre', 'Science Fiction')
|
|
50
|
+
.addFilter('Genre', 'Fantasy', '=', 'OR');
|
|
51
|
+
// WHERE Genre = :Genre_w0 OR Genre = :Genre_w1
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### LIKE operator
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
tmpQuery.addFilter('Title', '%Dune%', 'LIKE');
|
|
58
|
+
// WHERE Title LIKE :Title_w0
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
#### IN operator
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
tmpQuery.addFilter('IDOffice', [10, 11, 15, 18], 'IN');
|
|
65
|
+
// WHERE IDOffice IN ( :IDOffice_w0 )
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### NOT IN operator
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
tmpQuery.addFilter('Status', ['Deleted', 'Archived'], 'NOT IN');
|
|
72
|
+
// WHERE Status NOT IN ( :Status_w0 )
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### IS NULL / IS NOT NULL
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
tmpQuery.addFilter('DeleteDate', '', 'IS NULL');
|
|
79
|
+
// WHERE DeleteDate IS NULL
|
|
80
|
+
|
|
81
|
+
tmpQuery.addFilter('Title', '', 'IS NOT NULL');
|
|
82
|
+
// WHERE Title IS NOT NULL
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### Grouped conditions with parentheses
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
tmpQuery
|
|
89
|
+
.addFilter('', '', '(')
|
|
90
|
+
.addFilter('Genre', 'Science Fiction')
|
|
91
|
+
.addFilter('Genre', 'Fantasy', '=', 'OR')
|
|
92
|
+
.addFilter('', '', ')')
|
|
93
|
+
.addFilter('PublishedYear', 2000, '>');
|
|
94
|
+
// WHERE ( Genre = :Genre_w1 OR Genre = :Genre_w2 )
|
|
95
|
+
// AND PublishedYear > :PublishedYear_w4
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### Custom parameter name
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
tmpQuery.addFilter('Books.Genre', 'Fantasy', '=', 'AND', 'BookGenre');
|
|
102
|
+
// WHERE Books.Genre = :BookGenre_w0
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## setFilter
|
|
106
|
+
|
|
107
|
+
Replace the entire filter array.
|
|
108
|
+
|
|
109
|
+
### Signature
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
tmpQuery.setFilter(pFilter)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Parameters
|
|
116
|
+
|
|
117
|
+
| Parameter | Type | Description |
|
|
118
|
+
|-----------|------|-------------|
|
|
119
|
+
| `pFilter` | Object or Array | Filter expression(s) |
|
|
120
|
+
|
|
121
|
+
### Examples
|
|
122
|
+
|
|
123
|
+
#### Single filter object
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
tmpQuery.setFilter({
|
|
127
|
+
Column: 'Genre',
|
|
128
|
+
Operator: '=',
|
|
129
|
+
Value: 'Fantasy',
|
|
130
|
+
Connector: 'AND',
|
|
131
|
+
Parameter: 'Genre'
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### Multiple filters
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
tmpQuery.setFilter([
|
|
139
|
+
{Column: 'Genre', Operator: '=', Value: 'Fantasy', Connector: 'AND', Parameter: 'Genre'},
|
|
140
|
+
{Column: 'PublishedYear', Operator: '>', Value: 2000, Connector: 'AND', Parameter: 'PublishedYear'}
|
|
141
|
+
]);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Filter Object Structure
|
|
145
|
+
|
|
146
|
+
| Property | Type | Description |
|
|
147
|
+
|----------|------|-------------|
|
|
148
|
+
| `Column` | String | Column name to filter on |
|
|
149
|
+
| `Operator` | String | Comparison operator |
|
|
150
|
+
| `Value` | Any | Value to compare against |
|
|
151
|
+
| `Connector` | String | Logical connector: `AND`, `OR`, or `NONE` |
|
|
152
|
+
| `Parameter` | String | Named parameter key |
|
|
153
|
+
|
|
154
|
+
## Supported Operators
|
|
155
|
+
|
|
156
|
+
| Operator | SQL | Description |
|
|
157
|
+
|----------|-----|-------------|
|
|
158
|
+
| `'='` | `=` | Equals (default) |
|
|
159
|
+
| `'!='` | `!=` | Not equals |
|
|
160
|
+
| `'>'` | `>` | Greater than |
|
|
161
|
+
| `'>='` | `>=` | Greater than or equal |
|
|
162
|
+
| `'<'` | `<` | Less than |
|
|
163
|
+
| `'<='` | `<=` | Less than or equal |
|
|
164
|
+
| `'LIKE'` | `LIKE` | Pattern match |
|
|
165
|
+
| `'IN'` | `IN (...)` | Value in set |
|
|
166
|
+
| `'NOT IN'` | `NOT IN (...)` | Value not in set |
|
|
167
|
+
| `'IS NULL'` | `IS NULL` | Null check |
|
|
168
|
+
| `'IS NOT NULL'` | `IS NOT NULL` | Not-null check |
|
|
169
|
+
| `'('` | `(` | Open group |
|
|
170
|
+
| `')'` | `)` | Close group |
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# addJoin / setJoin
|
|
2
|
+
|
|
3
|
+
Add or replace JOIN expressions for multi-table queries.
|
|
4
|
+
|
|
5
|
+
## addJoin
|
|
6
|
+
|
|
7
|
+
Add a single join expression.
|
|
8
|
+
|
|
9
|
+
### Signature
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
tmpQuery.addJoin(pTable, pFrom, pTo, pType)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Parameters
|
|
16
|
+
|
|
17
|
+
| Parameter | Type | Default | Description |
|
|
18
|
+
|-----------|------|---------|-------------|
|
|
19
|
+
| `pTable` | String | *(required)* | Table to join |
|
|
20
|
+
| `pFrom` | String | *(required)* | Column on the join table (must start with `pTable`) |
|
|
21
|
+
| `pTo` | String | *(required)* | Column on the base table (must include a `.`) |
|
|
22
|
+
| `pType` | String | `'INNER JOIN'` | Join type |
|
|
23
|
+
|
|
24
|
+
### Returns
|
|
25
|
+
|
|
26
|
+
Returns `this` for chaining.
|
|
27
|
+
|
|
28
|
+
### Examples
|
|
29
|
+
|
|
30
|
+
#### Inner join (default)
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
tmpQuery
|
|
34
|
+
.setScope('Books')
|
|
35
|
+
.addJoin('Authors', 'Authors.IDAuthor', 'Books.IDAuthor')
|
|
36
|
+
.setDialect('MySQL')
|
|
37
|
+
.buildReadQuery();
|
|
38
|
+
|
|
39
|
+
// => SELECT `Books`.* FROM `Books`
|
|
40
|
+
// INNER JOIN Authors ON Authors.IDAuthor = Books.IDAuthor;
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
#### Left join
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
tmpQuery.addJoin('Authors', 'Authors.IDAuthor', 'Books.IDAuthor', 'LEFT JOIN');
|
|
47
|
+
|
|
48
|
+
// => ... LEFT JOIN Authors ON Authors.IDAuthor = Books.IDAuthor ...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Right join
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
tmpQuery.addJoin('Reviews', 'Reviews.IDBook', 'Books.IDBook', 'RIGHT JOIN');
|
|
55
|
+
|
|
56
|
+
// => ... RIGHT JOIN Reviews ON Reviews.IDBook = Books.IDBook ...
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
#### Multiple joins
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
tmpQuery
|
|
63
|
+
.setScope('Books')
|
|
64
|
+
.addJoin('Authors', 'Authors.IDAuthor', 'Books.IDAuthor')
|
|
65
|
+
.addJoin('Publishers', 'Publishers.IDPublisher', 'Books.IDPublisher', 'LEFT JOIN')
|
|
66
|
+
.setDialect('MySQL')
|
|
67
|
+
.buildReadQuery();
|
|
68
|
+
|
|
69
|
+
// => SELECT `Books`.* FROM `Books`
|
|
70
|
+
// INNER JOIN Authors ON Authors.IDAuthor = Books.IDAuthor
|
|
71
|
+
// LEFT JOIN Publishers ON Publishers.IDPublisher = Books.IDPublisher;
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## setJoin
|
|
75
|
+
|
|
76
|
+
Replace the entire join array.
|
|
77
|
+
|
|
78
|
+
### Signature
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
tmpQuery.setJoin(pJoin)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Parameters
|
|
85
|
+
|
|
86
|
+
| Parameter | Type | Description |
|
|
87
|
+
|-----------|------|-------------|
|
|
88
|
+
| `pJoin` | Object or Array | Join expression(s) |
|
|
89
|
+
|
|
90
|
+
### Examples
|
|
91
|
+
|
|
92
|
+
#### Single join object
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
tmpQuery.setJoin({
|
|
96
|
+
Type: 'INNER JOIN',
|
|
97
|
+
Table: 'Authors',
|
|
98
|
+
From: 'Authors.IDAuthor',
|
|
99
|
+
To: 'Books.IDAuthor'
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### Array of joins
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
tmpQuery.setJoin([
|
|
107
|
+
{Type: 'INNER JOIN', Table: 'Authors', From: 'Authors.IDAuthor', To: 'Books.IDAuthor'},
|
|
108
|
+
{Type: 'LEFT JOIN', Table: 'Publishers', From: 'Publishers.IDPublisher', To: 'Books.IDPublisher'}
|
|
109
|
+
]);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Join Object Structure
|
|
113
|
+
|
|
114
|
+
| Property | Type | Description |
|
|
115
|
+
|----------|------|-------------|
|
|
116
|
+
| `Type` | String | Join type (e.g. `'INNER JOIN'`, `'LEFT JOIN'`, `'RIGHT JOIN'`) |
|
|
117
|
+
| `Table` | String | Table to join |
|
|
118
|
+
| `From` | String | Column on the join table |
|
|
119
|
+
| `To` | String | Column on the base or another table |
|
|
120
|
+
|
|
121
|
+
## Validation
|
|
122
|
+
|
|
123
|
+
FoxHound validates join parameters:
|
|
124
|
+
|
|
125
|
+
- `pTable` must be a string
|
|
126
|
+
- `pFrom` must start with the join table name
|
|
127
|
+
- `pTo` must include a dot (table-qualified column name)
|
|
128
|
+
- Invalid joins are logged as warnings and silently skipped
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# addRecord
|
|
2
|
+
|
|
3
|
+
Add a record for CREATE or UPDATE operations.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
tmpQuery.addRecord(pRecord)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Parameters
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Required | Description |
|
|
14
|
+
|-----------|------|----------|-------------|
|
|
15
|
+
| `pRecord` | Object | Yes | Key-value pairs of column names and values |
|
|
16
|
+
|
|
17
|
+
## Returns
|
|
18
|
+
|
|
19
|
+
Returns `this` for chaining.
|
|
20
|
+
|
|
21
|
+
## Description
|
|
22
|
+
|
|
23
|
+
Adds a record object to the query's record list. The keys become column names and the values become parameterized values in the generated SQL.
|
|
24
|
+
|
|
25
|
+
For CREATE operations, the record defines the column-value pairs for the INSERT statement. For UPDATE operations, the record defines the SET clause (which columns to change and their new values).
|
|
26
|
+
|
|
27
|
+
Only the first record in the list is used for query generation in the current implementation.
|
|
28
|
+
|
|
29
|
+
Non-object values are silently ignored.
|
|
30
|
+
|
|
31
|
+
## Examples
|
|
32
|
+
|
|
33
|
+
### Insert a new record
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
tmpQuery
|
|
37
|
+
.setScope('Books')
|
|
38
|
+
.addRecord({Title: 'Dune', Author: 'Frank Herbert', PublishedYear: 1965})
|
|
39
|
+
.setDialect('MySQL')
|
|
40
|
+
.buildCreateQuery();
|
|
41
|
+
|
|
42
|
+
console.log(tmpQuery.query.body);
|
|
43
|
+
// => INSERT INTO `Books` ( Title, Author, PublishedYear)
|
|
44
|
+
// VALUES ( :Title_0, :Author_1, :PublishedYear_2);
|
|
45
|
+
|
|
46
|
+
console.log(tmpQuery.query.parameters);
|
|
47
|
+
// => { Title_0: 'Dune', Author_1: 'Frank Herbert', PublishedYear_2: 1965 }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Update a record
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
tmpQuery
|
|
54
|
+
.setScope('Books')
|
|
55
|
+
.addFilter('IDBook', 42)
|
|
56
|
+
.addRecord({Title: 'Dune Messiah', PublishedYear: 1969})
|
|
57
|
+
.setDialect('MySQL')
|
|
58
|
+
.buildUpdateQuery();
|
|
59
|
+
|
|
60
|
+
console.log(tmpQuery.query.body);
|
|
61
|
+
// => UPDATE `Books` SET Title = :Title_0, PublishedYear = :PublishedYear_1
|
|
62
|
+
// WHERE IDBook = :IDBook_w0;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### With schema for automatic column management
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
tmpQuery
|
|
69
|
+
.setScope('Books')
|
|
70
|
+
.addRecord({
|
|
71
|
+
IDBook: null,
|
|
72
|
+
Title: 'Neuromancer',
|
|
73
|
+
Author: 'William Gibson',
|
|
74
|
+
CreateDate: null,
|
|
75
|
+
CreatingIDUser: null,
|
|
76
|
+
UpdateDate: null,
|
|
77
|
+
UpdatingIDUser: null,
|
|
78
|
+
Deleted: 0
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
tmpQuery.query.schema = [
|
|
82
|
+
{Column: 'IDBook', Type: 'AutoIdentity'},
|
|
83
|
+
{Column: 'Title', Type: 'String'},
|
|
84
|
+
{Column: 'Author', Type: 'String'},
|
|
85
|
+
{Column: 'CreateDate', Type: 'CreateDate'},
|
|
86
|
+
{Column: 'CreatingIDUser', Type: 'CreateIDUser'},
|
|
87
|
+
{Column: 'UpdateDate', Type: 'UpdateDate'},
|
|
88
|
+
{Column: 'UpdatingIDUser', Type: 'UpdateIDUser'},
|
|
89
|
+
{Column: 'Deleted', Type: 'Deleted'}
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
tmpQuery.query.IDUser = 5;
|
|
93
|
+
tmpQuery.setDialect('MySQL').buildCreateQuery();
|
|
94
|
+
|
|
95
|
+
// IDBook => NULL (AutoIdentity)
|
|
96
|
+
// CreateDate => NOW(3) (auto timestamp)
|
|
97
|
+
// CreatingIDUser => 5 (auto user stamp)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Empty record
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
// No-op: buildCreateQuery() and buildUpdateQuery() return false
|
|
104
|
+
tmpQuery.addRecord({});
|
|
105
|
+
tmpQuery.buildCreateQuery();
|
|
106
|
+
console.log(tmpQuery.query.body);
|
|
107
|
+
// => false
|
|
108
|
+
```
|