json-fs-query 3.2.0 → 3.3.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 +257 -15
- package/browser/jsonFsQuery.js +2253 -293
- package/browser/jsonFsQuery.min.js +1 -1
- package/dist/data/introspect/alter.d.ts +12 -0
- package/dist/data/introspect/alter.js +304 -0
- package/dist/data/introspect/alter.min.js +1 -0
- package/dist/data/introspect/create.d.ts +10 -0
- package/dist/data/introspect/create.js +2 -1
- package/dist/data/introspect/create.min.js +1 -1
- package/dist/data/introspect/drop.js +2 -0
- package/dist/data/introspect/drop.min.js +1 -1
- package/dist/data/introspect/formatDatabase.d.ts +17 -0
- package/dist/data/introspect/formatDatabase.js +56 -0
- package/dist/data/introspect/formatDatabase.min.js +1 -0
- package/dist/data/introspect/index.d.ts +9 -0
- package/dist/data/introspect/index.js +18 -5
- package/dist/data/introspect/index.min.js +1 -1
- package/dist/data/introspect/packDatabase.d.ts +27 -0
- package/dist/data/introspect/packDatabase.js +113 -0
- package/dist/data/introspect/packDatabase.min.js +1 -0
- package/dist/data/queries/databaseStatements.d.ts +31 -0
- package/dist/data/queries/databaseStatements.js +97 -0
- package/dist/data/queries/databaseStatements.min.js +1 -0
- package/dist/data/queries/deleteEntity.js +6 -1
- package/dist/data/queries/deleteEntity.min.js +1 -1
- package/dist/data/queries/insertEntity.d.ts +8 -0
- package/dist/data/queries/insertEntity.js +338 -33
- package/dist/data/queries/insertEntity.min.js +1 -1
- package/dist/data/queries/query.d.ts +2 -1
- package/dist/data/queries/query.js +5 -2
- package/dist/data/queries/query.min.js +1 -1
- package/dist/data/queries/selectFields.d.ts +5 -4
- package/dist/data/queries/selectFields.js +25 -11
- package/dist/data/queries/selectFields.min.js +1 -1
- package/dist/data/queries/updateEntity.js +39 -5
- package/dist/data/queries/updateEntity.min.js +1 -1
- package/dist/data/storage/databases.d.ts +32 -0
- package/dist/data/storage/databases.js +93 -0
- package/dist/data/storage/databases.min.js +1 -0
- package/dist/data/storage/fileExists.d.ts +2 -1
- package/dist/data/storage/fileExists.js +9 -2
- package/dist/data/storage/fileExists.min.js +1 -1
- package/dist/data/storage/index.d.ts +5 -2
- package/dist/data/storage/index.js +64 -1
- package/dist/data/storage/index.min.js +1 -1
- package/dist/data/storage/listFiles.d.ts +8 -0
- package/dist/data/storage/listFiles.js +52 -0
- package/dist/data/storage/listFiles.min.js +1 -0
- package/dist/data/storage/makeDirectory.d.ts +2 -1
- package/dist/data/storage/makeDirectory.js +8 -2
- package/dist/data/storage/makeDirectory.min.js +1 -1
- package/dist/data/storage/readJson.d.ts +9 -1
- package/dist/data/storage/readJson.js +20 -7
- package/dist/data/storage/readJson.min.js +1 -1
- package/dist/data/storage/removeDirectory.d.ts +3 -1
- package/dist/data/storage/removeDirectory.js +7 -1
- package/dist/data/storage/removeDirectory.min.js +1 -1
- package/dist/data/storage/removeFile.d.ts +2 -1
- package/dist/data/storage/removeFile.js +10 -1
- package/dist/data/storage/removeFile.min.js +1 -1
- package/dist/data/storage/storagePath.js +1 -1
- package/dist/data/storage/storagePath.min.js +1 -1
- package/dist/data/storage/writeJson.d.ts +12 -3
- package/dist/data/storage/writeJson.js +21 -7
- package/dist/data/storage/writeJson.min.js +1 -1
- package/dist/data/storage/writeSet.d.ts +64 -0
- package/dist/data/storage/writeSet.js +306 -0
- package/dist/data/storage/writeSet.min.js +1 -0
- package/dist/data/utilities/config.d.ts +37 -0
- package/dist/data/utilities/config.js +77 -5
- package/dist/data/utilities/config.min.js +1 -1
- package/dist/data/utilities/databaseLock.d.ts +5 -0
- package/dist/data/utilities/databaseLock.js +15 -3
- package/dist/data/utilities/databaseLock.min.js +1 -1
- package/dist/data/utilities/getConditionIndexedList.js +3 -4
- package/dist/data/utilities/getConditionIndexedList.min.js +1 -1
- package/dist/data/utilities/indexBuffer.d.ts +35 -0
- package/dist/data/utilities/indexBuffer.js +139 -0
- package/dist/data/utilities/indexBuffer.min.js +1 -0
- package/dist/data/utilities/joinBases.js +2 -10
- package/dist/data/utilities/joinBases.min.js +1 -1
- package/dist/data/utilities/knownFields.d.ts +26 -0
- package/dist/data/utilities/knownFields.js +128 -0
- package/dist/data/utilities/knownFields.min.js +1 -0
- package/dist/data/utilities/packedTable.d.ts +34 -0
- package/dist/data/utilities/packedTable.js +60 -0
- package/dist/data/utilities/packedTable.min.js +1 -0
- package/dist/data/utilities/parsers/findInsertValues.d.ts +3 -3
- package/dist/data/utilities/parsers/findInsertValues.js +28 -12
- package/dist/data/utilities/parsers/findInsertValues.min.js +1 -1
- package/dist/data/utilities/parsers/maskStrings.js +11 -2
- package/dist/data/utilities/parsers/maskStrings.min.js +1 -1
- package/dist/data/utilities/parsers/parseAlter.d.ts +33 -0
- package/dist/data/utilities/parsers/parseAlter.js +84 -0
- package/dist/data/utilities/parsers/parseAlter.min.js +1 -0
- package/dist/data/utilities/parsers/parseCreate.d.ts +14 -0
- package/dist/data/utilities/parsers/parseCreate.js +13 -5
- package/dist/data/utilities/parsers/parseCreate.min.js +1 -1
- package/dist/data/utilities/parsers/parseDatabaseStatement.d.ts +17 -0
- package/dist/data/utilities/parsers/parseDatabaseStatement.js +82 -0
- package/dist/data/utilities/parsers/parseDatabaseStatement.min.js +1 -0
- package/dist/data/utilities/parsers/parseQuery.d.ts +3 -0
- package/dist/data/utilities/parsers/parseQuery.js +24 -0
- package/dist/data/utilities/parsers/parseQuery.min.js +1 -1
- package/dist/data/utilities/parsers/startsQuery.js +1 -1
- package/dist/data/utilities/parsers/startsQuery.min.js +1 -1
- package/dist/data/utilities/retrieveFiles.d.ts +15 -0
- package/dist/data/utilities/retrieveFiles.js +48 -0
- package/dist/data/utilities/retrieveFiles.min.js +1 -0
- package/dist/data/utilities/retrieveRecord.d.ts +6 -0
- package/dist/data/utilities/retrieveRecord.js +24 -1
- package/dist/data/utilities/retrieveRecord.min.js +1 -1
- package/dist/data/utilities/retrieveRecords.d.ts +22 -0
- package/dist/data/utilities/retrieveRecords.js +60 -10
- package/dist/data/utilities/retrieveRecords.min.js +1 -1
- package/dist/data/utilities/runQuery.js +27 -2
- package/dist/data/utilities/runQuery.min.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +24 -0
- package/dist/index.min.js +1 -1
- package/package.json +7 -3
|
@@ -4,13 +4,44 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.insertEntity = void 0;
|
|
7
|
+
require("core-js/modules/es.array.includes.js");
|
|
8
|
+
require("core-js/modules/es.json.stringify.js");
|
|
7
9
|
require("core-js/modules/esnext.iterator.constructor.js");
|
|
10
|
+
require("core-js/modules/esnext.iterator.every.js");
|
|
8
11
|
require("core-js/modules/esnext.iterator.find.js");
|
|
12
|
+
require("core-js/modules/esnext.iterator.for-each.js");
|
|
9
13
|
require("core-js/modules/esnext.iterator.map.js");
|
|
10
14
|
require("core-js/modules/esnext.iterator.some.js");
|
|
15
|
+
require("core-js/modules/esnext.map.delete-all.js");
|
|
16
|
+
require("core-js/modules/esnext.map.every.js");
|
|
17
|
+
require("core-js/modules/esnext.map.filter.js");
|
|
18
|
+
require("core-js/modules/esnext.map.find.js");
|
|
19
|
+
require("core-js/modules/esnext.map.find-key.js");
|
|
20
|
+
require("core-js/modules/esnext.map.includes.js");
|
|
21
|
+
require("core-js/modules/esnext.map.key-of.js");
|
|
22
|
+
require("core-js/modules/esnext.map.map-keys.js");
|
|
23
|
+
require("core-js/modules/esnext.map.map-values.js");
|
|
24
|
+
require("core-js/modules/esnext.map.merge.js");
|
|
25
|
+
require("core-js/modules/esnext.map.reduce.js");
|
|
26
|
+
require("core-js/modules/esnext.map.some.js");
|
|
27
|
+
require("core-js/modules/esnext.map.update.js");
|
|
28
|
+
require("core-js/modules/esnext.set.add-all.js");
|
|
29
|
+
require("core-js/modules/esnext.set.delete-all.js");
|
|
30
|
+
require("core-js/modules/esnext.set.difference.js");
|
|
31
|
+
require("core-js/modules/esnext.set.every.js");
|
|
32
|
+
require("core-js/modules/esnext.set.filter.js");
|
|
33
|
+
require("core-js/modules/esnext.set.find.js");
|
|
34
|
+
require("core-js/modules/esnext.set.intersection.js");
|
|
35
|
+
require("core-js/modules/esnext.set.is-disjoint-from.js");
|
|
36
|
+
require("core-js/modules/esnext.set.is-subset-of.js");
|
|
37
|
+
require("core-js/modules/esnext.set.is-superset-of.js");
|
|
38
|
+
require("core-js/modules/esnext.set.join.js");
|
|
39
|
+
require("core-js/modules/esnext.set.map.js");
|
|
40
|
+
require("core-js/modules/esnext.set.reduce.js");
|
|
41
|
+
require("core-js/modules/esnext.set.some.js");
|
|
42
|
+
require("core-js/modules/esnext.set.symmetric-difference.js");
|
|
43
|
+
require("core-js/modules/esnext.set.union.js");
|
|
11
44
|
var _retrieveRecord = require("../utilities/retrieveRecord");
|
|
12
|
-
var _updateIndex = require("../utilities/updateIndex");
|
|
13
|
-
var _keyGenerate = require("../introspect/policies/keyGenerate");
|
|
14
45
|
var _keyUnique = require("../introspect/policies/keyUnique");
|
|
15
46
|
var _useDefault = require("../introspect/policies/useDefault");
|
|
16
47
|
var _typeCheck = require("../introspect/policies/typeCheck");
|
|
@@ -18,26 +49,290 @@ var _foreignKeyExists = require("../introspect/policies/foreignKeyExists");
|
|
|
18
49
|
var _splitEntityProperty = require("../utilities/parsers/splitEntityProperty");
|
|
19
50
|
var _computeKeyValue = require("../utilities/computeKeyValue");
|
|
20
51
|
var _fieldType = require("../utilities/fieldType");
|
|
52
|
+
var _compareAs = require("../utilities/compareAs");
|
|
53
|
+
var _retrieveRecords = require("../utilities/retrieveRecords");
|
|
54
|
+
var _isSingleFieldKey = require("../utilities/isSingleFieldKey");
|
|
55
|
+
var _indexBuffer = require("../utilities/indexBuffer");
|
|
21
56
|
var _storage = require("../storage");
|
|
57
|
+
var _packedTable = require("../utilities/packedTable");
|
|
58
|
+
var _knownFields = require("../utilities/knownFields");
|
|
59
|
+
/**
|
|
60
|
+
* The rows of a record that hold each combination of values for some fields, compared field by field by type, as
|
|
61
|
+
* where's = compares them: built from the rows in storage once per statement, for a key no index can answer (several
|
|
62
|
+
* fields, or no index file), instead of looking at every row in storage once per inserted row.
|
|
63
|
+
*/
|
|
64
|
+
class RowValues {
|
|
65
|
+
constructor(record, fields) {
|
|
66
|
+
this.seen = new Set();
|
|
67
|
+
this.fields = fields;
|
|
68
|
+
this.types = fields.map(field => (0, _fieldType.fieldType)(record, field));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Whether these fields' values can be compared this way: every field of a comparable type (see compareAs).
|
|
72
|
+
* @param record
|
|
73
|
+
* @param fields
|
|
74
|
+
*/
|
|
75
|
+
static usable(record, fields) {
|
|
76
|
+
return fields.every(field => _compareAs.comparableTypes.includes((0, _fieldType.fieldType)(record, field)));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The values' key, or undefined when one is null or missing (where's = never matches those) or compareAs leaves it an
|
|
80
|
+
* object (compared by identity, so not by a key).
|
|
81
|
+
* @param row
|
|
82
|
+
*/
|
|
83
|
+
keyOf(row) {
|
|
84
|
+
const typed = [];
|
|
85
|
+
for (let position = 0; position < this.fields.length; position++) {
|
|
86
|
+
const value = row[this.fields[position]];
|
|
87
|
+
if (value === null || typeof value === 'undefined') {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
const key = (0, _compareAs.compareAs)(this.types[position], value);
|
|
91
|
+
if (typeof key === 'object') {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
typed.push([typeof key, key]);
|
|
95
|
+
}
|
|
96
|
+
return JSON.stringify(typed);
|
|
97
|
+
}
|
|
98
|
+
add(row) {
|
|
99
|
+
const key = this.keyOf(row);
|
|
100
|
+
if (typeof key !== 'undefined') {
|
|
101
|
+
this.seen.add(key);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
has(row) {
|
|
105
|
+
const key = this.keyOf(row);
|
|
106
|
+
return typeof key !== 'undefined' && this.seen.has(key);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* One insert statement's view of the keys it checks, so that each index is read and parsed once for all its rows
|
|
111
|
+
* instead of once per row, and the record file and its indexes are written once at the end instead of once per row.
|
|
112
|
+
*
|
|
113
|
+
* It answers every check exactly as reading the files after each row would (as inserts did one row at a time): the
|
|
114
|
+
* record's own indexes are held in memory (see IndexBuffer) and gain each row as it's added, so a row is checked
|
|
115
|
+
* against the rows before it in the statement too; another record's index is read once, since an insert never changes
|
|
116
|
+
* another record. Where a check can't use an index it looks at every row in storage as before, plus this statement's
|
|
117
|
+
* rows, which aren't in storage until the end.
|
|
118
|
+
*/
|
|
119
|
+
class InsertStatement {
|
|
120
|
+
constructor(entity, record) {
|
|
121
|
+
this.rows = [];
|
|
122
|
+
this.buffers = new Map();
|
|
123
|
+
this.unreadable = new Set();
|
|
124
|
+
this.otherLookups = new Map();
|
|
125
|
+
this.scans = new Map();
|
|
126
|
+
this.entity = entity;
|
|
127
|
+
this.record = record;
|
|
128
|
+
this.hadEntries = (record.entries || []).length > 0;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Read the record's index files, once. An index that can't be read starts empty (as updateIndex starts one) and its
|
|
132
|
+
* key is checked by looking at every row.
|
|
133
|
+
*/
|
|
134
|
+
async load() {
|
|
135
|
+
for (const key of this.record.keys) {
|
|
136
|
+
if (!key.lookup) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
let entries = [];
|
|
140
|
+
try {
|
|
141
|
+
entries = await (0, _storage.readJson)(`__indexes/${this.record.path}/${key.lookup}`);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
this.unreadable.add(key);
|
|
144
|
+
}
|
|
145
|
+
this.buffers.set(key, new _indexBuffer.IndexBuffer(Array.isArray(entries) ? entries : [], (0, _fieldType.keyValueType)(this.record, key)));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* How to look up a non-null value of `property` in the record being inserted into: through its index buffer, which
|
|
150
|
+
* holds the statement's rows so far. indexHas reads a record with no entries as holding nothing, whatever its index
|
|
151
|
+
* file says, so until this statement adds a row to a record that had none, nothing is found.
|
|
152
|
+
* @param property
|
|
153
|
+
*/
|
|
154
|
+
ownLookup(property) {
|
|
155
|
+
const key = this.record.keys.find(key => (0, _isSingleFieldKey.isSingleFieldKey)(key, property) && key.lookup);
|
|
156
|
+
if (!key || this.unreadable.has(key)) {
|
|
157
|
+
return () => undefined;
|
|
158
|
+
}
|
|
159
|
+
const buffer = this.buffers.get(key);
|
|
160
|
+
return value => (this.hadEntries || this.rows.length > 0) && buffer.has(value);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* How to look up a non-null value of `property` in another record, read once (an insert doesn't change it).
|
|
164
|
+
* @param entity
|
|
165
|
+
* @param property
|
|
166
|
+
*/
|
|
167
|
+
otherLookup(entity, property) {
|
|
168
|
+
const name = `${entity}\u0000${property}`;
|
|
169
|
+
if (!this.otherLookups.has(name)) {
|
|
170
|
+
this.otherLookups.set(name, (async () => {
|
|
171
|
+
const record = await (0, _retrieveRecord.retrieveRecord)(entity);
|
|
172
|
+
const key = record.keys.find(key => (0, _isSingleFieldKey.isSingleFieldKey)(key, property) && key.lookup);
|
|
173
|
+
if (!key) {
|
|
174
|
+
return () => undefined;
|
|
175
|
+
}
|
|
176
|
+
if (!(record.entries || []).length) {
|
|
177
|
+
return () => false;
|
|
178
|
+
}
|
|
179
|
+
let entries;
|
|
180
|
+
try {
|
|
181
|
+
entries = await (0, _storage.readJson)(`__indexes/${record.path}/${key.lookup}`);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
return () => undefined;
|
|
184
|
+
}
|
|
185
|
+
const buffer = new _indexBuffer.IndexBuffer(Array.isArray(entries) ? entries : [], (0, _fieldType.fieldType)(record, property));
|
|
186
|
+
return value => buffer.has(value);
|
|
187
|
+
})());
|
|
188
|
+
}
|
|
189
|
+
return this.otherLookups.get(name);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* The values of these fields in every row of `entity` (read once per statement), plus, for the record being inserted
|
|
193
|
+
* into, every row this statement adds; undefined when a field's type can't be compared by key.
|
|
194
|
+
* @param entity
|
|
195
|
+
* @param fields
|
|
196
|
+
*/
|
|
197
|
+
scan(entity, fields) {
|
|
198
|
+
const name = `${entity}\u0000${fields.join('\u0000')}`;
|
|
199
|
+
if (!this.scans.has(name)) {
|
|
200
|
+
this.scans.set(name, (async () => {
|
|
201
|
+
const record = entity === this.entity ? this.record : await (0, _retrieveRecord.retrieveRecord)(entity);
|
|
202
|
+
if (!RowValues.usable(record, fields)) {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
const values = new RowValues(record, fields);
|
|
206
|
+
// The record's rows in storage: this statement's own rows aren't there until the end, and are added as they come
|
|
207
|
+
for (const row of await (0, _retrieveRecords.retrieveRecords)(entity)) {
|
|
208
|
+
values.add(row);
|
|
209
|
+
}
|
|
210
|
+
if (entity === this.entity) {
|
|
211
|
+
this.rows.forEach(row => values.add(row));
|
|
212
|
+
}
|
|
213
|
+
return values;
|
|
214
|
+
})());
|
|
215
|
+
}
|
|
216
|
+
return this.scans.get(name);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Whether one of this statement's rows already holds these values in every one of these fields (none of them null).
|
|
220
|
+
* @param fields
|
|
221
|
+
* @param values
|
|
222
|
+
*/
|
|
223
|
+
claimed(fields, values) {
|
|
224
|
+
return this.rows.some(row => fields.every(field => row[field] !== null && typeof row[field] !== 'undefined' && (0, _compareAs.sameValue)((0, _fieldType.fieldType)(this.record, field), row[field], values[field])));
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Whether no row (in storage or earlier in this statement) holds these values for these fields, as keyUnique answers
|
|
228
|
+
* it after each earlier row is written.
|
|
229
|
+
* @param fields
|
|
230
|
+
* @param values
|
|
231
|
+
*/
|
|
232
|
+
async isUnique(fields, values) {
|
|
233
|
+
if (fields.length === 1) {
|
|
234
|
+
const found = this.ownLookup(fields[0])(values[fields[0]]);
|
|
235
|
+
if (typeof found !== 'undefined') {
|
|
236
|
+
return !found;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const scanned = await this.scan(this.entity, fields);
|
|
240
|
+
if (scanned) {
|
|
241
|
+
return !scanned.has(values);
|
|
242
|
+
}
|
|
243
|
+
return (await (0, _keyUnique.keyUnique)(this.entity, fields, fields.map(field => values[field]))) && !this.claimed(fields, values);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Whether `entity` holds `value` in `property`, as foreignKeyExists answers it after each earlier row is written: a
|
|
247
|
+
* row may reference one added earlier in the same statement when the key points at its own record.
|
|
248
|
+
* @param entity
|
|
249
|
+
* @param property
|
|
250
|
+
* @param value
|
|
251
|
+
*/
|
|
252
|
+
async exists(entity, property, value) {
|
|
253
|
+
const own = entity === this.entity;
|
|
254
|
+
const found = own ? this.ownLookup(property)(value) : (await this.otherLookup(entity, property))(value);
|
|
255
|
+
if (typeof found !== 'undefined') {
|
|
256
|
+
return found;
|
|
257
|
+
}
|
|
258
|
+
const scanned = await this.scan(entity, [property]);
|
|
259
|
+
if (scanned) {
|
|
260
|
+
return scanned.has({
|
|
261
|
+
[property]: value
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
return (await (0, _foreignKeyExists.foreignKeyExists)(entity, property, value)) || own && this.claimed([property], {
|
|
265
|
+
[property]: value
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Note a row added to the statement, filed under `fileName`, in the record's entries and every index buffer.
|
|
270
|
+
* @param entityValues
|
|
271
|
+
* @param fileName
|
|
272
|
+
*/
|
|
273
|
+
async add(entityValues, fileName) {
|
|
274
|
+
this.rows.push(entityValues);
|
|
275
|
+
// Scans already built for this record hold the rows in storage; a row added now joins them
|
|
276
|
+
for (const [name, scanned] of this.scans) {
|
|
277
|
+
if (name.startsWith(`${this.entity}\u0000`)) {
|
|
278
|
+
(await scanned)?.add(entityValues);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
this.record.entries.push(fileName);
|
|
282
|
+
for (const [key, buffer] of this.buffers) {
|
|
283
|
+
buffer.add((0, _computeKeyValue.computeKeyValue)(key.fields, entityValues), fileName);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Write the record file and each index once, with every row the statement added.
|
|
288
|
+
*/
|
|
289
|
+
async write() {
|
|
290
|
+
await (0, _storage.writeJson)(`${this.record.path}.json`, this.record);
|
|
291
|
+
if (this.buffers.size) {
|
|
292
|
+
await (0, _storage.makeDirectory)(`__indexes/${this.record.path}`);
|
|
293
|
+
}
|
|
294
|
+
for (const [key, buffer] of this.buffers) {
|
|
295
|
+
await (0, _storage.writeJson)(`__indexes/${this.record.path}/${key.lookup}`, buffer.entries);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* A new id for an autoGenerate field that no row holds yet, in storage or earlier in the statement.
|
|
301
|
+
* @param statement
|
|
302
|
+
* @param field
|
|
303
|
+
*/
|
|
304
|
+
const generateKey = async (statement, field) => {
|
|
305
|
+
let uuid;
|
|
306
|
+
do {
|
|
307
|
+
uuid = crypto.randomUUID();
|
|
308
|
+
} while (!(await statement.isUnique([field], {
|
|
309
|
+
[field]: uuid
|
|
310
|
+
})));
|
|
311
|
+
return uuid;
|
|
312
|
+
};
|
|
22
313
|
/**
|
|
23
314
|
* Build one entity's stored values from the raw input values, generating any autoGenerate
|
|
24
|
-
* fields and applying field defaults, then throw if a required field still has no value.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
315
|
+
* fields and applying field defaults, then throw if a required field still has no value. A null
|
|
316
|
+
* in an optional field that isn't nullable means the same as leaving it out (the field can't
|
|
317
|
+
* hold null, so nothing is lost): it isn't stored, and a default fills it in as for a field left out.
|
|
318
|
+
* @param statement
|
|
27
319
|
* @param rowValues
|
|
28
320
|
*/
|
|
29
|
-
const buildEntityValues = async (
|
|
321
|
+
const buildEntityValues = async (statement, rowValues = {}) => {
|
|
30
322
|
const entityValues = {};
|
|
31
|
-
for (const field of record.definition) {
|
|
323
|
+
for (const field of statement.record.definition) {
|
|
32
324
|
let value = rowValues[field.name];
|
|
325
|
+
if (value === null && field.optional && !field.nullable) {
|
|
326
|
+
value = undefined;
|
|
327
|
+
}
|
|
33
328
|
if (field.autoGenerate) {
|
|
34
|
-
value = await (
|
|
329
|
+
value = await generateKey(statement, field.name);
|
|
35
330
|
} else if (Object.prototype.hasOwnProperty.call(field, 'default')) {
|
|
36
331
|
value = (0, _useDefault.useDefault)(field['default'], value);
|
|
37
332
|
}
|
|
38
333
|
if (typeof value === 'undefined') {
|
|
39
334
|
if (!field.optional) {
|
|
40
|
-
throw new Error(`Missing required field "${field.name}" for ${entity}`);
|
|
335
|
+
throw new Error(`Missing required field "${field.name}" for ${statement.entity}`);
|
|
41
336
|
}
|
|
42
337
|
continue;
|
|
43
338
|
}
|
|
@@ -49,34 +344,30 @@ const buildEntityValues = async (entity, record, rowValues = {}) => {
|
|
|
49
344
|
* Reject the insert if it collides with an existing primary or unique key value - a single field,
|
|
50
345
|
* or a composite of several for a multi-field key. As in SQL, a value with a null in it is never a
|
|
51
346
|
* duplicate, so a nullable unique field can hold null in any number of rows.
|
|
52
|
-
* @param
|
|
53
|
-
* @param record
|
|
347
|
+
* @param statement
|
|
54
348
|
* @param entityValues
|
|
55
349
|
*/
|
|
56
|
-
const assertUnique = async (
|
|
57
|
-
for (const key of record.keys) {
|
|
350
|
+
const assertUnique = async (statement, entityValues) => {
|
|
351
|
+
for (const key of statement.record.keys) {
|
|
58
352
|
if (key.type !== 'primary' && key.type !== 'unique') {
|
|
59
353
|
continue;
|
|
60
354
|
}
|
|
61
|
-
|
|
62
|
-
if (checkKeys.some(value => value === null)) {
|
|
355
|
+
if (key.fields.some(field => entityValues[field] === null)) {
|
|
63
356
|
continue;
|
|
64
357
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
throw new Error(`Duplicate value for ${key.type} key "${key.fields.join(', ')}" on ${entity}`);
|
|
358
|
+
if (!(await statement.isUnique(key.fields, entityValues))) {
|
|
359
|
+
throw new Error(`Duplicate value for ${key.type} key "${key.fields.join(', ')}" on ${statement.entity}`);
|
|
68
360
|
}
|
|
69
361
|
}
|
|
70
362
|
};
|
|
71
363
|
/**
|
|
72
364
|
* Reject the insert if a single-field foreign key's value doesn't exist in the record it
|
|
73
365
|
* references. A null references nothing, so it isn't checked (as in SQL).
|
|
74
|
-
* @param
|
|
75
|
-
* @param record
|
|
366
|
+
* @param statement
|
|
76
367
|
* @param entityValues
|
|
77
368
|
*/
|
|
78
|
-
const assertForeignKeys = async (
|
|
79
|
-
for (const key of record.keys) {
|
|
369
|
+
const assertForeignKeys = async (statement, entityValues) => {
|
|
370
|
+
for (const key of statement.record.keys) {
|
|
80
371
|
if (key.type !== 'foreign' || key.fields.length !== 1 || !key.references || key.references.length !== 1) {
|
|
81
372
|
continue;
|
|
82
373
|
}
|
|
@@ -88,12 +379,19 @@ const assertForeignKeys = async (entity, record, entityValues) => {
|
|
|
88
379
|
entity: refEntity,
|
|
89
380
|
property: refProperty
|
|
90
381
|
} = (0, _splitEntityProperty.splitEntityProperty)(key.references[0].fields[0]);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
throw new Error(`Foreign key "${field}" on ${entity} references a nonexistent ${refEntity}.${refProperty} = ${entityValues[field]}`);
|
|
382
|
+
if (!(await statement.exists(refEntity, refProperty, entityValues[field]))) {
|
|
383
|
+
throw new Error(`Foreign key "${field}" on ${statement.entity} references a nonexistent ${refEntity}.${refProperty} = ${entityValues[field]}`);
|
|
94
384
|
}
|
|
95
385
|
}
|
|
96
386
|
};
|
|
387
|
+
/**
|
|
388
|
+
* Insert rows into a record, all in one statement: every row is checked (against the rows in storage and the rows
|
|
389
|
+
* before it in the statement) and its entry file written, then the record file and each index are written once.
|
|
390
|
+
* Inside a write set (as every query runs, see withWriteSet) a row that fails leaves none of them written.
|
|
391
|
+
* @param entity
|
|
392
|
+
* @param values - The rows, as field -> value.
|
|
393
|
+
* @param dataSet
|
|
394
|
+
*/
|
|
97
395
|
const insertEntity = async (entity = '', values = [], dataSet = {}) => {
|
|
98
396
|
const record = await (0, _retrieveRecord.retrieveRecord)(entity);
|
|
99
397
|
const primaryKey = record.keys.find(key => key.type === 'primary' && key.fields.length === 1);
|
|
@@ -103,19 +401,26 @@ const insertEntity = async (entity = '', values = [], dataSet = {}) => {
|
|
|
103
401
|
// The record's folder can be missing even though the record exists: git doesn't keep empty folders, so a database
|
|
104
402
|
// committed before its first insert comes back from a fresh clone without it
|
|
105
403
|
await (0, _storage.makeDirectory)(record.path);
|
|
404
|
+
if (!values.length) {
|
|
405
|
+
return dataSet;
|
|
406
|
+
}
|
|
407
|
+
// A field the record doesn't define would otherwise be dropped without a word
|
|
106
408
|
for (const rowValues of values) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
409
|
+
(0, _knownFields.assertFieldsDefined)(entity, record, Object.keys(rowValues || {}), 'insert');
|
|
410
|
+
}
|
|
411
|
+
await (0, _packedTable.discardPacked)(record.path);
|
|
412
|
+
const statement = new InsertStatement(entity, record);
|
|
413
|
+
await statement.load();
|
|
414
|
+
for (const rowValues of values) {
|
|
415
|
+
const entityValues = await buildEntityValues(statement, rowValues);
|
|
416
|
+
await assertUnique(statement, entityValues);
|
|
417
|
+
await assertForeignKeys(statement, entityValues);
|
|
110
418
|
const fileName = `${primaryKey ? entityValues[primaryKey.fields[0]] : crypto.randomUUID()}.json`;
|
|
111
419
|
await (0, _storage.writeJson)(`${record.path}/${fileName}`, entityValues);
|
|
112
|
-
|
|
113
|
-
await (0, _storage.writeJson)(`${record.path}.json`, record);
|
|
114
|
-
for (const key of record.keys) {
|
|
115
|
-
await (0, _updateIndex.updateIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, entityValues), fileName, (0, _fieldType.keyValueType)(record, key));
|
|
116
|
-
}
|
|
420
|
+
await statement.add(entityValues, fileName);
|
|
117
421
|
dataSet[entity].push(entityValues);
|
|
118
422
|
}
|
|
423
|
+
await statement.write();
|
|
119
424
|
return dataSet;
|
|
120
425
|
};
|
|
121
426
|
exports.insertEntity = insertEntity;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.insertEntity=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.find.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.js")
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.insertEntity=void 0,require("core-js/modules/es.array.includes.js"),require("core-js/modules/es.json.stringify.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.every.js"),require("core-js/modules/esnext.iterator.find.js"),require("core-js/modules/esnext.iterator.for-each.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.js"),require("core-js/modules/esnext.map.delete-all.js"),require("core-js/modules/esnext.map.every.js"),require("core-js/modules/esnext.map.filter.js"),require("core-js/modules/esnext.map.find.js"),require("core-js/modules/esnext.map.find-key.js"),require("core-js/modules/esnext.map.includes.js"),require("core-js/modules/esnext.map.key-of.js"),require("core-js/modules/esnext.map.map-keys.js"),require("core-js/modules/esnext.map.map-values.js"),require("core-js/modules/esnext.map.merge.js"),require("core-js/modules/esnext.map.reduce.js"),require("core-js/modules/esnext.map.some.js"),require("core-js/modules/esnext.map.update.js"),require("core-js/modules/esnext.set.add-all.js"),require("core-js/modules/esnext.set.delete-all.js"),require("core-js/modules/esnext.set.difference.js"),require("core-js/modules/esnext.set.every.js"),require("core-js/modules/esnext.set.filter.js"),require("core-js/modules/esnext.set.find.js"),require("core-js/modules/esnext.set.intersection.js"),require("core-js/modules/esnext.set.is-disjoint-from.js"),require("core-js/modules/esnext.set.is-subset-of.js"),require("core-js/modules/esnext.set.is-superset-of.js"),require("core-js/modules/esnext.set.join.js"),require("core-js/modules/esnext.set.map.js"),require("core-js/modules/esnext.set.reduce.js"),require("core-js/modules/esnext.set.some.js"),require("core-js/modules/esnext.set.symmetric-difference.js"),require("core-js/modules/esnext.set.union.js");var _retrieveRecord=require("../utilities/retrieveRecord"),_keyUnique=require("../introspect/policies/keyUnique"),_useDefault=require("../introspect/policies/useDefault"),_typeCheck=require("../introspect/policies/typeCheck"),_foreignKeyExists=require("../introspect/policies/foreignKeyExists"),_splitEntityProperty=require("../utilities/parsers/splitEntityProperty"),_computeKeyValue=require("../utilities/computeKeyValue"),_fieldType=require("../utilities/fieldType"),_compareAs=require("../utilities/compareAs"),_retrieveRecords=require("../utilities/retrieveRecords"),_isSingleFieldKey=require("../utilities/isSingleFieldKey"),_indexBuffer=require("../utilities/indexBuffer"),_storage=require("../storage"),_packedTable=require("../utilities/packedTable"),_knownFields=require("../utilities/knownFields");class RowValues{constructor(e,s){this.seen=new Set,this.fields=s,this.types=s.map((s=>(0,_fieldType.fieldType)(e,s)))}static usable(e,s){return s.every((s=>_compareAs.comparableTypes.includes((0,_fieldType.fieldType)(e,s))))}keyOf(e){const s=[];for(let t=0;t<this.fields.length;t++){const r=e[this.fields[t]];if(null==r)return;const i=(0,_compareAs.compareAs)(this.types[t],r);if("object"==typeof i)return;s.push([typeof i,i])}return JSON.stringify(s)}add(e){const s=this.keyOf(e);void 0!==s&&this.seen.add(s)}has(e){const s=this.keyOf(e);return void 0!==s&&this.seen.has(s)}}class InsertStatement{constructor(e,s){this.rows=[],this.buffers=new Map,this.unreadable=new Set,this.otherLookups=new Map,this.scans=new Map,this.entity=e,this.record=s,this.hadEntries=(s.entries||[]).length>0}async load(){for(const e of this.record.keys){if(!e.lookup)continue;let s=[];try{s=await(0,_storage.readJson)(`__indexes/${this.record.path}/${e.lookup}`)}catch(s){this.unreadable.add(e)}this.buffers.set(e,new _indexBuffer.IndexBuffer(Array.isArray(s)?s:[],(0,_fieldType.keyValueType)(this.record,e)))}}ownLookup(e){const s=this.record.keys.find((s=>(0,_isSingleFieldKey.isSingleFieldKey)(s,e)&&s.lookup));if(!s||this.unreadable.has(s))return()=>{};const t=this.buffers.get(s);return e=>(this.hadEntries||this.rows.length>0)&&t.has(e)}otherLookup(e,s){const t=`${e}\0${s}`;return this.otherLookups.has(t)||this.otherLookups.set(t,(async()=>{const t=await(0,_retrieveRecord.retrieveRecord)(e),r=t.keys.find((e=>(0,_isSingleFieldKey.isSingleFieldKey)(e,s)&&e.lookup));if(!r)return()=>{};if(!(t.entries||[]).length)return()=>!1;let i;try{i=await(0,_storage.readJson)(`__indexes/${t.path}/${r.lookup}`)}catch(e){return()=>{}}const o=new _indexBuffer.IndexBuffer(Array.isArray(i)?i:[],(0,_fieldType.fieldType)(t,s));return e=>o.has(e)})()),this.otherLookups.get(t)}scan(e,s){const t=`${e}\0${s.join("\0")}`;return this.scans.has(t)||this.scans.set(t,(async()=>{const t=e===this.entity?this.record:await(0,_retrieveRecord.retrieveRecord)(e);if(!RowValues.usable(t,s))return;const r=new RowValues(t,s);for(const s of await(0,_retrieveRecords.retrieveRecords)(e))r.add(s);return e===this.entity&&this.rows.forEach((e=>r.add(e))),r})()),this.scans.get(t)}claimed(e,s){return this.rows.some((t=>e.every((e=>null!==t[e]&&void 0!==t[e]&&(0,_compareAs.sameValue)((0,_fieldType.fieldType)(this.record,e),t[e],s[e])))))}async isUnique(e,s){if(1===e.length){const t=this.ownLookup(e[0])(s[e[0]]);if(void 0!==t)return!t}const t=await this.scan(this.entity,e);return t?!t.has(s):await(0,_keyUnique.keyUnique)(this.entity,e,e.map((e=>s[e])))&&!this.claimed(e,s)}async exists(e,s,t){const r=e===this.entity,i=r?this.ownLookup(s)(t):(await this.otherLookup(e,s))(t);if(void 0!==i)return i;const o=await this.scan(e,[s]);return o?o.has({[s]:t}):await(0,_foreignKeyExists.foreignKeyExists)(e,s,t)||r&&this.claimed([s],{[s]:t})}async add(e,s){this.rows.push(e);for(const[s,t]of this.scans)s.startsWith(`${this.entity}\0`)&&(await t)?.add(e);this.record.entries.push(s);for(const[t,r]of this.buffers)r.add((0,_computeKeyValue.computeKeyValue)(t.fields,e),s)}async write(){await(0,_storage.writeJson)(`${this.record.path}.json`,this.record),this.buffers.size&&await(0,_storage.makeDirectory)(`__indexes/${this.record.path}`);for(const[e,s]of this.buffers)await(0,_storage.writeJson)(`__indexes/${this.record.path}/${e.lookup}`,s.entries)}}const generateKey=async(e,s)=>{let t;do{t=crypto.randomUUID()}while(!await e.isUnique([s],{[s]:t}));return t},buildEntityValues=async(e,s={})=>{const t={};for(const r of e.record.definition){let i=s[r.name];if(null===i&&r.optional&&!r.nullable&&(i=void 0),r.autoGenerate?i=await generateKey(e,r.name):Object.prototype.hasOwnProperty.call(r,"default")&&(i=(0,_useDefault.useDefault)(r.default,i)),void 0!==i)t[r.name]=(0,_typeCheck.typeCheck)(r.type,i,r.nullable);else if(!r.optional)throw new Error(`Missing required field "${r.name}" for ${e.entity}`)}return t},assertUnique=async(e,s)=>{for(const t of e.record.keys)if(!("primary"!==t.type&&"unique"!==t.type||t.fields.some((e=>null===s[e]))||await e.isUnique(t.fields,s)))throw new Error(`Duplicate value for ${t.type} key "${t.fields.join(", ")}" on ${e.entity}`)},assertForeignKeys=async(e,s)=>{for(const t of e.record.keys){if("foreign"!==t.type||1!==t.fields.length||!t.references||1!==t.references.length)continue;const r=t.fields[0];if(!Object.prototype.hasOwnProperty.call(s,r)||null===s[r])continue;const{entity:i,property:o}=(0,_splitEntityProperty.splitEntityProperty)(t.references[0].fields[0]);if(!await e.exists(i,o,s[r]))throw new Error(`Foreign key "${r}" on ${e.entity} references a nonexistent ${i}.${o} = ${s[r]}`)}},insertEntity=async(e="",s=[],t={})=>{const r=await(0,_retrieveRecord.retrieveRecord)(e),i=r.keys.find((e=>"primary"===e.type&&1===e.fields.length));if(Object.prototype.hasOwnProperty.call(t,e)||(t[e]=[]),await(0,_storage.makeDirectory)(r.path),!s.length)return t;for(const t of s)(0,_knownFields.assertFieldsDefined)(e,r,Object.keys(t||{}),"insert");await(0,_packedTable.discardPacked)(r.path);const o=new InsertStatement(e,r);await o.load();for(const n of s){const s=await buildEntityValues(o,n);await assertUnique(o,s),await assertForeignKeys(o,s);const a=`${i?s[i.fields[0]]:crypto.randomUUID()}.json`;await(0,_storage.writeJson)(`${r.path}/${a}`,s),await o.add(s,a),t[e].push(s)}return await o.write(),t};exports.insertEntity=insertEntity;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Parse a query string (one query, or several separated by `;`) and run each query in turn, resolving to one result
|
|
3
3
|
* per query. The queries of one call run together, under the database lock: no other call's queries run between
|
|
4
|
-
* them (see withDatabaseLock).
|
|
4
|
+
* them (see withDatabaseLock). Each query is all or nothing: one that fails changes nothing, and the queries before it
|
|
5
|
+
* keep their changes (see withWriteSet).
|
|
5
6
|
* @param queryString
|
|
6
7
|
*/
|
|
7
8
|
export declare const query: (queryString?: string) => Promise<any[]>;
|
|
@@ -7,10 +7,12 @@ exports.query = void 0;
|
|
|
7
7
|
var _parser = require("./parser");
|
|
8
8
|
var _runQuery = require("../utilities/runQuery");
|
|
9
9
|
var _databaseLock = require("../utilities/databaseLock");
|
|
10
|
+
var _storage = require("../storage");
|
|
10
11
|
/**
|
|
11
12
|
* Parse a query string (one query, or several separated by `;`) and run each query in turn, resolving to one result
|
|
12
13
|
* per query. The queries of one call run together, under the database lock: no other call's queries run between
|
|
13
|
-
* them (see withDatabaseLock).
|
|
14
|
+
* them (see withDatabaseLock). Each query is all or nothing: one that fails changes nothing, and the queries before it
|
|
15
|
+
* keep their changes (see withWriteSet).
|
|
14
16
|
* @param queryString
|
|
15
17
|
*/
|
|
16
18
|
const query = async (queryString = '') => {
|
|
@@ -18,7 +20,8 @@ const query = async (queryString = '') => {
|
|
|
18
20
|
return (0, _databaseLock.withDatabaseLock)(async () => {
|
|
19
21
|
const queryResults = [];
|
|
20
22
|
for (const parsed of parsedQueries) {
|
|
21
|
-
|
|
23
|
+
// Each query's changes reach storage together, or not at all if it fails (see withWriteSet)
|
|
24
|
+
const dataSet = await (0, _storage.withWriteSet)(() => (0, _runQuery.runQuery)(parsed));
|
|
22
25
|
queryResults.push(dataSet[parsed.entity]);
|
|
23
26
|
}
|
|
24
27
|
return queryResults;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.query=void 0;var _parser=require("./parser"),_runQuery=require("../utilities/runQuery"),_databaseLock=require("../utilities/databaseLock");const query=async(e="")=>{const r=(0,_parser.parser)(e);return(0,_databaseLock.withDatabaseLock)((async()=>{const e=[];for(const t of r){const r=await(0,_runQuery.runQuery)(t);e.push(r[t.entity])}return e}))};exports.query=query;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.query=void 0;var _parser=require("./parser"),_runQuery=require("../utilities/runQuery"),_databaseLock=require("../utilities/databaseLock"),_storage=require("../storage");const query=async(e="")=>{const r=(0,_parser.parser)(e);return(0,_databaseLock.withDatabaseLock)((async()=>{const e=[];for(const t of r){const r=await(0,_storage.withWriteSet)((()=>(0,_runQuery.runQuery)(t)));e.push(r[t.entity])}return e}))};exports.query=query;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { ParsedSelect } from '../utilities/parsers/findSelects';
|
|
2
2
|
import { Group } from './groupBy';
|
|
3
3
|
/**
|
|
4
|
-
* Shape each row down to only the selected properties (aliased, when given). dataSet is
|
|
5
|
-
* (once groupBy has run) a list of groups, whose rows are shaped the same way at any depth;
|
|
6
|
-
* field and value, selected or not.
|
|
4
|
+
* Shape each row down to only the selected properties (aliased, when given, otherwise named as written). dataSet is
|
|
5
|
+
* either a list of rows, or (once groupBy has run) a list of groups, whose rows are shaped the same way at any depth;
|
|
6
|
+
* the groups keep their field and value, selected or not.
|
|
7
7
|
* @param dataSet
|
|
8
8
|
* @param selectClauses
|
|
9
9
|
* @param grouped Whether dataSet is a list of groups (see groupBy)
|
|
10
|
+
* @param entity The queried record's name, so `<record>.<field>` picks the row's own field
|
|
10
11
|
*/
|
|
11
|
-
export declare const selectFields: (dataSet?: Object[] | Group[], selectClauses?: ParsedSelect[], grouped?: boolean) => Object[] | Group[];
|
|
12
|
+
export declare const selectFields: (dataSet?: Object[] | Group[], selectClauses?: ParsedSelect[], grouped?: boolean, entity?: string) => Object[] | Group[];
|
|
12
13
|
//# sourceMappingURL=selectFields.d.ts.map
|
|
@@ -7,35 +7,49 @@ exports.selectFields = void 0;
|
|
|
7
7
|
require("core-js/modules/esnext.iterator.constructor.js");
|
|
8
8
|
require("core-js/modules/esnext.iterator.map.js");
|
|
9
9
|
require("core-js/modules/esnext.iterator.reduce.js");
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* The value a select name picks from a row: the row's own key (a field, a merged record's `<record>.<field>`, or a
|
|
12
|
+
* joined record's rows), or for `<queried record>.<field>`, the row's own field.
|
|
13
|
+
* @param row
|
|
14
|
+
* @param property
|
|
15
|
+
* @param entity - The queried record's name.
|
|
16
|
+
*/
|
|
17
|
+
const pick = (row, property, entity) => {
|
|
18
|
+
if (Object.prototype.hasOwnProperty.call(row, property) || typeof entity === 'undefined' || !property.startsWith(`${entity}.`)) {
|
|
19
|
+
return row[property];
|
|
20
|
+
}
|
|
21
|
+
return row[property.slice(entity.length + 1)];
|
|
22
|
+
};
|
|
23
|
+
const shapeRow = (row, selectClauses, entity) => selectClauses.reduce((shaped, {
|
|
11
24
|
property,
|
|
12
25
|
alias
|
|
13
26
|
}) => {
|
|
14
|
-
shaped[alias || property] = row
|
|
27
|
+
shaped[alias || property] = pick(row, property, entity);
|
|
15
28
|
return shaped;
|
|
16
29
|
}, {});
|
|
17
|
-
const shapeGroup = (group, selectClauses) => group.groups ? {
|
|
30
|
+
const shapeGroup = (group, selectClauses, entity) => group.groups ? {
|
|
18
31
|
...group,
|
|
19
|
-
groups: group.groups.map(inner => shapeGroup(inner, selectClauses))
|
|
32
|
+
groups: group.groups.map(inner => shapeGroup(inner, selectClauses, entity))
|
|
20
33
|
} : {
|
|
21
34
|
...group,
|
|
22
|
-
rows: group.rows.map(row => shapeRow(row, selectClauses))
|
|
35
|
+
rows: group.rows.map(row => shapeRow(row, selectClauses, entity))
|
|
23
36
|
};
|
|
24
37
|
/**
|
|
25
|
-
* Shape each row down to only the selected properties (aliased, when given). dataSet is
|
|
26
|
-
* (once groupBy has run) a list of groups, whose rows are shaped the same way at any depth;
|
|
27
|
-
* field and value, selected or not.
|
|
38
|
+
* Shape each row down to only the selected properties (aliased, when given, otherwise named as written). dataSet is
|
|
39
|
+
* either a list of rows, or (once groupBy has run) a list of groups, whose rows are shaped the same way at any depth;
|
|
40
|
+
* the groups keep their field and value, selected or not.
|
|
28
41
|
* @param dataSet
|
|
29
42
|
* @param selectClauses
|
|
30
43
|
* @param grouped Whether dataSet is a list of groups (see groupBy)
|
|
44
|
+
* @param entity The queried record's name, so `<record>.<field>` picks the row's own field
|
|
31
45
|
*/
|
|
32
|
-
const selectFields = (dataSet = [], selectClauses = [], grouped = false) => {
|
|
46
|
+
const selectFields = (dataSet = [], selectClauses = [], grouped = false, entity) => {
|
|
33
47
|
if (!selectClauses.length) {
|
|
34
48
|
return dataSet;
|
|
35
49
|
}
|
|
36
50
|
if (grouped) {
|
|
37
|
-
return dataSet.map(group => shapeGroup(group, selectClauses));
|
|
51
|
+
return dataSet.map(group => shapeGroup(group, selectClauses, entity));
|
|
38
52
|
}
|
|
39
|
-
return dataSet.map(row => shapeRow(row, selectClauses));
|
|
53
|
+
return dataSet.map(row => shapeRow(row, selectClauses, entity));
|
|
40
54
|
};
|
|
41
55
|
exports.selectFields = selectFields;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.selectFields=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.reduce.js");const shapeRow=(e,s)=>s.reduce(((s,{property:
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.selectFields=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.reduce.js");const pick=(e,s,r)=>Object.prototype.hasOwnProperty.call(e,s)||void 0===r||!s.startsWith(`${r}.`)?e[s]:e[s.slice(r.length+1)],shapeRow=(e,s,r)=>s.reduce(((s,{property:o,alias:t})=>(s[t||o]=pick(e,o,r),s)),{}),shapeGroup=(e,s,r)=>e.groups?{...e,groups:e.groups.map((e=>shapeGroup(e,s,r)))}:{...e,rows:e.rows.map((e=>shapeRow(e,s,r)))},selectFields=(e=[],s=[],r=!1,o)=>s.length?r?e.map((e=>shapeGroup(e,s,o))):e.map((e=>shapeRow(e,s,o))):e;exports.selectFields=selectFields;
|