@ja-platform/db 0.0.0-dev-20261113193343 → 0.0.0-stage
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 -0
- package/package.json +3 -52
- package/LICENSE +0 -521
- package/dist/@types/Schema.d.ts +0 -1
- package/dist/@types/app.d.ts +0 -12
- package/dist/@types/browser.d.ts +0 -31
- package/dist/@types/credentials/ApiCredentials.d.ts +0 -18
- package/dist/@types/credentials/MobileCredentials.d.ts +0 -22
- package/dist/@types/credentials/index.d.ts +0 -2
- package/dist/@types/database/Batch.d.ts +0 -61
- package/dist/@types/database/Collection.d.ts +0 -85
- package/dist/@types/database/Database.d.ts +0 -22
- package/dist/@types/database/DatabaseObject.d.ts +0 -51
- package/dist/@types/database/GenericDatabase.d.ts +0 -4
- package/dist/@types/database/JourneyAPIAdapter.d.ts +0 -45
- package/dist/@types/database/ObjectDisplayScope.d.ts +0 -22
- package/dist/@types/database/OnlineDB.d.ts +0 -8
- package/dist/@types/database/adapters/BaseAdapter.d.ts +0 -26
- package/dist/@types/database/adapters/DatabaseAdapter.d.ts +0 -22
- package/dist/@types/database/adapters/WebSQLAdapter.d.ts +0 -50
- package/dist/@types/index.d.ts +0 -2
- package/dist/@types/query/InternalQuery.d.ts +0 -17
- package/dist/@types/query/Query.d.ts +0 -133
- package/dist/@types/query/getPreloadRelationships.d.ts +0 -9
- package/dist/@types/query/queryOperations.d.ts +0 -87
- package/dist/@types/types/Attachment.d.ts +0 -104
- package/dist/@types/types/Location.d.ts +0 -18
- package/dist/@types/types/ObjectData.d.ts +0 -12
- package/dist/@types/types/TypeOperations.d.ts +0 -42
- package/dist/@types/utils/FunctionQueue.d.ts +0 -32
- package/dist/@types/utils/JourneyPromise.d.ts +0 -2
- package/dist/@types/utils/base64.d.ts +0 -4
- package/dist/@types/utils/retryableFetch.d.ts +0 -3
- package/dist/Schema.js +0 -6
- package/dist/Schema.js.map +0 -1
- package/dist/app.js +0 -30
- package/dist/app.js.map +0 -1
- package/dist/browser.js +0 -77
- package/dist/browser.js.map +0 -1
- package/dist/credentials/ApiCredentials.js +0 -61
- package/dist/credentials/ApiCredentials.js.map +0 -1
- package/dist/credentials/MobileCredentials.js +0 -73
- package/dist/credentials/MobileCredentials.js.map +0 -1
- package/dist/credentials/index.js +0 -19
- package/dist/credentials/index.js.map +0 -1
- package/dist/database/Batch.js +0 -189
- package/dist/database/Batch.js.map +0 -1
- package/dist/database/Collection.js +0 -69
- package/dist/database/Collection.js.map +0 -1
- package/dist/database/Database.js +0 -47
- package/dist/database/Database.js.map +0 -1
- package/dist/database/DatabaseObject.js +0 -646
- package/dist/database/DatabaseObject.js.map +0 -1
- package/dist/database/GenericDatabase.js +0 -3
- package/dist/database/GenericDatabase.js.map +0 -1
- package/dist/database/JourneyAPIAdapter.js +0 -357
- package/dist/database/JourneyAPIAdapter.js.map +0 -1
- package/dist/database/ObjectDisplayScope.js +0 -117
- package/dist/database/ObjectDisplayScope.js.map +0 -1
- package/dist/database/OnlineDB.js +0 -16
- package/dist/database/OnlineDB.js.map +0 -1
- package/dist/database/adapters/BaseAdapter.js +0 -66
- package/dist/database/adapters/BaseAdapter.js.map +0 -1
- package/dist/database/adapters/DatabaseAdapter.js +0 -3
- package/dist/database/adapters/DatabaseAdapter.js.map +0 -1
- package/dist/database/adapters/WebSQLAdapter.js +0 -343
- package/dist/database/adapters/WebSQLAdapter.js.map +0 -1
- package/dist/index.js +0 -20
- package/dist/index.js.map +0 -1
- package/dist/query/InternalQuery.js +0 -3
- package/dist/query/InternalQuery.js.map +0 -1
- package/dist/query/Query.js +0 -414
- package/dist/query/Query.js.map +0 -1
- package/dist/query/getPreloadRelationships.js +0 -45
- package/dist/query/getPreloadRelationships.js.map +0 -1
- package/dist/query/queryOperations.js +0 -678
- package/dist/query/queryOperations.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/types/Attachment.js +0 -198
- package/dist/types/Attachment.js.map +0 -1
- package/dist/types/Location.js +0 -38
- package/dist/types/Location.js.map +0 -1
- package/dist/types/ObjectData.js +0 -3
- package/dist/types/ObjectData.js.map +0 -1
- package/dist/types/TypeOperations.js +0 -267
- package/dist/types/TypeOperations.js.map +0 -1
- package/dist/utils/FunctionQueue.js +0 -91
- package/dist/utils/FunctionQueue.js.map +0 -1
- package/dist/utils/JourneyPromise.js +0 -3
- package/dist/utils/JourneyPromise.js.map +0 -1
- package/dist/utils/base64.js +0 -40
- package/dist/utils/base64.js.map +0 -1
- package/dist/utils/retryableFetch.js +0 -57
- package/dist/utils/retryableFetch.js.map +0 -1
- package/readme.md +0 -37
|
@@ -1,678 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Parser = exports.Tokenizer = exports.TrueExpression = exports.OrExpression = exports.AndExpression = exports.RelationMatch = exports.Operation = void 0;
|
|
4
|
-
exports.expressionFromHash = expressionFromHash;
|
|
5
|
-
exports.parse = parse;
|
|
6
|
-
const TypeOperations_1 = require("../types/TypeOperations");
|
|
7
|
-
// # Query
|
|
8
|
-
// This module handles the parsing and representation of query expressions.
|
|
9
|
-
// It contains the logic to filter a single object based on an expression.
|
|
10
|
-
// It is up to the database adapters to perform optimised queries based on an index.
|
|
11
|
-
const parser_schema_1 = require("@ja-platform/parser-schema");
|
|
12
|
-
const parser_type_system_1 = require("@ja-platform/parser-type-system");
|
|
13
|
-
// Build an expression from a hash such as {make: 'Nokia', model: '5800'}
|
|
14
|
-
function expressionFromHash(scopeType, hash) {
|
|
15
|
-
let operations = [];
|
|
16
|
-
for (let key in hash) {
|
|
17
|
-
if (hash.hasOwnProperty(key)) {
|
|
18
|
-
let value = hash[key];
|
|
19
|
-
const attribute = scopeType.getAttribute(key);
|
|
20
|
-
if (attribute == null) {
|
|
21
|
-
throw new Error(`'${key}' is not defined on '${scopeType.name}'`);
|
|
22
|
-
}
|
|
23
|
-
if (parser_schema_1.SchemaModelType.isInstanceOf(attribute.type) && value != null) {
|
|
24
|
-
value = value.id;
|
|
25
|
-
}
|
|
26
|
-
const operation = new Operation(attribute, '=', value);
|
|
27
|
-
operations.push(operation);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return new AndExpression(operations);
|
|
31
|
-
}
|
|
32
|
-
// ## Operation
|
|
33
|
-
// An operation has an attribute (variable), operator and (constant) value to compare with.
|
|
34
|
-
function discardMillis(ms) {
|
|
35
|
-
return Math.floor(ms / 1000) * 1000;
|
|
36
|
-
}
|
|
37
|
-
class Operation {
|
|
38
|
-
constructor(attribute, operator, value) {
|
|
39
|
-
this.attribute = attribute;
|
|
40
|
-
this.operator = operator;
|
|
41
|
-
this.value = value;
|
|
42
|
-
}
|
|
43
|
-
normalize() {
|
|
44
|
-
if (this.operator == 'in') {
|
|
45
|
-
// Allows using this in indexes
|
|
46
|
-
return new OrExpression(this.value.map((v) => new AndExpression([new Operation(this.attribute, '=', v)])));
|
|
47
|
-
}
|
|
48
|
-
return new OrExpression([new AndExpression([this])]);
|
|
49
|
-
}
|
|
50
|
-
attributes() {
|
|
51
|
-
return [this.attribute];
|
|
52
|
-
}
|
|
53
|
-
evaluate(object) {
|
|
54
|
-
let attributeValue;
|
|
55
|
-
if (this.attribute.relationship) {
|
|
56
|
-
attributeValue = object.belongs_to[this.attribute.relationship];
|
|
57
|
-
if (typeof this.value == 'undefined') {
|
|
58
|
-
// Most likely an incorrect parameter was passed to the query.
|
|
59
|
-
// We don't want to interpret this the same as null.
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
else if (this.attribute.name == 'id') {
|
|
64
|
-
attributeValue = object.id;
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
const rawAttributeValue = object.attributes[this.attribute.name];
|
|
68
|
-
if (rawAttributeValue == null) {
|
|
69
|
-
attributeValue = null;
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
attributeValue = (0, TypeOperations_1.operationsFor)(this.attribute.type).valueFromJSON(rawAttributeValue);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
const compareDiff = compare(attributeValue, this.value);
|
|
76
|
-
if (this.operator == '=') {
|
|
77
|
-
if (compareDiff !== null) {
|
|
78
|
-
return compareDiff === 0;
|
|
79
|
-
}
|
|
80
|
-
if (Array.isArray(attributeValue) && !Array.isArray(this.value)) {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
return attributeValue == this.value;
|
|
84
|
-
}
|
|
85
|
-
else if (this.operator == '!=') {
|
|
86
|
-
if (compareDiff !== null) {
|
|
87
|
-
return compareDiff !== 0;
|
|
88
|
-
}
|
|
89
|
-
if (Array.isArray(attributeValue) && !Array.isArray(this.value)) {
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
return attributeValue != this.value;
|
|
93
|
-
}
|
|
94
|
-
else if (this.operator == '>') {
|
|
95
|
-
if (compareDiff === null) {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
return compareDiff > 0;
|
|
99
|
-
}
|
|
100
|
-
else if (this.operator == '<') {
|
|
101
|
-
if (compareDiff === null) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
return compareDiff < 0;
|
|
105
|
-
}
|
|
106
|
-
else if (this.operator == '>=') {
|
|
107
|
-
if (compareDiff === null) {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
return compareDiff >= 0;
|
|
111
|
-
}
|
|
112
|
-
else if (this.operator == '<=') {
|
|
113
|
-
if (compareDiff === null) {
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
return compareDiff <= 0;
|
|
117
|
-
}
|
|
118
|
-
else if (this.operator == 'contains') {
|
|
119
|
-
if (typeof attributeValue == 'string' && typeof this.value == 'string') {
|
|
120
|
-
return attributeValue.toLowerCase().indexOf(this.value.toLowerCase()) >= 0;
|
|
121
|
-
}
|
|
122
|
-
else if (Array.isArray(attributeValue) && Array.isArray(this.value)) {
|
|
123
|
-
for (let child of this.value) {
|
|
124
|
-
if (attributeValue.indexOf(child) < 0) {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
else if (Array.isArray(attributeValue)) {
|
|
131
|
-
return attributeValue.indexOf(this.value) >= 0;
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
else if (this.operator == 'starts with') {
|
|
138
|
-
if (typeof attributeValue == 'string' && typeof this.value == 'string') {
|
|
139
|
-
return attributeValue.toLowerCase().indexOf(this.value.toLowerCase()) === 0;
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
else if (this.operator == 'in') {
|
|
146
|
-
if (Array.isArray(this.value)) {
|
|
147
|
-
if (Array.isArray(attributeValue)) {
|
|
148
|
-
return (this.value.findIndex((value) => {
|
|
149
|
-
return attributeValue.indexOf(value) >= 0;
|
|
150
|
-
}) >= 0);
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
return (this.value.findIndex((value) => {
|
|
154
|
-
const compared = compare(attributeValue, value);
|
|
155
|
-
return compared == 0 || (compared == null && value == attributeValue);
|
|
156
|
-
}) >= 0);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
throw new Error("Array value is required for 'in' operator");
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
else if (this.operator == 'not in') {
|
|
164
|
-
if (Array.isArray(this.value)) {
|
|
165
|
-
if (Array.isArray(attributeValue)) {
|
|
166
|
-
return (this.value.findIndex((value) => {
|
|
167
|
-
return attributeValue.indexOf(value) >= 0;
|
|
168
|
-
}) == -1);
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
return (this.value.findIndex((value) => {
|
|
172
|
-
const compared = compare(attributeValue, value);
|
|
173
|
-
return compared == 0 || (compared == null && value == attributeValue);
|
|
174
|
-
}) == -1);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
throw new Error("Array value is required for 'not in' operator");
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
throw Error('Operator ' + this.operator + ' is not implemented yet');
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
toString() {
|
|
186
|
-
return this.attribute.name + ' ' + this.operator + ' ' + this.value;
|
|
187
|
-
}
|
|
188
|
-
toOriginalExpression() {
|
|
189
|
-
if (this.attribute.relationship) {
|
|
190
|
-
// Works for both relationships (user = ?) and relationship ids (user_id = ?)
|
|
191
|
-
return [this.attribute.relationship + '_id ' + this.operator + ' ?', this.value];
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
return [this.attribute.name + ' ' + this.operator + ' ?', this.value];
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
exports.Operation = Operation;
|
|
199
|
-
function compare(a, b) {
|
|
200
|
-
let compareDiff = null;
|
|
201
|
-
if (typeof a == 'number' && typeof b == 'number') {
|
|
202
|
-
compareDiff = a - b;
|
|
203
|
-
}
|
|
204
|
-
else if (typeof a == 'string' && typeof b == 'string') {
|
|
205
|
-
if (a < b) {
|
|
206
|
-
compareDiff = -1;
|
|
207
|
-
}
|
|
208
|
-
else if (a > b) {
|
|
209
|
-
compareDiff = 1;
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
compareDiff = 0;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
else if (a instanceof Date && b instanceof Date) {
|
|
216
|
-
// Compare seconds only
|
|
217
|
-
compareDiff = discardMillis(a.getTime()) - discardMillis(b.getTime());
|
|
218
|
-
}
|
|
219
|
-
else if (parser_schema_1.Day.isDay(a) || parser_schema_1.Day.isDay(b)) {
|
|
220
|
-
// New syntax only
|
|
221
|
-
try {
|
|
222
|
-
// Convert both to Days, in the local timezone.
|
|
223
|
-
// Also works for JSON strings.
|
|
224
|
-
compareDiff = new parser_schema_1.Day(a).valueOf() - new parser_schema_1.Day(b).valueOf();
|
|
225
|
-
}
|
|
226
|
-
catch (err) {
|
|
227
|
-
compareDiff = null;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
else if (Array.isArray(a) && Array.isArray(b)) {
|
|
231
|
-
if (a.length != b.length) {
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
for (let i = 0; i < a.length && i < b.length; i++) {
|
|
235
|
-
const child = compare(a[i], b[i]);
|
|
236
|
-
if (child != 0) {
|
|
237
|
-
return null;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return 0;
|
|
241
|
-
}
|
|
242
|
-
return compareDiff;
|
|
243
|
-
}
|
|
244
|
-
// ## RelationMatch
|
|
245
|
-
// A RelationMatch searches for a relationship match
|
|
246
|
-
class RelationMatch {
|
|
247
|
-
constructor(name, id) {
|
|
248
|
-
this.name = name;
|
|
249
|
-
this.id = id;
|
|
250
|
-
}
|
|
251
|
-
normalize() {
|
|
252
|
-
return new OrExpression([new AndExpression([this])]);
|
|
253
|
-
}
|
|
254
|
-
evaluate(object) {
|
|
255
|
-
const relatedId = object.belongs_to[this.name];
|
|
256
|
-
return relatedId == this.id;
|
|
257
|
-
}
|
|
258
|
-
toString() {
|
|
259
|
-
return this.name + ' = ' + this.id;
|
|
260
|
-
}
|
|
261
|
-
toOriginalExpression() {
|
|
262
|
-
return [this.name + '_id = ?', this.id];
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
exports.RelationMatch = RelationMatch;
|
|
266
|
-
// ## AndExpression
|
|
267
|
-
// Takes any number of expressions, and combine them with an "and".
|
|
268
|
-
class AndExpression {
|
|
269
|
-
constructor(operands) {
|
|
270
|
-
this.operands = operands;
|
|
271
|
-
}
|
|
272
|
-
join(otherAnd) {
|
|
273
|
-
const ops = this.operands.concat(otherAnd.operands);
|
|
274
|
-
return new AndExpression(ops);
|
|
275
|
-
}
|
|
276
|
-
normalize() {
|
|
277
|
-
let i;
|
|
278
|
-
// Step 1: filter out TrueExpression
|
|
279
|
-
let operands = [];
|
|
280
|
-
for (i = 0; i < this.operands.length; i++) {
|
|
281
|
-
const op = this.operands[i];
|
|
282
|
-
if (!(op instanceof TrueExpression)) {
|
|
283
|
-
operands.push(op);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
if (operands.length === 0) {
|
|
287
|
-
return new TrueExpression().normalize();
|
|
288
|
-
}
|
|
289
|
-
else if (operands.length == 1) {
|
|
290
|
-
return operands[0].normalize();
|
|
291
|
-
}
|
|
292
|
-
else if (operands.length > 2) {
|
|
293
|
-
const a = new AndExpression(operands.slice(0, 2));
|
|
294
|
-
const b = new AndExpression(operands.slice(2));
|
|
295
|
-
return new AndExpression([a, b]).normalize();
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
const left = operands[0].normalize().operands;
|
|
299
|
-
const right = operands[1].normalize().operands;
|
|
300
|
-
let newOperands = [];
|
|
301
|
-
for (i = 0; i < left.length; i++) {
|
|
302
|
-
for (let j = 0; j < right.length; j++) {
|
|
303
|
-
newOperands.push(left[i].join(right[j]));
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
return new OrExpression(newOperands);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
evaluate(object) {
|
|
310
|
-
for (let i = 0; i < this.operands.length; i++) {
|
|
311
|
-
const operand = this.operands[i];
|
|
312
|
-
if (!operand.evaluate(object)) {
|
|
313
|
-
return false;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
return true;
|
|
317
|
-
}
|
|
318
|
-
toString() {
|
|
319
|
-
let s = '(';
|
|
320
|
-
for (let i = 0; i < this.operands.length; i++) {
|
|
321
|
-
const operand = this.operands[i];
|
|
322
|
-
s += operand.toString();
|
|
323
|
-
if (i != this.operands.length - 1) {
|
|
324
|
-
s += ' and ';
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
s += ')';
|
|
328
|
-
return s;
|
|
329
|
-
}
|
|
330
|
-
toOriginalExpression() {
|
|
331
|
-
let args = [];
|
|
332
|
-
let s = '(';
|
|
333
|
-
for (let i = 0; i < this.operands.length; i++) {
|
|
334
|
-
const operand = this.operands[i];
|
|
335
|
-
if (operand instanceof TrueExpression)
|
|
336
|
-
continue;
|
|
337
|
-
let opPair = operand.toOriginalExpression();
|
|
338
|
-
if (opPair[0] == '') {
|
|
339
|
-
// nested TrueExpression
|
|
340
|
-
continue;
|
|
341
|
-
}
|
|
342
|
-
if (s.length > 1) {
|
|
343
|
-
// Not the first operand
|
|
344
|
-
s += ' and ';
|
|
345
|
-
}
|
|
346
|
-
s += opPair[0];
|
|
347
|
-
args.push.apply(args, opPair.slice(1));
|
|
348
|
-
}
|
|
349
|
-
s += ')';
|
|
350
|
-
if (s.length == 2) {
|
|
351
|
-
return new TrueExpression().toOriginalExpression();
|
|
352
|
-
}
|
|
353
|
-
return [s, ...args];
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
exports.AndExpression = AndExpression;
|
|
357
|
-
// ## OrExpression
|
|
358
|
-
// Takes any number of expressions, and combine them with an "or".
|
|
359
|
-
class OrExpression {
|
|
360
|
-
constructor(operands) {
|
|
361
|
-
this.operands = operands;
|
|
362
|
-
}
|
|
363
|
-
normalize() {
|
|
364
|
-
const newOperands = [];
|
|
365
|
-
this.operands.forEach(function (op) {
|
|
366
|
-
const norm = op.normalize();
|
|
367
|
-
norm.operands.forEach(function (subop) {
|
|
368
|
-
newOperands.push(subop);
|
|
369
|
-
});
|
|
370
|
-
});
|
|
371
|
-
return new OrExpression(newOperands);
|
|
372
|
-
}
|
|
373
|
-
evaluate(object) {
|
|
374
|
-
for (let i = 0; i < this.operands.length; i++) {
|
|
375
|
-
const operand = this.operands[i];
|
|
376
|
-
if (operand.evaluate(object)) {
|
|
377
|
-
return true;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
toString() {
|
|
383
|
-
let s = '(';
|
|
384
|
-
for (let i = 0; i < this.operands.length; i++) {
|
|
385
|
-
const operand = this.operands[i];
|
|
386
|
-
s += operand.toString();
|
|
387
|
-
if (i != this.operands.length - 1) {
|
|
388
|
-
s += ' or ';
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
s += ')';
|
|
392
|
-
return s;
|
|
393
|
-
}
|
|
394
|
-
toOriginalExpression() {
|
|
395
|
-
let args = [];
|
|
396
|
-
let s = '(';
|
|
397
|
-
for (let i = 0; i < this.operands.length; i++) {
|
|
398
|
-
const operand = this.operands[i];
|
|
399
|
-
if (operand instanceof TrueExpression) {
|
|
400
|
-
// One TrueExpression makes the entire OR clause true
|
|
401
|
-
return operand.toOriginalExpression();
|
|
402
|
-
}
|
|
403
|
-
const opPair = operand.toOriginalExpression();
|
|
404
|
-
s += opPair[0];
|
|
405
|
-
args.push.apply(args, opPair.slice(1));
|
|
406
|
-
if (i != this.operands.length - 1) {
|
|
407
|
-
s += ' or ';
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
s += ')';
|
|
411
|
-
return [s, ...args];
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
exports.OrExpression = OrExpression;
|
|
415
|
-
// ## TrueExpression
|
|
416
|
-
// Simple expression that is always true.
|
|
417
|
-
class TrueExpression {
|
|
418
|
-
constructor() { }
|
|
419
|
-
normalize() {
|
|
420
|
-
return new OrExpression([new AndExpression([this])]);
|
|
421
|
-
}
|
|
422
|
-
evaluate(object) {
|
|
423
|
-
return true;
|
|
424
|
-
}
|
|
425
|
-
toString() {
|
|
426
|
-
return 'true';
|
|
427
|
-
}
|
|
428
|
-
toOriginalExpression() {
|
|
429
|
-
return [''];
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
exports.TrueExpression = TrueExpression;
|
|
433
|
-
// We should probably support international characters as well
|
|
434
|
-
function isLetter(character) {
|
|
435
|
-
return (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z');
|
|
436
|
-
}
|
|
437
|
-
function isDigit(character) {
|
|
438
|
-
return character >= '0' && character <= '9';
|
|
439
|
-
}
|
|
440
|
-
const OPERATORS = ['>=', '<=', '=', '!=', '>', '<', 'starts with', 'contains', 'in', 'not in'];
|
|
441
|
-
const BOOLEAN_LOGIC = ['and', 'or'];
|
|
442
|
-
// ## Tokenizer
|
|
443
|
-
// The Tokenizer is responsible for interpreting the raw characters, and splitting the expression into
|
|
444
|
-
// tokens.
|
|
445
|
-
class Tokenizer {
|
|
446
|
-
constructor(expression) {
|
|
447
|
-
this.expression = expression;
|
|
448
|
-
this.i = 0;
|
|
449
|
-
this.interpolatorCount = 0;
|
|
450
|
-
if (expression == null) {
|
|
451
|
-
throw new Error('expression is required');
|
|
452
|
-
}
|
|
453
|
-
this.advance();
|
|
454
|
-
}
|
|
455
|
-
exception(message, start, end) {
|
|
456
|
-
let error = new Error(message + '\n' + this.expression + '\n' + new Array(this.i + 1).join(' ') + '^');
|
|
457
|
-
if (start == null) {
|
|
458
|
-
start = this.i;
|
|
459
|
-
}
|
|
460
|
-
error.position = start;
|
|
461
|
-
error.positionEnd = end == null ? start + 1 : end;
|
|
462
|
-
error.plainMessage = message;
|
|
463
|
-
return error;
|
|
464
|
-
}
|
|
465
|
-
isWordSeparator(p) {
|
|
466
|
-
return p >= this.expression.length || this.expression[p] == ' ';
|
|
467
|
-
}
|
|
468
|
-
isWord() {
|
|
469
|
-
this.checkBuffer();
|
|
470
|
-
return isLetter(this.expression[this.i]) || this.expression[this.i] == '_';
|
|
471
|
-
}
|
|
472
|
-
hasMore() {
|
|
473
|
-
return this.i < this.expression.length;
|
|
474
|
-
}
|
|
475
|
-
// Read an operator, for example "!=" or "starts with".
|
|
476
|
-
readOperator() {
|
|
477
|
-
return this.readToken(OPERATORS);
|
|
478
|
-
}
|
|
479
|
-
// Read an "and" or "or".
|
|
480
|
-
readBooleanLogic() {
|
|
481
|
-
return this.readToken(BOOLEAN_LOGIC);
|
|
482
|
-
}
|
|
483
|
-
// Is the current token an "or"?
|
|
484
|
-
isOr() {
|
|
485
|
-
return this.isToken(['or']);
|
|
486
|
-
}
|
|
487
|
-
// Is the current token an "and"?
|
|
488
|
-
isAnd() {
|
|
489
|
-
return this.isToken(['and']);
|
|
490
|
-
}
|
|
491
|
-
// Are we at a closing or opening parenthesis?
|
|
492
|
-
isParenthesis() {
|
|
493
|
-
this.checkBuffer();
|
|
494
|
-
return this.expression[this.i] == '(' || this.expression[this.i] == ')';
|
|
495
|
-
}
|
|
496
|
-
// Read a single parenthesis. Throws an exception if we are not at one.
|
|
497
|
-
readParenthesis() {
|
|
498
|
-
if (!this.isParenthesis()) {
|
|
499
|
-
throw this.exception('Expected ( or )');
|
|
500
|
-
}
|
|
501
|
-
const c = this.expression[this.i];
|
|
502
|
-
this.i++;
|
|
503
|
-
this.advance();
|
|
504
|
-
return c;
|
|
505
|
-
}
|
|
506
|
-
// Read an interpolation character "?"
|
|
507
|
-
readInterpolator() {
|
|
508
|
-
this.checkBuffer();
|
|
509
|
-
if (this.expression[this.i] == '?' && (this.isWordSeparator(this.i + 1) || this.expression[this.i + 1] == ')')) {
|
|
510
|
-
this.i += 1;
|
|
511
|
-
this.advance();
|
|
512
|
-
return this.interpolatorCount++;
|
|
513
|
-
}
|
|
514
|
-
else {
|
|
515
|
-
throw this.exception('Expected ?');
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
// Read a single alphanumeric word. May contain underscores.
|
|
519
|
-
readWord() {
|
|
520
|
-
this.checkBuffer();
|
|
521
|
-
let word = '';
|
|
522
|
-
word += this.expression[this.i];
|
|
523
|
-
let offset = 1;
|
|
524
|
-
while (this.expression.length > this.i + offset &&
|
|
525
|
-
(isLetter(this.expression[this.i + offset]) ||
|
|
526
|
-
isDigit(this.expression[this.i + offset]) ||
|
|
527
|
-
this.expression[this.i + offset] == '_')) {
|
|
528
|
-
word += this.expression[this.i + offset++];
|
|
529
|
-
}
|
|
530
|
-
this.i += offset;
|
|
531
|
-
this.advance();
|
|
532
|
-
return word;
|
|
533
|
-
}
|
|
534
|
-
// Advance to the next non-whitespace character.
|
|
535
|
-
advance() {
|
|
536
|
-
this.previousPosition = this.i - 1;
|
|
537
|
-
while (this.hasMore() && this.expression[this.i] == ' ') {
|
|
538
|
-
this.i++;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
checkBuffer() {
|
|
542
|
-
if (!this.hasMore()) {
|
|
543
|
-
throw this.exception('Unexpected end of expression');
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
// Given an array of tokens, read and return one of them at the current position.
|
|
547
|
-
// Throws an exception if none of them are found.
|
|
548
|
-
// Use isToken() first to check if one of the tokens is present.
|
|
549
|
-
readToken(tokens) {
|
|
550
|
-
this.checkBuffer();
|
|
551
|
-
for (let j = 0; j < tokens.length; j++) {
|
|
552
|
-
const token = tokens[j];
|
|
553
|
-
if (this.i + token.length <= this.expression.length) {
|
|
554
|
-
if (token == this.expression.substring(this.i, this.i + token.length)) {
|
|
555
|
-
this.i += token.length;
|
|
556
|
-
this.advance();
|
|
557
|
-
return token;
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
throw this.exception('Expected one of ' + JSON.stringify(tokens));
|
|
562
|
-
}
|
|
563
|
-
// Checks if one of the given array of tokens is present at the current position.
|
|
564
|
-
isToken(tokens) {
|
|
565
|
-
this.checkBuffer();
|
|
566
|
-
for (let j = 0; j < tokens.length; j++) {
|
|
567
|
-
const token = tokens[j];
|
|
568
|
-
if (this.i + token.length <= this.expression.length) {
|
|
569
|
-
if (token == this.expression.substring(this.i, this.i + token.length)) {
|
|
570
|
-
if (!isLetter(token[token.length - 1]) || this.isWordSeparator(this.i + token.length)) {
|
|
571
|
-
return true;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
return false;
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
exports.Tokenizer = Tokenizer;
|
|
580
|
-
// ## Parser
|
|
581
|
-
// The parser uses the tokenizer to build an Expression object.
|
|
582
|
-
class Parser {
|
|
583
|
-
constructor(scopeType, tokenizer, args) {
|
|
584
|
-
this.scopeType = scopeType;
|
|
585
|
-
this.tokenizer = tokenizer;
|
|
586
|
-
this.args = args;
|
|
587
|
-
if (args == null) {
|
|
588
|
-
this.args = [];
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
parse() {
|
|
592
|
-
const e = this.parseOrExpression();
|
|
593
|
-
if (this.tokenizer.hasMore()) {
|
|
594
|
-
throw this.tokenizer.exception('Invalid expression (expected expression to end)');
|
|
595
|
-
}
|
|
596
|
-
return e;
|
|
597
|
-
}
|
|
598
|
-
/*
|
|
599
|
-
expr : term ( ( AND | OR ) term ) *
|
|
600
|
-
term : ( attribute operator value ) | ( \( expression \) )
|
|
601
|
-
factor : NUMBER ;
|
|
602
|
-
*/
|
|
603
|
-
parseOrExpression() {
|
|
604
|
-
const e = this.parseAndExpression();
|
|
605
|
-
if (this.tokenizer.hasMore() && this.tokenizer.isOr()) {
|
|
606
|
-
let expressions = [e];
|
|
607
|
-
while (this.tokenizer.hasMore() && this.tokenizer.isOr()) {
|
|
608
|
-
this.tokenizer.readBooleanLogic();
|
|
609
|
-
expressions.push(this.parseAndExpression());
|
|
610
|
-
}
|
|
611
|
-
return new OrExpression(expressions);
|
|
612
|
-
}
|
|
613
|
-
else {
|
|
614
|
-
return e;
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
parseAndExpression() {
|
|
618
|
-
const e = this.parseTerm();
|
|
619
|
-
if (this.tokenizer.hasMore() && this.tokenizer.isAnd()) {
|
|
620
|
-
let expressions = [e];
|
|
621
|
-
while (this.tokenizer.hasMore() && this.tokenizer.isAnd()) {
|
|
622
|
-
this.tokenizer.readBooleanLogic();
|
|
623
|
-
expressions.push(this.parseTerm());
|
|
624
|
-
}
|
|
625
|
-
return new AndExpression(expressions);
|
|
626
|
-
}
|
|
627
|
-
else {
|
|
628
|
-
return e;
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
parseTerm() {
|
|
632
|
-
if (this.tokenizer.isWord()) {
|
|
633
|
-
let attributeStart = this.tokenizer.i;
|
|
634
|
-
const attributeName = this.tokenizer.readWord();
|
|
635
|
-
let attributeEnd = this.tokenizer.previousPosition + 1;
|
|
636
|
-
const operator = this.tokenizer.readOperator();
|
|
637
|
-
let interpolatorPosition = this.tokenizer.i;
|
|
638
|
-
const valueIndex = this.tokenizer.readInterpolator();
|
|
639
|
-
const attribute = this.scopeType.getAttribute(attributeName);
|
|
640
|
-
if (attribute == null) {
|
|
641
|
-
throw this.tokenizer.exception("'" + attributeName + "' is not a valid field of '" + this.scopeType.name + "'", attributeStart, attributeEnd);
|
|
642
|
-
}
|
|
643
|
-
else if (parser_type_system_1.ArrayType.isInstanceOf(attribute.type) || parser_schema_1.QueryType.isInstanceOf(attribute.type)) {
|
|
644
|
-
throw this.tokenizer.exception("Cannot query by has-many field '" + attributeName + "'", attributeStart, attributeEnd);
|
|
645
|
-
}
|
|
646
|
-
if (this.args.length > valueIndex) {
|
|
647
|
-
let value = this.args[valueIndex];
|
|
648
|
-
if (parser_schema_1.SchemaModelType.isInstanceOf(attribute.type) && value != null) {
|
|
649
|
-
value = value.id;
|
|
650
|
-
}
|
|
651
|
-
return new Operation(attribute, operator, value);
|
|
652
|
-
}
|
|
653
|
-
else {
|
|
654
|
-
throw this.tokenizer.exception('Not enough arguments to interpolate', interpolatorPosition);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
else if (this.tokenizer.isParenthesis()) {
|
|
658
|
-
let c = this.tokenizer.readParenthesis();
|
|
659
|
-
if (c != '(') {
|
|
660
|
-
throw this.tokenizer.exception('Expected (', this.tokenizer.previousPosition);
|
|
661
|
-
}
|
|
662
|
-
const e = this.parseOrExpression();
|
|
663
|
-
c = this.tokenizer.readParenthesis();
|
|
664
|
-
if (c != ')') {
|
|
665
|
-
throw this.tokenizer.exception('Expected )', this.tokenizer.previousPosition);
|
|
666
|
-
}
|
|
667
|
-
return e;
|
|
668
|
-
}
|
|
669
|
-
else {
|
|
670
|
-
throw this.tokenizer.exception('Expected attribute name or (');
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
exports.Parser = Parser;
|
|
675
|
-
function parse(scopeType, expression, args) {
|
|
676
|
-
return new Parser(scopeType, new Tokenizer(expression), args).parse();
|
|
677
|
-
}
|
|
678
|
-
//# sourceMappingURL=queryOperations.js.map
|