json-fs-query 1.1.0 → 2.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/LICENSE +674 -674
- package/README.md +326 -98
- package/browser/jsonFsQuery.js +12819 -10934
- package/browser/jsonFsQuery.min.js +1 -1
- package/dist/data/introspect/create.d.ts +1 -1
- package/dist/data/introspect/create.js +35 -4
- package/dist/data/introspect/create.min.js +1 -1
- package/dist/data/introspect/definitions/field.d.ts +6 -2
- package/dist/data/introspect/definitions/field.js +7 -1
- package/dist/data/introspect/definitions/field.min.js +1 -1
- package/dist/data/introspect/index.d.ts +6 -3
- package/dist/data/introspect/index.js +10 -19
- package/dist/data/introspect/index.min.js +1 -1
- package/dist/data/introspect/policies/foreignKeyExists.js +28 -28
- package/dist/data/introspect/policies/hasDependents.js +53 -53
- package/dist/data/introspect/policies/keyUnique.js +34 -34
- package/dist/data/introspect/policies/typeCheck.d.ts +9 -1
- package/dist/data/introspect/policies/typeCheck.js +20 -2
- package/dist/data/introspect/policies/typeCheck.min.js +1 -1
- package/dist/data/queries/createRecord.d.ts +11 -0
- package/dist/data/queries/createRecord.js +58 -0
- package/dist/data/queries/createRecord.min.js +1 -0
- package/dist/data/queries/deleteEntity.js +100 -100
- package/dist/data/queries/describeRecord.d.ts +9 -0
- package/dist/data/queries/describeRecord.js +29 -0
- package/dist/data/queries/describeRecord.min.js +1 -0
- package/dist/data/queries/dropRecord.d.ts +9 -0
- package/dist/data/queries/dropRecord.js +61 -0
- package/dist/data/queries/dropRecord.min.js +1 -0
- package/dist/data/queries/insertEntity.js +122 -117
- package/dist/data/queries/insertEntity.min.js +1 -1
- package/dist/data/queries/parser.d.ts +1 -1
- package/dist/data/queries/parser.js +7 -15
- package/dist/data/queries/parser.min.js +1 -1
- package/dist/data/queries/query.d.ts +6 -0
- package/dist/data/queries/query.js +15 -6
- package/dist/data/queries/query.min.js +1 -1
- package/dist/data/queries/queryStatus.d.ts +15 -0
- package/dist/data/queries/queryStatus.js +52 -0
- package/dist/data/queries/queryStatus.min.js +1 -0
- package/dist/data/queries/quote.d.ts +11 -0
- package/dist/data/queries/quote.js +17 -0
- package/dist/data/queries/quote.min.js +1 -0
- package/dist/data/queries/showRecords.d.ts +6 -0
- package/dist/data/queries/showRecords.js +21 -0
- package/dist/data/queries/showRecords.min.js +1 -0
- package/dist/data/queries/sortBy.js +40 -27
- package/dist/data/queries/sortBy.min.js +1 -1
- package/dist/data/queries/updateEntity.js +245 -193
- package/dist/data/queries/updateEntity.min.js +1 -1
- package/dist/data/queries/where.d.ts +7 -7
- package/dist/data/queries/where.js +85 -67
- package/dist/data/queries/where.min.js +1 -1
- package/dist/data/utilities/compareAs.d.ts +19 -19
- package/dist/data/utilities/compareAs.js +57 -57
- package/dist/data/utilities/config.d.ts +15 -0
- package/dist/data/utilities/config.js +31 -1
- package/dist/data/utilities/config.min.js +1 -1
- package/dist/data/utilities/databaseLock.d.ts +16 -0
- package/dist/data/utilities/databaseLock.js +63 -0
- package/dist/data/utilities/databaseLock.min.js +1 -0
- package/dist/data/utilities/fieldType.d.ts +15 -15
- package/dist/data/utilities/fieldType.js +22 -22
- package/dist/data/utilities/getConditionIndexedList.js +47 -47
- package/dist/data/utilities/matchIndexedJoins.js +62 -62
- package/dist/data/utilities/mergeJoin.js +47 -47
- package/dist/data/utilities/parsers/findConditions.d.ts +19 -15
- package/dist/data/utilities/parsers/findConditions.js +34 -8
- package/dist/data/utilities/parsers/findConditions.min.js +1 -1
- package/dist/data/utilities/parsers/findInsertValues.js +3 -2
- package/dist/data/utilities/parsers/findInsertValues.min.js +1 -1
- package/dist/data/utilities/parsers/findJoinEntity.js +3 -1
- package/dist/data/utilities/parsers/findJoinEntity.min.js +1 -1
- package/dist/data/utilities/parsers/findSelects.js +6 -3
- package/dist/data/utilities/parsers/findSelects.min.js +1 -1
- package/dist/data/utilities/parsers/findSortClauses.js +5 -2
- package/dist/data/utilities/parsers/findSortClauses.min.js +1 -1
- package/dist/data/utilities/parsers/findUpdateValues.js +3 -2
- package/dist/data/utilities/parsers/findUpdateValues.min.js +1 -1
- package/dist/data/utilities/parsers/maskStrings.d.ts +39 -0
- package/dist/data/utilities/parsers/maskStrings.js +127 -0
- package/dist/data/utilities/parsers/maskStrings.min.js +1 -0
- package/dist/data/utilities/parsers/parseCreate.d.ts +20 -0
- package/dist/data/utilities/parsers/parseCreate.js +332 -0
- package/dist/data/utilities/parsers/parseCreate.min.js +1 -0
- package/dist/data/utilities/parsers/parseLiteralValue.d.ts +18 -1
- package/dist/data/utilities/parsers/parseLiteralValue.js +34 -5
- package/dist/data/utilities/parsers/parseLiteralValue.min.js +1 -1
- package/dist/data/utilities/parsers/parseQuery.d.ts +3 -0
- package/dist/data/utilities/parsers/parseQuery.js +28 -2
- package/dist/data/utilities/parsers/parseQuery.min.js +1 -1
- package/dist/data/utilities/parsers/parseSchemaStatement.d.ts +13 -0
- package/dist/data/utilities/parsers/parseSchemaStatement.js +82 -0
- package/dist/data/utilities/parsers/parseSchemaStatement.min.js +1 -0
- package/dist/data/utilities/parsers/scanQuery.d.ts +52 -0
- package/dist/data/utilities/parsers/scanQuery.js +158 -0
- package/dist/data/utilities/parsers/scanQuery.min.js +1 -0
- package/dist/data/utilities/parsers/splitQueries.d.ts +6 -0
- package/dist/data/utilities/parsers/splitQueries.js +20 -2
- package/dist/data/utilities/parsers/splitQueries.min.js +1 -1
- package/dist/data/utilities/parsers/startsQuery.d.ts +5 -0
- package/dist/data/utilities/parsers/startsQuery.js +10 -0
- package/dist/data/utilities/parsers/startsQuery.min.js +1 -0
- package/dist/data/utilities/recordExists.d.ts +6 -0
- package/dist/data/utilities/recordExists.js +18 -0
- package/dist/data/utilities/recordExists.min.js +1 -0
- package/dist/data/utilities/removeFromIndex.d.ts +14 -14
- package/dist/data/utilities/removeFromIndex.js +45 -45
- package/dist/data/utilities/runQuery.js +135 -112
- package/dist/data/utilities/runQuery.min.js +1 -1
- package/dist/data/utilities/updateIndex.d.ts +17 -17
- package/dist/data/utilities/updateIndex.js +57 -57
- package/dist/data/utilities/useJoinClause.js +56 -56
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -0
- package/dist/index.min.js +1 -1
- package/package.json +68 -60
package/README.md
CHANGED
|
@@ -1,98 +1,326 @@
|
|
|
1
|
-
# json-fs-query
|
|
2
|
-
|
|
3
|
-
Read and manage data with JSON files - a small query engine that treats a folder of JSON files as a database, with
|
|
4
|
-
a SQL-like query string (`read workouts where date = '2024-06-02'`) instead of a query builder API.
|
|
5
|
-
|
|
6
|
-
The core engine (schema management, a full read/insert/update/delete query language, single- and multi-field key
|
|
7
|
-
support, and referential integrity) is implemented and tested. Still open, tracked for whenever they're actually
|
|
8
|
-
needed: multi-field *foreign* keys specifically (single-field ones work; composite primary/unique/index keys work
|
|
9
|
-
too), and CASCADE/SET NULL modes for referential integrity (deleting/updating a referenced record is RESTRICTed -
|
|
10
|
-
blocked outright - rather than cascading or nulling out the dependents; see `configure()` below to turn that
|
|
11
|
-
enforcement off entirely if it doesn't fit your workflow).
|
|
12
|
-
|
|
13
|
-
## Install
|
|
14
|
-
|
|
15
|
-
```shell
|
|
16
|
-
npm install json-fs-query
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Usage
|
|
20
|
-
|
|
21
|
-
```js
|
|
22
|
-
const { create, query, drop } = require('json-fs-query')
|
|
23
|
-
|
|
24
|
-
await create('workouts', [
|
|
25
|
-
{ name: '_id', type: 'string', optional: false, autoGenerate: true },
|
|
26
|
-
{ name: 'reps', type: 'int', optional: false },
|
|
27
|
-
{ name: 'date', type: 'string', optional: false }
|
|
28
|
-
], [
|
|
29
|
-
{ type: 'primary', fields: ['_id'], lookup: 'pk__id.json' }
|
|
30
|
-
])
|
|
31
|
-
|
|
32
|
-
await query("insert workouts values reps = 10, date = '2024-06-02'")
|
|
33
|
-
const rows = await query("read workouts where date = '2024-06-02'")
|
|
34
|
-
await query("update workouts set reps = 12 where date = '2024-06-02'")
|
|
35
|
-
await query("delete workouts where date = '2024-06-02'")
|
|
36
|
-
|
|
37
|
-
await drop('workouts')
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
`
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
1
|
+
# json-fs-query
|
|
2
|
+
|
|
3
|
+
Read and manage data with JSON files - a small query engine that treats a folder of JSON files as a database, with
|
|
4
|
+
a SQL-like query string (`read workouts where date = '2024-06-02'`) instead of a query builder API.
|
|
5
|
+
|
|
6
|
+
The core engine (schema management, a full read/insert/update/delete query language, single- and multi-field key
|
|
7
|
+
support, and referential integrity) is implemented and tested. Still open, tracked for whenever they're actually
|
|
8
|
+
needed: multi-field *foreign* keys specifically (single-field ones work; composite primary/unique/index keys work
|
|
9
|
+
too), and CASCADE/SET NULL modes for referential integrity (deleting/updating a referenced record is RESTRICTed -
|
|
10
|
+
blocked outright - rather than cascading or nulling out the dependents; see `configure()` below to turn that
|
|
11
|
+
enforcement off entirely if it doesn't fit your workflow).
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```shell
|
|
16
|
+
npm install json-fs-query
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
const { create, query, drop } = require('json-fs-query')
|
|
23
|
+
|
|
24
|
+
await create('workouts', [
|
|
25
|
+
{ name: '_id', type: 'string', optional: false, autoGenerate: true },
|
|
26
|
+
{ name: 'reps', type: 'int', optional: false },
|
|
27
|
+
{ name: 'date', type: 'string', optional: false }
|
|
28
|
+
], [
|
|
29
|
+
{ type: 'primary', fields: ['_id'], lookup: 'pk__id.json' }
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
await query("insert workouts values reps = 10, date = '2024-06-02'")
|
|
33
|
+
const rows = await query("read workouts where date = '2024-06-02'")
|
|
34
|
+
await query("update workouts set reps = 12 where date = '2024-06-02'")
|
|
35
|
+
await query("delete workouts where date = '2024-06-02'")
|
|
36
|
+
|
|
37
|
+
await drop('workouts')
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Fields: `optional`, `nullable` and defaults
|
|
41
|
+
|
|
42
|
+
A field is required and can't be `null` unless its definition says otherwise. The two flags are separate, because
|
|
43
|
+
JSON can leave a key out or store it as `null`:
|
|
44
|
+
|
|
45
|
+
| Definition | Left out | `null` |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `{ name: 'note', type: 'string' }` | error | error |
|
|
48
|
+
| `{ name: 'note', type: 'string', optional: true }` | allowed | error |
|
|
49
|
+
| `{ name: 'note', type: 'string', nullable: true }` | error | stored as `null` |
|
|
50
|
+
| `{ name: 'note', type: 'string', optional: true, nullable: true }` | allowed | stored as `null` |
|
|
51
|
+
|
|
52
|
+
`useDefault: true, defaultValue: …` fills in a field that is left out. `create()` checks the default against the
|
|
53
|
+
field's type (a `null` default needs `nullable: true`) and throws if it doesn't fit. Primary key fields can't be
|
|
54
|
+
nullable. As in SQL, a `null` is never a duplicate in a unique key (any number of rows can hold it), and
|
|
55
|
+
a `null` foreign key isn't checked against the record it references.
|
|
56
|
+
|
|
57
|
+
## Writing queries
|
|
58
|
+
|
|
59
|
+
`query()` takes one query, or several separated by `;`, and resolves to one result per query:
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
const [workouts, exercises] = await query("read workouts where date = '2024-06-02'; read exercises;")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The `;` after the last (or only) query is optional. A `;` inside a string, a comment or brackets doesn't end a query.
|
|
66
|
+
|
|
67
|
+
### Strings
|
|
68
|
+
|
|
69
|
+
Strings use single or double quotes. A string closes only with the quote it opened with, so the other quote is just
|
|
70
|
+
a character inside it:
|
|
71
|
+
|
|
72
|
+
```sql
|
|
73
|
+
insert notes values text = "it's done"
|
|
74
|
+
insert notes values text = 'say "hi"'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Inside a string, a backslash escapes the next character: `\'`, `\"` and `\\` are a quote and a backslash, `\n` and
|
|
78
|
+
`\t` are a newline and a tab, and any other `\x` is just `x`. Escapes are read left to right, so `'\\\''` is a
|
|
79
|
+
backslash followed by a quote:
|
|
80
|
+
|
|
81
|
+
```sql
|
|
82
|
+
insert notes values text = 'it\'s done'
|
|
83
|
+
insert files values path = 'C:\\temp'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
An unclosed string, or mismatched quotes (`'abc"`), is an error rather than a guess. Keywords, commas and `;` inside a
|
|
87
|
+
string are only text: `where note = 'x sort b limit 1'` compares against that whole text.
|
|
88
|
+
|
|
89
|
+
### Building a query from values: `quote()`
|
|
90
|
+
|
|
91
|
+
To put any text into a query (such as user input), wrap it with `quote()`. It escapes the backslash and the quote, so
|
|
92
|
+
the query reads back exactly the text given:
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
const { query, quote } = require('json-fs-query')
|
|
96
|
+
|
|
97
|
+
const name = "Farmer's walk"
|
|
98
|
+
await query(`insert exercises values name = ${quote(name)}`)
|
|
99
|
+
await query(`read exercises where name = ${quote(name)}`)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`quote(value, '"')` wraps in double quotes instead.
|
|
103
|
+
|
|
104
|
+
### Numbers
|
|
105
|
+
|
|
106
|
+
A number may be negative, have a fraction and an exponent: `12`, `-3`, `1.5`, `2.5e-4`. They work the same in
|
|
107
|
+
`values`, `set`, `where`, arrays and defaults:
|
|
108
|
+
|
|
109
|
+
```sql
|
|
110
|
+
read workouts where reps > -1
|
|
111
|
+
update workouts set weight = 102.5 where _id = 4
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
A whole number past 9007199254740991 (`Number.MAX_SAFE_INTEGER`), however it is written (`1e20`,
|
|
115
|
+
`9007199254740993.0`), is an error rather than being stored rounded: quote it to keep it as text. A fraction is stored as the nearest double, as in JSON.
|
|
116
|
+
|
|
117
|
+
### Arrays
|
|
118
|
+
|
|
119
|
+
Arrays are JSON-style, and their strings can use either quote. They can be nested, and are used by `in` and
|
|
120
|
+
`between`, or stored as a value:
|
|
121
|
+
|
|
122
|
+
```sql
|
|
123
|
+
read workouts where exercise_id in [3, 5, 8]
|
|
124
|
+
read workouts where reps between [8, 12]
|
|
125
|
+
read exercises where name in ['bench press', "farmer's walk"]
|
|
126
|
+
insert routines values days = ['mon', 'thu'], sets = [[3, 10], [3, 8]]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Comments
|
|
130
|
+
|
|
131
|
+
`-- to the end of the line` and `/* anything, over several lines */`. A `--` starts a comment only at the start of a
|
|
132
|
+
line or after whitespace or a `;`, since names can contain `-` (`my--table` is a name). Comment markers inside a
|
|
133
|
+
string are just text.
|
|
134
|
+
|
|
135
|
+
```sql
|
|
136
|
+
-- this week's sessions
|
|
137
|
+
read workouts where date = '2024-06-02' /* the Sunday */;
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Several conditions
|
|
141
|
+
|
|
142
|
+
Each condition starts with `where`; join them with `and where`:
|
|
143
|
+
|
|
144
|
+
```sql
|
|
145
|
+
read workouts where reps > 8 and where date = '2024-06-02'
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`or where` is not supported yet, and is an error (in 1.x it was silently read as `and where`). And/or with grouping
|
|
149
|
+
and precedence is planned.
|
|
150
|
+
|
|
151
|
+
### Null and missing fields
|
|
152
|
+
|
|
153
|
+
A field can be missing (left out) or hold `null`, and both are their own cases, as in SQL: they match no
|
|
154
|
+
comparison (`=`, `!=`, `<`, `between`, `in`, `like`...), and a `null` never equals anything, not even another
|
|
155
|
+
`null`. These test for them instead:
|
|
156
|
+
|
|
157
|
+
| Condition | Matches when the field |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `where note is null` | is there and holds `null` |
|
|
160
|
+
| `where note is not null` | is there and holds a value |
|
|
161
|
+
| `where note exists` | is there (`null` counts) |
|
|
162
|
+
| `where note does not exist` | is missing |
|
|
163
|
+
|
|
164
|
+
`where note = null` is an error that points to `is null`, since it could never match. Sorting puts missing first,
|
|
165
|
+
then `null`, then the values (`asc`; `desc` is the reverse).
|
|
166
|
+
|
|
167
|
+
In 2.0 and earlier, a `null` acted as `0` in comparisons and sorting (`where n < 5` matched it), and `= null` matched it.
|
|
168
|
+
|
|
169
|
+
### Creating a record: `create`
|
|
170
|
+
|
|
171
|
+
The query form of `create()`. Inside the brackets: the fields, then the keys, separated by commas. Everything is
|
|
172
|
+
case-insensitive except names.
|
|
173
|
+
|
|
174
|
+
```sql
|
|
175
|
+
create if not exists exercise_muscle (
|
|
176
|
+
_id string auto,
|
|
177
|
+
exercise_id string,
|
|
178
|
+
muscle_id string,
|
|
179
|
+
muscle_rank int,
|
|
180
|
+
note string optional default '',
|
|
181
|
+
replaced_by string null default null,
|
|
182
|
+
primary key (_id),
|
|
183
|
+
index (muscle_id, muscle_rank),
|
|
184
|
+
foreign key (exercise_id) references exercises (_id),
|
|
185
|
+
foreign key (muscle_id) references muscles (_id)
|
|
186
|
+
);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
A field is `<name> <type>`, then any of these, in any order:
|
|
190
|
+
|
|
191
|
+
| Modifier | Means |
|
|
192
|
+
|---|---|
|
|
193
|
+
| `optional` | may be left out (see "Fields" above) |
|
|
194
|
+
| `null` / `not null` | may hold `null` / may not (the default) |
|
|
195
|
+
| `auto` | a UUID is generated on insert; the type must be `string` |
|
|
196
|
+
| `default <value>` | used when the field is left out: a string, a number, an array, `null`, `true` or `false` (so an `object` field can only default to `null` here; `create()` takes any default) |
|
|
197
|
+
|
|
198
|
+
The types are `string`, `int`, `boolean`, `object` and `array`; any other is an error.
|
|
199
|
+
|
|
200
|
+
| Key | Index file |
|
|
201
|
+
|---|---|
|
|
202
|
+
| `primary key (a)` | `pk_a.json` |
|
|
203
|
+
| `unique (a)` | `unique_a.json` |
|
|
204
|
+
| `index (a)` | `index_a.json` |
|
|
205
|
+
| `foreign key (a) references other (b)` | `fk_a.json` (other's record file: `other.json`) |
|
|
206
|
+
|
|
207
|
+
Several fields join with `_`: `index (a, b)` is `index_a_b.json`. Foreign keys are single-field for now.
|
|
208
|
+
|
|
209
|
+
The result is the new record's definition, as from `create()`. Creating a record that already exists is an error;
|
|
210
|
+
with `if not exists` it is left as it is and the result is `null`. A foreign key's record and field must exist
|
|
211
|
+
already (or be the record being created).
|
|
212
|
+
|
|
213
|
+
### Dropping, describing and listing records: `drop`, `describe`, `show records`
|
|
214
|
+
|
|
215
|
+
```sql
|
|
216
|
+
drop exercise_muscle; -- removes the record, its entries and its indexes
|
|
217
|
+
drop if exists exercise_muscle; -- the same, or nothing if there is no such record
|
|
218
|
+
describe exercises; -- the record's fields and keys
|
|
219
|
+
show records; -- the name of every record, in the order they were created
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
`drop` resolves to the definition the record had, or `null` with `if exists` for a record that doesn't exist; without
|
|
223
|
+
it, that is an error. A record that another record's foreign key references can't be dropped until that one is
|
|
224
|
+
(unless `enforceForeignKeys` is turned off, see Configuration). `describe` resolves to `{ path, definition, keys }`
|
|
225
|
+
(the `describe()` function gives only the fields), and `show records` to an array of names.
|
|
226
|
+
|
|
227
|
+
### Reading a query over several lines: `queryStatus()`
|
|
228
|
+
|
|
229
|
+
For a prompt which reads a query line by line (like the CLI), `queryStatus()` says whether the text so far is ready to
|
|
230
|
+
run, and if not, what it is waiting for: `'` or `"` (a string), `/*` (a comment), `[` or `(` (a bracket), or `;`.
|
|
231
|
+
Join the lines with `'\n'`, since a `--` comment ends at the end of its line.
|
|
232
|
+
|
|
233
|
+
```js
|
|
234
|
+
const { queryStatus } = require('json-fs-query')
|
|
235
|
+
|
|
236
|
+
queryStatus("read notes where text = 'it") // { complete: false, waitingFor: "'" }
|
|
237
|
+
queryStatus('read notes') // { complete: false, waitingFor: ';' }
|
|
238
|
+
queryStatus('read notes;') // { complete: true, waitingFor: '' }
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Upgrading from 1.x
|
|
242
|
+
|
|
243
|
+
- Several queries in one string need a `;` between them: `read a; read b`. Before, they were split on command words,
|
|
244
|
+
which also split a value such as `'please read this'` and stored it as `pleaseread this`.
|
|
245
|
+
- Unclosed strings and mismatched quotes are errors.
|
|
246
|
+
- A backslash inside a string is an escape: write `\\` for a backslash (or use `quote()`).
|
|
247
|
+
- `--` and `/* */` start comments outside strings.
|
|
248
|
+
- `null` is only accepted by fields with `nullable: true`, and is stored as a real `null`. Before, a `string` field
|
|
249
|
+
stored the text `"null"` and an `int` field stored `0`.
|
|
250
|
+
- `create()` throws for a default that doesn't fit its field's type, and for a nullable primary key field.
|
|
251
|
+
- `or where` is an error until or is supported. Before, it was read as `and where`, returning the wrong rows.
|
|
252
|
+
- A select alias needs `as`: `select name as n`. Before, the first field took the next word as its alias, so
|
|
253
|
+
`select name where a = 1` failed and `select name limit 1` lost its limit; `select name n` is now an error.
|
|
254
|
+
- Sort fields need a comma between them: `sort a, b desc`. Before, the list ran on into the next clause, so
|
|
255
|
+
`sort a limit 1` sorted by `a`, `limit` and `1` and lost the limit; `sort a b` is now an error.
|
|
256
|
+
|
|
257
|
+
## Configuration
|
|
258
|
+
|
|
259
|
+
`configure()` sets where this package's data lives and how strictly it enforces foreign keys - call it once,
|
|
260
|
+
before anything else:
|
|
261
|
+
|
|
262
|
+
```js
|
|
263
|
+
const { configure } = require('json-fs-query')
|
|
264
|
+
|
|
265
|
+
configure({
|
|
266
|
+
databasePath: 'database/', // where the JSON files live (default 'database/')
|
|
267
|
+
relativePath: '', // URL prefix prepended to databasePath when reading via fetch() in a browser
|
|
268
|
+
enforceForeignKeys: true // RESTRICT delete/update of a record other entities still reference (default true)
|
|
269
|
+
})
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Turning `enforceForeignKeys` off restores the pre-1.0 behavior (no referential integrity checks at all) - useful if
|
|
273
|
+
it doesn't fit a particular workflow, mirroring MySQL's own toggleable `foreign_key_checks`.
|
|
274
|
+
|
|
275
|
+
## Several calls at once
|
|
276
|
+
|
|
277
|
+
Calls to `query()`, `create()`, `drop()` and `describe()` on one database (one `databasePath`) run one at a time, in
|
|
278
|
+
the order they were made, so concurrent requests (for example in a web server) can't overwrite each other's
|
|
279
|
+
changes. All the queries in one `query()` string run together, with no other call in between. This covers one Node
|
|
280
|
+
process: two processes writing to the same folder (say a server and a CLI) are not protected from each other.
|
|
281
|
+
|
|
282
|
+
`configure()` can't point `databasePath` at another folder while calls are still running or queued (it throws:
|
|
283
|
+
await them first), since each call reads the setting again at every step. The same folder written another way
|
|
284
|
+
(`database/`, `./database`, its absolute path) counts as one database.
|
|
285
|
+
|
|
286
|
+
## In the browser
|
|
287
|
+
|
|
288
|
+
A pre-built bundle is included (`browser/jsonFsQuery.js`, `.min.js` - also served directly via
|
|
289
|
+
[unpkg](https://unpkg.com/json-fs-query) or [jsDelivr](https://cdn.jsdelivr.net/npm/json-fs-query)) and exposes the
|
|
290
|
+
same functions as a global:
|
|
291
|
+
|
|
292
|
+
```html
|
|
293
|
+
<script src="https://unpkg.com/json-fs-query"></script>
|
|
294
|
+
<script>
|
|
295
|
+
jsonFsQuery.configure({ databasePath: 'database/', relativePath: 'https://example.com/' })
|
|
296
|
+
jsonFsQuery.query("read workouts where date = '2024-06-02'").then(console.log)
|
|
297
|
+
</script>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Reads work in the browser via `fetch()` - `create`/`drop`, and any write through `query()` (`insert`/`update`/
|
|
301
|
+
`delete`), need real filesystem access and only work in Node. They're still present in the browser build (same
|
|
302
|
+
public API either way), but throw if actually called there, rather than being silently unavailable.
|
|
303
|
+
|
|
304
|
+
## Documentation
|
|
305
|
+
|
|
306
|
+
The reference for every function is in [`docs/`](https://joshuaheagle.com/projects/json-file-db/docs/index.html) (or open `docs/index.html` locally). It is generated from the TypeScript source, and each module is a folder of `src/data/`:
|
|
307
|
+
|
|
308
|
+
| Module | What it holds |
|
|
309
|
+
| --- | --- |
|
|
310
|
+
| `introspect` | Create, drop and describe a JSON-file "table" (and the policies that validate one) |
|
|
311
|
+
| `queries` | Parse a query string and run it - reads, inserts, updates, deletes, joins, `where`, `sortBy`, `groupBy` |
|
|
312
|
+
| `utilities` | Shared helpers the queries use - retrieving records/files/indexes and merging join results |
|
|
313
|
+
|
|
314
|
+
`src/index.ts` is the package's public entry point - it re-exports `query`, `quote`, `queryStatus`, `create`, `drop`,
|
|
315
|
+
`describe`, `configure`, and the schema-definition types (`fieldProperties`, `keyProperties`, etc.) needed to build a `create()` call. Everything else
|
|
316
|
+
under `src/data/` is an internal module, reachable but not part of the supported public API.
|
|
317
|
+
|
|
318
|
+
## Development
|
|
319
|
+
|
|
320
|
+
```shell
|
|
321
|
+
npm install
|
|
322
|
+
npm test # the tests
|
|
323
|
+
npm run typecheck # the types
|
|
324
|
+
npm run build # dist/ and browser/ (not committed; npm publish builds them)
|
|
325
|
+
npm run docs # the documentation (docs.to)
|
|
326
|
+
```
|