redis-om-type 1.0.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/CHANGELOG +168 -0
- package/LICENSE +22 -0
- package/README.md +1150 -0
- package/dist/index.d.ts +1398 -0
- package/dist/index.js +2047 -0
- package/docs/.nojekyll +1 -0
- package/docs/README.md +479 -0
- package/docs/classes/AbstractSearch.md +912 -0
- package/docs/classes/ArrayHashInput.md +198 -0
- package/docs/classes/Circle.md +375 -0
- package/docs/classes/Client.md +190 -0
- package/docs/classes/Field.md +254 -0
- package/docs/classes/FieldNotInSchema.md +198 -0
- package/docs/classes/InvalidHashInput.md +213 -0
- package/docs/classes/InvalidHashValue.md +228 -0
- package/docs/classes/InvalidInput.md +207 -0
- package/docs/classes/InvalidJsonInput.md +228 -0
- package/docs/classes/InvalidJsonValue.md +228 -0
- package/docs/classes/InvalidSchema.md +197 -0
- package/docs/classes/InvalidValue.md +203 -0
- package/docs/classes/NestedHashInput.md +198 -0
- package/docs/classes/NullJsonInput.md +228 -0
- package/docs/classes/NullJsonValue.md +228 -0
- package/docs/classes/PointOutOfRange.md +203 -0
- package/docs/classes/RawSearch.md +1063 -0
- package/docs/classes/RedisOmError.md +207 -0
- package/docs/classes/Repository.md +425 -0
- package/docs/classes/Schema.md +242 -0
- package/docs/classes/Search.md +1199 -0
- package/docs/classes/SearchError.md +201 -0
- package/docs/classes/SemanticSearchError.md +197 -0
- package/docs/classes/Where.md +43 -0
- package/docs/classes/WhereField.md +915 -0
- package/logo.svg +1 -0
- package/package.json +67 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2047 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
25
|
+
|
|
26
|
+
// lib/index.ts
|
|
27
|
+
var lib_exports = {};
|
|
28
|
+
__export(lib_exports, {
|
|
29
|
+
AbstractSearch: () => AbstractSearch,
|
|
30
|
+
ArrayHashInput: () => ArrayHashInput,
|
|
31
|
+
Circle: () => Circle,
|
|
32
|
+
Client: () => Client,
|
|
33
|
+
EntityId: () => EntityId,
|
|
34
|
+
EntityKeyName: () => EntityKeyName,
|
|
35
|
+
Field: () => Field,
|
|
36
|
+
FieldNotInSchema: () => FieldNotInSchema,
|
|
37
|
+
InvalidHashInput: () => InvalidHashInput,
|
|
38
|
+
InvalidHashValue: () => InvalidHashValue,
|
|
39
|
+
InvalidInput: () => InvalidInput,
|
|
40
|
+
InvalidJsonInput: () => InvalidJsonInput,
|
|
41
|
+
InvalidJsonValue: () => InvalidJsonValue,
|
|
42
|
+
InvalidSchema: () => InvalidSchema,
|
|
43
|
+
InvalidValue: () => InvalidValue,
|
|
44
|
+
NestedHashInput: () => NestedHashInput,
|
|
45
|
+
NullJsonInput: () => NullJsonInput,
|
|
46
|
+
NullJsonValue: () => NullJsonValue,
|
|
47
|
+
PointOutOfRange: () => PointOutOfRange,
|
|
48
|
+
RawSearch: () => RawSearch,
|
|
49
|
+
RedisOmError: () => RedisOmError,
|
|
50
|
+
Repository: () => Repository,
|
|
51
|
+
Schema: () => Schema,
|
|
52
|
+
Search: () => Search,
|
|
53
|
+
SearchError: () => SearchError,
|
|
54
|
+
SemanticSearchError: () => SemanticSearchError,
|
|
55
|
+
Where: () => Where,
|
|
56
|
+
WhereField: () => WhereField
|
|
57
|
+
});
|
|
58
|
+
module.exports = __toCommonJS(lib_exports);
|
|
59
|
+
|
|
60
|
+
// lib/client/client.ts
|
|
61
|
+
var import_redis2 = require("redis");
|
|
62
|
+
|
|
63
|
+
// lib/entity/entity.ts
|
|
64
|
+
var EntityId = Symbol("entityId");
|
|
65
|
+
var EntityKeyName = Symbol("entityKeyName");
|
|
66
|
+
|
|
67
|
+
// lib/indexer/index-builder.ts
|
|
68
|
+
var import_redis = require("redis");
|
|
69
|
+
var entryBuilders = { HASH: addHashEntry, JSON: addJsonEntry };
|
|
70
|
+
function buildRediSearchSchema(schema) {
|
|
71
|
+
const addEntry = entryBuilders[schema.dataStructure];
|
|
72
|
+
return schema.fields.reduce(addEntry, {});
|
|
73
|
+
}
|
|
74
|
+
function addHashEntry(schema, field) {
|
|
75
|
+
const hashField = field.hashField;
|
|
76
|
+
switch (field.type) {
|
|
77
|
+
case "boolean":
|
|
78
|
+
schema[hashField] = buildHashBoolean(field);
|
|
79
|
+
break;
|
|
80
|
+
case "date":
|
|
81
|
+
schema[hashField] = buildDateNumber(field);
|
|
82
|
+
break;
|
|
83
|
+
case "number":
|
|
84
|
+
schema[hashField] = buildDateNumber(field);
|
|
85
|
+
break;
|
|
86
|
+
case "point":
|
|
87
|
+
schema[hashField] = buildPoint(field);
|
|
88
|
+
break;
|
|
89
|
+
case "string[]":
|
|
90
|
+
case "string":
|
|
91
|
+
schema[hashField] = buildHashString(field);
|
|
92
|
+
break;
|
|
93
|
+
case "text":
|
|
94
|
+
schema[hashField] = buildText(field);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
return schema;
|
|
98
|
+
}
|
|
99
|
+
function addJsonEntry(schema, field) {
|
|
100
|
+
const jsonPath = field.jsonPath;
|
|
101
|
+
switch (field.type) {
|
|
102
|
+
case "boolean":
|
|
103
|
+
schema[jsonPath] = buildJsonBoolean(field);
|
|
104
|
+
break;
|
|
105
|
+
case "date":
|
|
106
|
+
schema[jsonPath] = buildDateNumber(field);
|
|
107
|
+
break;
|
|
108
|
+
case "number":
|
|
109
|
+
case "number[]":
|
|
110
|
+
schema[jsonPath] = buildDateNumber(field);
|
|
111
|
+
break;
|
|
112
|
+
case "point":
|
|
113
|
+
schema[jsonPath] = buildPoint(field);
|
|
114
|
+
break;
|
|
115
|
+
case "string":
|
|
116
|
+
case "string[]":
|
|
117
|
+
schema[jsonPath] = buildJsonString(field);
|
|
118
|
+
break;
|
|
119
|
+
case "text":
|
|
120
|
+
schema[jsonPath] = buildText(field);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
return schema;
|
|
124
|
+
}
|
|
125
|
+
function buildHashBoolean(field) {
|
|
126
|
+
const schemaField = { type: import_redis.SchemaFieldTypes.TAG, AS: field.name };
|
|
127
|
+
addSortable(schemaField, field);
|
|
128
|
+
addIndexed(schemaField, field);
|
|
129
|
+
return schemaField;
|
|
130
|
+
}
|
|
131
|
+
function buildJsonBoolean(field) {
|
|
132
|
+
if (field.sortable)
|
|
133
|
+
console.warn(`You have marked a boolean field as sortable but RediSearch doesn't support the SORTABLE argument on a TAG for JSON. Ignored.`);
|
|
134
|
+
const schemaField = { type: import_redis.SchemaFieldTypes.TAG, AS: field.name };
|
|
135
|
+
addIndexed(schemaField, field);
|
|
136
|
+
return schemaField;
|
|
137
|
+
}
|
|
138
|
+
function buildDateNumber(field) {
|
|
139
|
+
const schemaField = { type: import_redis.SchemaFieldTypes.NUMERIC, AS: field.name };
|
|
140
|
+
addSortable(schemaField, field);
|
|
141
|
+
addIndexed(schemaField, field);
|
|
142
|
+
return schemaField;
|
|
143
|
+
}
|
|
144
|
+
function buildPoint(field) {
|
|
145
|
+
const schemaField = { type: import_redis.SchemaFieldTypes.GEO, AS: field.name };
|
|
146
|
+
addIndexed(schemaField, field);
|
|
147
|
+
return schemaField;
|
|
148
|
+
}
|
|
149
|
+
function buildHashString(field) {
|
|
150
|
+
const schemaField = { type: import_redis.SchemaFieldTypes.TAG, AS: field.name };
|
|
151
|
+
addCaseInsensitive(schemaField, field), addSeparable(schemaField, field), addSortable(schemaField, field);
|
|
152
|
+
addIndexed(schemaField, field);
|
|
153
|
+
return schemaField;
|
|
154
|
+
}
|
|
155
|
+
function buildJsonString(field) {
|
|
156
|
+
if (field.sortable)
|
|
157
|
+
console.warn(`You have marked a ${field.type} field as sortable but RediSearch doesn't support the SORTABLE argument on a TAG for JSON. Ignored.`);
|
|
158
|
+
const schemaField = { type: import_redis.SchemaFieldTypes.TAG, AS: field.name };
|
|
159
|
+
addCaseInsensitive(schemaField, field), addSeparable(schemaField, field), addIndexed(schemaField, field);
|
|
160
|
+
return schemaField;
|
|
161
|
+
}
|
|
162
|
+
function buildText(field) {
|
|
163
|
+
const schemaField = { type: import_redis.SchemaFieldTypes.TEXT, AS: field.name };
|
|
164
|
+
addSortable(schemaField, field);
|
|
165
|
+
addStemming(schemaField, field);
|
|
166
|
+
addIndexed(schemaField, field);
|
|
167
|
+
addPhonetic(schemaField, field);
|
|
168
|
+
addWeight(schemaField, field);
|
|
169
|
+
return schemaField;
|
|
170
|
+
}
|
|
171
|
+
function addCaseInsensitive(schemaField, field) {
|
|
172
|
+
if (field.caseSensitive)
|
|
173
|
+
schemaField.CASESENSITIVE = true;
|
|
174
|
+
}
|
|
175
|
+
function addIndexed(schemaField, field) {
|
|
176
|
+
if (!field.indexed)
|
|
177
|
+
schemaField.NOINDEX = true;
|
|
178
|
+
}
|
|
179
|
+
function addStemming(schemaField, field) {
|
|
180
|
+
if (!field.stemming)
|
|
181
|
+
schemaField.NOSTEM = true;
|
|
182
|
+
}
|
|
183
|
+
function addPhonetic(schemaField, field) {
|
|
184
|
+
if (field.matcher)
|
|
185
|
+
schemaField.PHONETIC = field.matcher;
|
|
186
|
+
}
|
|
187
|
+
function addSeparable(schemaField, field) {
|
|
188
|
+
schemaField.SEPARATOR = field.separator;
|
|
189
|
+
}
|
|
190
|
+
function addSortable(schemaField, field) {
|
|
191
|
+
if (field.normalized) {
|
|
192
|
+
if (field.sortable)
|
|
193
|
+
schemaField.SORTABLE = true;
|
|
194
|
+
} else {
|
|
195
|
+
schemaField.SORTABLE = "UNF";
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function addWeight(schemaField, field) {
|
|
199
|
+
if (field.weight)
|
|
200
|
+
schemaField.WEIGHT = field.weight;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// lib/error/redis-om-error.ts
|
|
204
|
+
var RedisOmError = class extends Error {
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// lib/error/invalid-input.ts
|
|
208
|
+
var InvalidInput = class extends RedisOmError {
|
|
209
|
+
};
|
|
210
|
+
var NullJsonInput = class extends InvalidInput {
|
|
211
|
+
#field;
|
|
212
|
+
constructor(field) {
|
|
213
|
+
const message = `Null or undefined found in field '${field.name}' of type '${field.type}' in JSON at '${field.jsonPath}'.`;
|
|
214
|
+
super(message);
|
|
215
|
+
this.#field = field;
|
|
216
|
+
}
|
|
217
|
+
get fieldName() {
|
|
218
|
+
return this.#field.name;
|
|
219
|
+
}
|
|
220
|
+
get fieldType() {
|
|
221
|
+
return this.#field.type;
|
|
222
|
+
}
|
|
223
|
+
get jsonPath() {
|
|
224
|
+
return this.#field.jsonPath;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
var InvalidJsonInput = class extends InvalidInput {
|
|
228
|
+
#field;
|
|
229
|
+
constructor(field) {
|
|
230
|
+
const message = `Unexpected value for field '${field.name}' of type '${field.type}' in JSON at '${field.jsonPath}'.`;
|
|
231
|
+
super(message);
|
|
232
|
+
this.#field = field;
|
|
233
|
+
}
|
|
234
|
+
get fieldName() {
|
|
235
|
+
return this.#field.name;
|
|
236
|
+
}
|
|
237
|
+
get fieldType() {
|
|
238
|
+
return this.#field.type;
|
|
239
|
+
}
|
|
240
|
+
get jsonPath() {
|
|
241
|
+
return this.#field.jsonPath;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
var InvalidHashInput = class extends InvalidInput {
|
|
245
|
+
#field;
|
|
246
|
+
constructor(field) {
|
|
247
|
+
const message = `Unexpected value for field '${field.name}' of type '${field.type}' in Hash.`;
|
|
248
|
+
super(message);
|
|
249
|
+
this.#field = field;
|
|
250
|
+
}
|
|
251
|
+
get fieldName() {
|
|
252
|
+
return this.#field.name;
|
|
253
|
+
}
|
|
254
|
+
get fieldType() {
|
|
255
|
+
return this.#field.type;
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
var NestedHashInput = class extends InvalidInput {
|
|
259
|
+
#property;
|
|
260
|
+
constructor(property) {
|
|
261
|
+
const message = `Unexpected object in Hash at property '${property}'. You can not store a nested object in a Redis Hash.`;
|
|
262
|
+
super(message);
|
|
263
|
+
this.#property = property;
|
|
264
|
+
}
|
|
265
|
+
get field() {
|
|
266
|
+
return this.#property;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
var ArrayHashInput = class extends InvalidInput {
|
|
270
|
+
#property;
|
|
271
|
+
constructor(property) {
|
|
272
|
+
const message = `Unexpected array in Hash at property '${property}'. You can not store an array in a Redis Hash without defining it in the Schema.`;
|
|
273
|
+
super(message);
|
|
274
|
+
this.#property = property;
|
|
275
|
+
}
|
|
276
|
+
get field() {
|
|
277
|
+
return this.#property;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// lib/error/invalid-schema.ts
|
|
282
|
+
var InvalidSchema = class extends RedisOmError {
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// lib/error/invalid-value.ts
|
|
286
|
+
var InvalidValue = class extends RedisOmError {
|
|
287
|
+
};
|
|
288
|
+
var NullJsonValue = class extends InvalidValue {
|
|
289
|
+
#field;
|
|
290
|
+
constructor(field) {
|
|
291
|
+
const message = `Null or undefined found in field '${field.name}' of type '${field.type}' from JSON path '${field.jsonPath}' in Redis.`;
|
|
292
|
+
super(message);
|
|
293
|
+
this.#field = field;
|
|
294
|
+
}
|
|
295
|
+
get fieldName() {
|
|
296
|
+
return this.#field.name;
|
|
297
|
+
}
|
|
298
|
+
get fieldType() {
|
|
299
|
+
return this.#field.type;
|
|
300
|
+
}
|
|
301
|
+
get jsonPath() {
|
|
302
|
+
return this.#field.jsonPath;
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
var InvalidJsonValue = class extends InvalidValue {
|
|
306
|
+
#field;
|
|
307
|
+
constructor(field) {
|
|
308
|
+
const message = `Unexpected value for field '${field.name}' of type '${field.type}' from JSON path '${field.jsonPath}' in Redis.`;
|
|
309
|
+
super(message);
|
|
310
|
+
this.#field = field;
|
|
311
|
+
}
|
|
312
|
+
get fieldName() {
|
|
313
|
+
return this.#field.name;
|
|
314
|
+
}
|
|
315
|
+
get fieldType() {
|
|
316
|
+
return this.#field.type;
|
|
317
|
+
}
|
|
318
|
+
get jsonPath() {
|
|
319
|
+
return this.#field.jsonPath;
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
var InvalidHashValue = class extends InvalidValue {
|
|
323
|
+
#field;
|
|
324
|
+
constructor(field) {
|
|
325
|
+
const message = `Unexpected value for field '${field.name}' of type '${field.type}' from Hash field '${field.hashField}' read from Redis.`;
|
|
326
|
+
super(message);
|
|
327
|
+
this.#field = field;
|
|
328
|
+
}
|
|
329
|
+
get fieldName() {
|
|
330
|
+
return this.#field.name;
|
|
331
|
+
}
|
|
332
|
+
get fieldType() {
|
|
333
|
+
return this.#field.type;
|
|
334
|
+
}
|
|
335
|
+
get hashField() {
|
|
336
|
+
return this.#field.hashField;
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
// lib/error/point-out-of-range.ts
|
|
341
|
+
var PointOutOfRange = class extends RedisOmError {
|
|
342
|
+
#latitude;
|
|
343
|
+
#longitude;
|
|
344
|
+
constructor(point) {
|
|
345
|
+
super("Points must be between \xB185.05112878 latitude and \xB1180 longitude.");
|
|
346
|
+
this.#longitude = point.longitude;
|
|
347
|
+
this.#latitude = point.latitude;
|
|
348
|
+
}
|
|
349
|
+
get point() {
|
|
350
|
+
return { longitude: this.#longitude, latitude: this.#latitude };
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// lib/error/search-error.ts
|
|
355
|
+
var SearchError = class extends RedisOmError {
|
|
356
|
+
};
|
|
357
|
+
var SemanticSearchError = class extends SearchError {
|
|
358
|
+
};
|
|
359
|
+
var FieldNotInSchema = class extends SearchError {
|
|
360
|
+
#field;
|
|
361
|
+
constructor(fieldName) {
|
|
362
|
+
super(`The field '${fieldName}' is not part of the schema and thus cannot be used to search.`);
|
|
363
|
+
this.#field = fieldName;
|
|
364
|
+
}
|
|
365
|
+
get field() {
|
|
366
|
+
return this.#field;
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// lib/transformer/transformer-common.ts
|
|
371
|
+
var isNull = (value) => value === null;
|
|
372
|
+
var isDefined = (value) => value !== void 0;
|
|
373
|
+
var isUndefined = (value) => value === void 0;
|
|
374
|
+
var isNullish = (value) => value === void 0 || value === null;
|
|
375
|
+
var isNotNullish = (value) => value !== void 0 && value !== null;
|
|
376
|
+
var isBoolean = (value) => typeof value === "boolean";
|
|
377
|
+
var isNumber = (value) => typeof value === "number";
|
|
378
|
+
var isString = (value) => typeof value === "string";
|
|
379
|
+
var isDate = (value) => value instanceof Date;
|
|
380
|
+
var isDateString = (value) => isString(value) && !isNaN(new Date(value).getTime());
|
|
381
|
+
var isArray = (value) => Array.isArray(value);
|
|
382
|
+
var isObject = (value) => value !== null && typeof value === "object" && !isArray(value) && !isDate(value);
|
|
383
|
+
var isPoint = (value) => isObject(value) && Object.keys(value).length === 2 && typeof value.latitude === "number" && typeof value.longitude === "number";
|
|
384
|
+
var isNumberString = (value) => !isNaN(Number(value));
|
|
385
|
+
var isPointString = (value) => /^-?\d+(\.\d*)?,-?\d+(\.\d*)?$/.test(value);
|
|
386
|
+
var isValidPoint = (value) => Math.abs(value.latitude) <= 85.05112878 && Math.abs(value.longitude) <= 180;
|
|
387
|
+
var convertBooleanToString = (value) => value ? "1" : "0";
|
|
388
|
+
var convertNumberToString = (value) => value.toString();
|
|
389
|
+
var convertStringToNumber = (value) => Number.parseFloat(value);
|
|
390
|
+
var convertDateToEpoch = (value) => value.getTime() / 1e3;
|
|
391
|
+
var convertDateToString = (value) => convertDateToEpoch(value).toString();
|
|
392
|
+
var convertEpochDateToString = (value) => convertNumberToString(value);
|
|
393
|
+
var convertIsoDateToEpoch = (value) => convertDateToEpoch(new Date(value));
|
|
394
|
+
var convertIsoDateToString = (value) => convertDateToString(new Date(value));
|
|
395
|
+
var convertEpochStringToDate = (value) => new Date(convertEpochToDate(convertStringToNumber(value)));
|
|
396
|
+
var convertEpochToDate = (value) => new Date(value * 1e3);
|
|
397
|
+
var convertPointToString = (value) => {
|
|
398
|
+
if (isValidPoint(value))
|
|
399
|
+
return `${value.longitude},${value.latitude}`;
|
|
400
|
+
throw new PointOutOfRange(value);
|
|
401
|
+
};
|
|
402
|
+
var convertStringToPoint = (value) => {
|
|
403
|
+
const [longitude, latitude] = value.split(",").map(convertStringToNumber);
|
|
404
|
+
return { longitude, latitude };
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
// lib/transformer/from-hash-transformer.ts
|
|
408
|
+
function fromRedisHash(schema, hashData) {
|
|
409
|
+
const data = { ...hashData };
|
|
410
|
+
schema.fields.forEach((field) => {
|
|
411
|
+
if (field.hashField)
|
|
412
|
+
delete data[field.hashField];
|
|
413
|
+
const value = hashData[field.hashField];
|
|
414
|
+
if (isNotNullish(value)) {
|
|
415
|
+
data[field.name] = convertKnownValueFromString(field, value);
|
|
416
|
+
} else if (isNullish(value) && field.type === "string[]") {
|
|
417
|
+
data[field.name] = [];
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
return data;
|
|
421
|
+
}
|
|
422
|
+
function convertKnownValueFromString(field, value) {
|
|
423
|
+
switch (field.type) {
|
|
424
|
+
case "boolean":
|
|
425
|
+
if (value === "1")
|
|
426
|
+
return true;
|
|
427
|
+
if (value === "0")
|
|
428
|
+
return false;
|
|
429
|
+
throw new InvalidHashValue(field);
|
|
430
|
+
case "number":
|
|
431
|
+
if (isNumberString(value))
|
|
432
|
+
return convertStringToNumber(value);
|
|
433
|
+
throw new InvalidHashValue(field);
|
|
434
|
+
case "date":
|
|
435
|
+
if (isNumberString(value))
|
|
436
|
+
return convertEpochStringToDate(value);
|
|
437
|
+
throw new InvalidHashValue(field);
|
|
438
|
+
case "point":
|
|
439
|
+
if (isPointString(value))
|
|
440
|
+
return convertStringToPoint(value);
|
|
441
|
+
throw new InvalidHashValue(field);
|
|
442
|
+
case "string":
|
|
443
|
+
case "text":
|
|
444
|
+
return value;
|
|
445
|
+
case "string[]":
|
|
446
|
+
return convertStringToStringArray(value, field.separator);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
var convertStringToStringArray = (value, separator) => value.split(separator);
|
|
450
|
+
|
|
451
|
+
// lib/transformer/from-json-transformer.ts
|
|
452
|
+
var import_jsonpath_plus = require("jsonpath-plus");
|
|
453
|
+
var import_just_clone = __toESM(require("just-clone"));
|
|
454
|
+
function fromRedisJson(schema, json) {
|
|
455
|
+
const data = (0, import_just_clone.default)(json);
|
|
456
|
+
convertFromRedisJsonKnown(schema, data);
|
|
457
|
+
return data;
|
|
458
|
+
}
|
|
459
|
+
function convertFromRedisJsonKnown(schema, data) {
|
|
460
|
+
schema.fields.forEach((field) => {
|
|
461
|
+
const path = field.jsonPath;
|
|
462
|
+
const results = (0, import_jsonpath_plus.JSONPath)({ resultType: "all", path, json: data });
|
|
463
|
+
if (field.isArray) {
|
|
464
|
+
convertKnownResultsFromJson(field, results);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (results.length === 1) {
|
|
468
|
+
convertKnownResultFromJson(field, results[0]);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (results.length > 1)
|
|
472
|
+
throw new InvalidJsonValue(field);
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
function convertKnownResultFromJson(field, result) {
|
|
476
|
+
const { value, parent, parentProperty } = result;
|
|
477
|
+
parent[parentProperty] = convertKnownValueFromJson(field, value);
|
|
478
|
+
}
|
|
479
|
+
function convertKnownResultsFromJson(field, results) {
|
|
480
|
+
results.forEach((result) => {
|
|
481
|
+
const { value, parent, parentProperty } = result;
|
|
482
|
+
parent[parentProperty] = convertKnownArrayValueFromJson(field, value);
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
function convertKnownValueFromJson(field, value) {
|
|
486
|
+
if (isNull(value))
|
|
487
|
+
return value;
|
|
488
|
+
switch (field.type) {
|
|
489
|
+
case "boolean":
|
|
490
|
+
if (isBoolean(value))
|
|
491
|
+
return value;
|
|
492
|
+
throw new InvalidJsonValue(field);
|
|
493
|
+
case "number":
|
|
494
|
+
if (isNumber(value))
|
|
495
|
+
return value;
|
|
496
|
+
throw new InvalidJsonValue(field);
|
|
497
|
+
case "date":
|
|
498
|
+
if (isNumber(value))
|
|
499
|
+
return convertEpochToDate(value);
|
|
500
|
+
throw new InvalidJsonValue(field);
|
|
501
|
+
case "point":
|
|
502
|
+
if (isPointString(value))
|
|
503
|
+
return convertStringToPoint(value);
|
|
504
|
+
throw new InvalidJsonValue(field);
|
|
505
|
+
case "string":
|
|
506
|
+
case "text":
|
|
507
|
+
if (isBoolean(value))
|
|
508
|
+
return value.toString();
|
|
509
|
+
if (isNumber(value))
|
|
510
|
+
return value.toString();
|
|
511
|
+
if (isString(value))
|
|
512
|
+
return value;
|
|
513
|
+
throw new InvalidJsonValue(field);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function convertKnownArrayValueFromJson(field, value) {
|
|
517
|
+
if (isNull(value))
|
|
518
|
+
throw new NullJsonValue(field);
|
|
519
|
+
switch (field.type) {
|
|
520
|
+
case "string[]":
|
|
521
|
+
if (isBoolean(value))
|
|
522
|
+
return value.toString();
|
|
523
|
+
if (isNumber(value))
|
|
524
|
+
return value.toString();
|
|
525
|
+
if (isString(value))
|
|
526
|
+
return value;
|
|
527
|
+
throw new InvalidJsonValue(field);
|
|
528
|
+
case "number[]":
|
|
529
|
+
if (isNumber(value))
|
|
530
|
+
return value;
|
|
531
|
+
throw new InvalidJsonValue(field);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// lib/transformer/to-hash-transformer.ts
|
|
536
|
+
function toRedisHash(schema, data) {
|
|
537
|
+
const hashData = {};
|
|
538
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
539
|
+
if (isNotNullish(value)) {
|
|
540
|
+
const field = schema.fieldByName(key);
|
|
541
|
+
const hashField = field ? field.hashField : key;
|
|
542
|
+
if (field && field.type === "string[]" && isArray(value) && value.length === 0) {
|
|
543
|
+
} else {
|
|
544
|
+
hashData[hashField] = field ? convertKnownValueToString2(field, value) : convertUnknownValueToString(key, value);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
return hashData;
|
|
549
|
+
}
|
|
550
|
+
function convertKnownValueToString2(field, value) {
|
|
551
|
+
switch (field.type) {
|
|
552
|
+
case "boolean":
|
|
553
|
+
if (isBoolean(value))
|
|
554
|
+
return convertBooleanToString(value);
|
|
555
|
+
throw new InvalidHashInput(field);
|
|
556
|
+
case "number":
|
|
557
|
+
if (isNumber(value))
|
|
558
|
+
return convertNumberToString(value);
|
|
559
|
+
throw new InvalidHashInput(field);
|
|
560
|
+
case "date":
|
|
561
|
+
if (isNumber(value))
|
|
562
|
+
return convertEpochDateToString(value);
|
|
563
|
+
if (isDate(value))
|
|
564
|
+
return convertDateToString(value);
|
|
565
|
+
if (isDateString(value))
|
|
566
|
+
return convertIsoDateToString(value);
|
|
567
|
+
throw new InvalidHashInput(field);
|
|
568
|
+
case "point":
|
|
569
|
+
if (isPoint(value))
|
|
570
|
+
return convertPointToString(value);
|
|
571
|
+
throw new InvalidHashInput(field);
|
|
572
|
+
case "string":
|
|
573
|
+
case "text":
|
|
574
|
+
if (isBoolean(value))
|
|
575
|
+
return value.toString();
|
|
576
|
+
if (isNumber(value))
|
|
577
|
+
return value.toString();
|
|
578
|
+
if (isString(value))
|
|
579
|
+
return value;
|
|
580
|
+
throw new InvalidHashInput(field);
|
|
581
|
+
case "string[]":
|
|
582
|
+
if (isArray(value))
|
|
583
|
+
return convertStringArrayToString(value, field.separator);
|
|
584
|
+
throw new InvalidHashInput(field);
|
|
585
|
+
default:
|
|
586
|
+
throw new RedisOmError(`Expected a valid field type but received: ${field.type}`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
function convertUnknownValueToString(key, value) {
|
|
590
|
+
if (isBoolean(value))
|
|
591
|
+
return convertBooleanToString(value);
|
|
592
|
+
if (isNumber(value))
|
|
593
|
+
return convertNumberToString(value);
|
|
594
|
+
if (isDate(value))
|
|
595
|
+
return convertDateToString(value);
|
|
596
|
+
if (isPoint(value))
|
|
597
|
+
return convertPointToString(value);
|
|
598
|
+
if (isArray(value))
|
|
599
|
+
throw new ArrayHashInput(key);
|
|
600
|
+
if (isObject(value))
|
|
601
|
+
throw new NestedHashInput(key);
|
|
602
|
+
return value.toString();
|
|
603
|
+
}
|
|
604
|
+
var convertStringArrayToString = (value, separator) => value.join(separator);
|
|
605
|
+
|
|
606
|
+
// lib/transformer/to-json-transformer.ts
|
|
607
|
+
var import_jsonpath_plus2 = require("jsonpath-plus");
|
|
608
|
+
var import_just_clone2 = __toESM(require("just-clone"));
|
|
609
|
+
function toRedisJson(schema, data) {
|
|
610
|
+
let json = (0, import_just_clone2.default)(data);
|
|
611
|
+
convertToRedisJsonKnown(schema, json);
|
|
612
|
+
return convertToRedisJsonUnknown(json);
|
|
613
|
+
}
|
|
614
|
+
function convertToRedisJsonKnown(schema, json) {
|
|
615
|
+
schema.fields.forEach((field) => {
|
|
616
|
+
const results = (0, import_jsonpath_plus2.JSONPath)({ resultType: "all", path: field.jsonPath, json });
|
|
617
|
+
if (field.isArray) {
|
|
618
|
+
convertKnownResultsToJson(field, results);
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
if (results.length === 0)
|
|
622
|
+
return;
|
|
623
|
+
if (results.length === 1) {
|
|
624
|
+
convertKnownResultToJson(field, results[0]);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
throw new InvalidJsonInput(field);
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
function convertToRedisJsonUnknown(json) {
|
|
631
|
+
Object.entries(json).forEach(([key, value]) => {
|
|
632
|
+
if (isUndefined(value)) {
|
|
633
|
+
delete json[key];
|
|
634
|
+
} else if (isObject(value)) {
|
|
635
|
+
json[key] = convertToRedisJsonUnknown(value);
|
|
636
|
+
} else {
|
|
637
|
+
json[key] = convertUnknownValueToJson(value);
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
return json;
|
|
641
|
+
}
|
|
642
|
+
function convertKnownResultToJson(field, result) {
|
|
643
|
+
const { value, parent, parentProperty } = result;
|
|
644
|
+
if (isDefined(value))
|
|
645
|
+
parent[parentProperty] = convertKnownValueToJson(field, value);
|
|
646
|
+
}
|
|
647
|
+
function convertKnownResultsToJson(field, results) {
|
|
648
|
+
results.forEach((result) => {
|
|
649
|
+
const { value, parent, parentProperty } = result;
|
|
650
|
+
if (isNull(value))
|
|
651
|
+
throw new NullJsonInput(field);
|
|
652
|
+
if (isUndefined(value) && isArray(parent))
|
|
653
|
+
throw new NullJsonInput(field);
|
|
654
|
+
if (isDefined(value))
|
|
655
|
+
parent[parentProperty] = convertKnownArrayValueToJson(field, value);
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
function convertKnownValueToJson(field, value) {
|
|
659
|
+
if (isNull(value))
|
|
660
|
+
return value;
|
|
661
|
+
switch (field.type) {
|
|
662
|
+
case "boolean":
|
|
663
|
+
if (isBoolean(value))
|
|
664
|
+
return value;
|
|
665
|
+
throw new InvalidJsonInput(field);
|
|
666
|
+
case "number":
|
|
667
|
+
if (isNumber(value))
|
|
668
|
+
return value;
|
|
669
|
+
throw new InvalidJsonInput(field);
|
|
670
|
+
case "date":
|
|
671
|
+
if (isNumber(value))
|
|
672
|
+
return value;
|
|
673
|
+
if (isDate(value))
|
|
674
|
+
return convertDateToEpoch(value);
|
|
675
|
+
if (isDateString(value))
|
|
676
|
+
return convertIsoDateToEpoch(value);
|
|
677
|
+
throw new InvalidJsonInput(field);
|
|
678
|
+
case "point":
|
|
679
|
+
if (isPoint(value))
|
|
680
|
+
return convertPointToString(value);
|
|
681
|
+
throw new InvalidJsonInput(field);
|
|
682
|
+
case "string":
|
|
683
|
+
case "text":
|
|
684
|
+
if (isBoolean(value))
|
|
685
|
+
return value.toString();
|
|
686
|
+
if (isNumber(value))
|
|
687
|
+
return value.toString();
|
|
688
|
+
if (isString(value))
|
|
689
|
+
return value;
|
|
690
|
+
throw new InvalidJsonInput(field);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
function convertKnownArrayValueToJson(field, value) {
|
|
694
|
+
switch (field.type) {
|
|
695
|
+
case "string[]":
|
|
696
|
+
if (isBoolean(value))
|
|
697
|
+
return value.toString();
|
|
698
|
+
if (isNumber(value))
|
|
699
|
+
return value.toString();
|
|
700
|
+
if (isString(value))
|
|
701
|
+
return value;
|
|
702
|
+
throw new InvalidJsonInput(field);
|
|
703
|
+
case "number[]":
|
|
704
|
+
if (isNumber(value))
|
|
705
|
+
return value;
|
|
706
|
+
throw new InvalidJsonInput(field);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function convertUnknownValueToJson(value) {
|
|
710
|
+
if (isDate(value))
|
|
711
|
+
return convertDateToEpoch(value);
|
|
712
|
+
return value;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// lib/search/results-converter.ts
|
|
716
|
+
function extractCountFromSearchResults(results) {
|
|
717
|
+
return results.total;
|
|
718
|
+
}
|
|
719
|
+
function extractKeyNamesFromSearchResults(results) {
|
|
720
|
+
return results.documents.map((document) => document.id);
|
|
721
|
+
}
|
|
722
|
+
function extractEntityIdsFromSearchResults(schema, results) {
|
|
723
|
+
const keyNames = extractKeyNamesFromSearchResults(results);
|
|
724
|
+
return keyNamesToEntityIds(schema.schemaName, keyNames);
|
|
725
|
+
}
|
|
726
|
+
function extractEntitiesFromSearchResults(schema, results) {
|
|
727
|
+
if (schema.dataStructure === "HASH") {
|
|
728
|
+
return results.documents.map((document) => hashDocumentToEntity(schema, document));
|
|
729
|
+
} else {
|
|
730
|
+
return results.documents.map((document) => jsonDocumentToEntity(schema, document));
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
function hashDocumentToEntity(schema, document) {
|
|
734
|
+
const keyName = document.id;
|
|
735
|
+
const hashData = document.value;
|
|
736
|
+
const entityData = fromRedisHash(schema, hashData);
|
|
737
|
+
return enrichEntityData(schema.schemaName, keyName, entityData);
|
|
738
|
+
}
|
|
739
|
+
function jsonDocumentToEntity(schema, document) {
|
|
740
|
+
const keyName = document.id;
|
|
741
|
+
const jsonData = document.value["$"] ?? false ? JSON.parse(document.value["$"]) : document.value;
|
|
742
|
+
const entityData = fromRedisJson(schema, jsonData);
|
|
743
|
+
return enrichEntityData(schema.schemaName, keyName, entityData);
|
|
744
|
+
}
|
|
745
|
+
function enrichEntityData(keyPrefix, keyName, entityData) {
|
|
746
|
+
const entityId = keyNameToEntityId(keyPrefix, keyName);
|
|
747
|
+
return { ...entityData, [EntityId]: entityId, [EntityKeyName]: keyName };
|
|
748
|
+
}
|
|
749
|
+
function keyNamesToEntityIds(keyPrefix, keyNames) {
|
|
750
|
+
return keyNames.map((keyName) => keyNameToEntityId(keyPrefix, keyName));
|
|
751
|
+
}
|
|
752
|
+
function keyNameToEntityId(keyPrefix, keyName) {
|
|
753
|
+
const escapedPrefix = keyPrefix.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
754
|
+
const regex = new RegExp(`^${escapedPrefix}:`);
|
|
755
|
+
return keyName.replace(regex, "");
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// lib/search/where.ts
|
|
759
|
+
var Where = class {
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
// lib/search/where-and.ts
|
|
763
|
+
var WhereAnd = class extends Where {
|
|
764
|
+
left;
|
|
765
|
+
right;
|
|
766
|
+
constructor(left, right) {
|
|
767
|
+
super();
|
|
768
|
+
this.left = left;
|
|
769
|
+
this.right = right;
|
|
770
|
+
}
|
|
771
|
+
toString() {
|
|
772
|
+
return `( ${this.left.toString()} ${this.right.toString()} )`;
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
// lib/search/where-or.ts
|
|
777
|
+
var WhereOr = class extends Where {
|
|
778
|
+
left;
|
|
779
|
+
right;
|
|
780
|
+
constructor(left, right) {
|
|
781
|
+
super();
|
|
782
|
+
this.left = left;
|
|
783
|
+
this.right = right;
|
|
784
|
+
}
|
|
785
|
+
toString() {
|
|
786
|
+
return `( ${this.left.toString()} | ${this.right.toString()} )`;
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
// lib/search/where-field.ts
|
|
791
|
+
var WhereField = class {
|
|
792
|
+
negated = false;
|
|
793
|
+
search;
|
|
794
|
+
field;
|
|
795
|
+
constructor(search, field) {
|
|
796
|
+
this.search = search;
|
|
797
|
+
this.field = field;
|
|
798
|
+
}
|
|
799
|
+
get is() {
|
|
800
|
+
return this;
|
|
801
|
+
}
|
|
802
|
+
get does() {
|
|
803
|
+
return this;
|
|
804
|
+
}
|
|
805
|
+
get not() {
|
|
806
|
+
this.negate();
|
|
807
|
+
return this;
|
|
808
|
+
}
|
|
809
|
+
negate() {
|
|
810
|
+
this.negated = !this.negated;
|
|
811
|
+
}
|
|
812
|
+
buildQuery(valuePortion) {
|
|
813
|
+
const negationPortion = this.negated ? "-" : "";
|
|
814
|
+
const fieldPortion = this.escapePunctuationAndSpaces(this.field.name);
|
|
815
|
+
return `(${negationPortion}@${fieldPortion}:${valuePortion})`;
|
|
816
|
+
}
|
|
817
|
+
escapePunctuation(value) {
|
|
818
|
+
const matchPunctuation = /[,.?<>{}[\]"':;!@#$%^&()\-+=~|/\\]/g;
|
|
819
|
+
return value.replace(matchPunctuation, "\\$&");
|
|
820
|
+
}
|
|
821
|
+
escapePunctuationAndSpaces(value) {
|
|
822
|
+
const matchPunctuation = /[,.?<>{}[\]"':;!@#$%^&()\-+=~|/\\ ]/g;
|
|
823
|
+
return value.replace(matchPunctuation, "\\$&");
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
// lib/search/where-string-array.ts
|
|
828
|
+
var WhereStringArray = class extends WhereField {
|
|
829
|
+
value;
|
|
830
|
+
contain(value) {
|
|
831
|
+
this.value = [value];
|
|
832
|
+
return this.search;
|
|
833
|
+
}
|
|
834
|
+
contains(value) {
|
|
835
|
+
return this.contain(value);
|
|
836
|
+
}
|
|
837
|
+
containsOneOf(...value) {
|
|
838
|
+
this.value = value;
|
|
839
|
+
return this.search;
|
|
840
|
+
}
|
|
841
|
+
containOneOf(...value) {
|
|
842
|
+
return this.containsOneOf(...value);
|
|
843
|
+
}
|
|
844
|
+
toString() {
|
|
845
|
+
const escapedValue = this.value.map((s) => this.escapePunctuationAndSpaces(s)).join("|");
|
|
846
|
+
return this.buildQuery(`{${escapedValue}}`);
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
// lib/search/where-boolean.ts
|
|
851
|
+
var WhereBoolean = class extends WhereField {
|
|
852
|
+
value;
|
|
853
|
+
eq(value) {
|
|
854
|
+
this.value = value;
|
|
855
|
+
return this.search;
|
|
856
|
+
}
|
|
857
|
+
equal(value) {
|
|
858
|
+
return this.eq(value);
|
|
859
|
+
}
|
|
860
|
+
equals(value) {
|
|
861
|
+
return this.eq(value);
|
|
862
|
+
}
|
|
863
|
+
equalTo(value) {
|
|
864
|
+
return this.eq(value);
|
|
865
|
+
}
|
|
866
|
+
true() {
|
|
867
|
+
return this.eq(true);
|
|
868
|
+
}
|
|
869
|
+
false() {
|
|
870
|
+
return this.eq(false);
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
var WhereHashBoolean = class extends WhereBoolean {
|
|
874
|
+
toString() {
|
|
875
|
+
return this.buildQuery(`{${this.value ? "1" : "0"}}`);
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
var WhereJsonBoolean = class extends WhereBoolean {
|
|
879
|
+
toString() {
|
|
880
|
+
return this.buildQuery(`{${this.value}}`);
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
// lib/search/where-number.ts
|
|
885
|
+
var WhereNumber = class extends WhereField {
|
|
886
|
+
lower = Number.NEGATIVE_INFINITY;
|
|
887
|
+
upper = Number.POSITIVE_INFINITY;
|
|
888
|
+
lowerExclusive = false;
|
|
889
|
+
upperExclusive = false;
|
|
890
|
+
eq(value) {
|
|
891
|
+
this.lower = value;
|
|
892
|
+
this.upper = value;
|
|
893
|
+
return this.search;
|
|
894
|
+
}
|
|
895
|
+
gt(value) {
|
|
896
|
+
this.lower = value;
|
|
897
|
+
this.lowerExclusive = true;
|
|
898
|
+
return this.search;
|
|
899
|
+
}
|
|
900
|
+
gte(value) {
|
|
901
|
+
this.lower = value;
|
|
902
|
+
return this.search;
|
|
903
|
+
}
|
|
904
|
+
lt(value) {
|
|
905
|
+
this.upper = value;
|
|
906
|
+
this.upperExclusive = true;
|
|
907
|
+
return this.search;
|
|
908
|
+
}
|
|
909
|
+
lte(value) {
|
|
910
|
+
this.upper = value;
|
|
911
|
+
return this.search;
|
|
912
|
+
}
|
|
913
|
+
between(lower, upper) {
|
|
914
|
+
this.lower = lower;
|
|
915
|
+
this.upper = upper;
|
|
916
|
+
return this.search;
|
|
917
|
+
}
|
|
918
|
+
equal(value) {
|
|
919
|
+
return this.eq(value);
|
|
920
|
+
}
|
|
921
|
+
equals(value) {
|
|
922
|
+
return this.eq(value);
|
|
923
|
+
}
|
|
924
|
+
equalTo(value) {
|
|
925
|
+
return this.eq(value);
|
|
926
|
+
}
|
|
927
|
+
greaterThan(value) {
|
|
928
|
+
return this.gt(value);
|
|
929
|
+
}
|
|
930
|
+
greaterThanOrEqualTo(value) {
|
|
931
|
+
return this.gte(value);
|
|
932
|
+
}
|
|
933
|
+
lessThan(value) {
|
|
934
|
+
return this.lt(value);
|
|
935
|
+
}
|
|
936
|
+
lessThanOrEqualTo(value) {
|
|
937
|
+
return this.lte(value);
|
|
938
|
+
}
|
|
939
|
+
toString() {
|
|
940
|
+
const lower = this.makeLowerString();
|
|
941
|
+
const upper = this.makeUpperString();
|
|
942
|
+
return this.buildQuery(`[${lower} ${upper}]`);
|
|
943
|
+
}
|
|
944
|
+
makeLowerString() {
|
|
945
|
+
if (this.lower === Number.NEGATIVE_INFINITY)
|
|
946
|
+
return "-inf";
|
|
947
|
+
if (this.lowerExclusive)
|
|
948
|
+
return `(${this.lower}`;
|
|
949
|
+
return this.lower.toString();
|
|
950
|
+
}
|
|
951
|
+
makeUpperString() {
|
|
952
|
+
if (this.upper === Number.POSITIVE_INFINITY)
|
|
953
|
+
return "+inf";
|
|
954
|
+
if (this.upperExclusive)
|
|
955
|
+
return `(${this.upper}`;
|
|
956
|
+
return this.upper.toString();
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
// lib/search/where-point.ts
|
|
961
|
+
var Circle = class {
|
|
962
|
+
longitudeOfOrigin = 0;
|
|
963
|
+
latitudeOfOrigin = 0;
|
|
964
|
+
size = 1;
|
|
965
|
+
units = "m";
|
|
966
|
+
longitude(value) {
|
|
967
|
+
this.longitudeOfOrigin = value;
|
|
968
|
+
return this;
|
|
969
|
+
}
|
|
970
|
+
latitude(value) {
|
|
971
|
+
this.latitudeOfOrigin = value;
|
|
972
|
+
return this;
|
|
973
|
+
}
|
|
974
|
+
origin(pointOrLongitude, latitude) {
|
|
975
|
+
if (typeof pointOrLongitude === "number" && latitude !== void 0) {
|
|
976
|
+
this.longitudeOfOrigin = pointOrLongitude;
|
|
977
|
+
this.latitudeOfOrigin = latitude;
|
|
978
|
+
} else {
|
|
979
|
+
const point = pointOrLongitude;
|
|
980
|
+
this.longitudeOfOrigin = point.longitude;
|
|
981
|
+
this.latitudeOfOrigin = point.latitude;
|
|
982
|
+
}
|
|
983
|
+
return this;
|
|
984
|
+
}
|
|
985
|
+
radius(size) {
|
|
986
|
+
this.size = size;
|
|
987
|
+
return this;
|
|
988
|
+
}
|
|
989
|
+
get m() {
|
|
990
|
+
return this.meters;
|
|
991
|
+
}
|
|
992
|
+
get meter() {
|
|
993
|
+
return this.meters;
|
|
994
|
+
}
|
|
995
|
+
get meters() {
|
|
996
|
+
this.units = "m";
|
|
997
|
+
return this;
|
|
998
|
+
}
|
|
999
|
+
get km() {
|
|
1000
|
+
return this.kilometers;
|
|
1001
|
+
}
|
|
1002
|
+
get kilometer() {
|
|
1003
|
+
return this.kilometers;
|
|
1004
|
+
}
|
|
1005
|
+
get kilometers() {
|
|
1006
|
+
this.units = "km";
|
|
1007
|
+
return this;
|
|
1008
|
+
}
|
|
1009
|
+
get ft() {
|
|
1010
|
+
return this.feet;
|
|
1011
|
+
}
|
|
1012
|
+
get foot() {
|
|
1013
|
+
return this.feet;
|
|
1014
|
+
}
|
|
1015
|
+
get feet() {
|
|
1016
|
+
this.units = "ft";
|
|
1017
|
+
return this;
|
|
1018
|
+
}
|
|
1019
|
+
get mi() {
|
|
1020
|
+
return this.miles;
|
|
1021
|
+
}
|
|
1022
|
+
get mile() {
|
|
1023
|
+
return this.miles;
|
|
1024
|
+
}
|
|
1025
|
+
get miles() {
|
|
1026
|
+
this.units = "mi";
|
|
1027
|
+
return this;
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
var WherePoint = class extends WhereField {
|
|
1031
|
+
circle = new Circle();
|
|
1032
|
+
inRadius(circleFn) {
|
|
1033
|
+
return this.inCircle(circleFn);
|
|
1034
|
+
}
|
|
1035
|
+
inCircle(circleFn) {
|
|
1036
|
+
this.circle = circleFn(this.circle);
|
|
1037
|
+
return this.search;
|
|
1038
|
+
}
|
|
1039
|
+
toString() {
|
|
1040
|
+
const { longitudeOfOrigin, latitudeOfOrigin, size, units } = this.circle;
|
|
1041
|
+
return this.buildQuery(`[${longitudeOfOrigin} ${latitudeOfOrigin} ${size} ${units}]`);
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
// lib/search/where-string.ts
|
|
1046
|
+
var WhereString = class extends WhereField {
|
|
1047
|
+
value;
|
|
1048
|
+
eq(value) {
|
|
1049
|
+
this.value = value.toString();
|
|
1050
|
+
return this.search;
|
|
1051
|
+
}
|
|
1052
|
+
equal(value) {
|
|
1053
|
+
return this.eq(value);
|
|
1054
|
+
}
|
|
1055
|
+
equals(value) {
|
|
1056
|
+
return this.eq(value);
|
|
1057
|
+
}
|
|
1058
|
+
equalTo(value) {
|
|
1059
|
+
return this.eq(value);
|
|
1060
|
+
}
|
|
1061
|
+
match(_) {
|
|
1062
|
+
return this.throwMatchExcpetion();
|
|
1063
|
+
}
|
|
1064
|
+
matches(_) {
|
|
1065
|
+
return this.throwMatchExcpetion();
|
|
1066
|
+
}
|
|
1067
|
+
matchExact(_) {
|
|
1068
|
+
return this.throwMatchExcpetion();
|
|
1069
|
+
}
|
|
1070
|
+
matchExactly(_) {
|
|
1071
|
+
return this.throwMatchExcpetion();
|
|
1072
|
+
}
|
|
1073
|
+
matchesExactly(_) {
|
|
1074
|
+
return this.throwMatchExcpetion();
|
|
1075
|
+
}
|
|
1076
|
+
get exact() {
|
|
1077
|
+
return this.throwMatchExcpetionReturningThis();
|
|
1078
|
+
}
|
|
1079
|
+
get exactly() {
|
|
1080
|
+
return this.throwMatchExcpetionReturningThis();
|
|
1081
|
+
}
|
|
1082
|
+
toString() {
|
|
1083
|
+
const escapedValue = this.escapePunctuationAndSpaces(this.value);
|
|
1084
|
+
return this.buildQuery(`{${escapedValue}}`);
|
|
1085
|
+
}
|
|
1086
|
+
throwMatchExcpetion() {
|
|
1087
|
+
throw new SemanticSearchError("Cannot perform full-text search operations like .match on field of type 'string'. If full-text search is needed on this field, change the type to 'text' in the Schema.");
|
|
1088
|
+
}
|
|
1089
|
+
throwMatchExcpetionReturningThis() {
|
|
1090
|
+
throw new SemanticSearchError("Cannot perform full-text search operations like .match on field of type 'string'. If full-text search is needed on this field, change the type to 'text' in the Schema.");
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
|
|
1094
|
+
// lib/search/where-text.ts
|
|
1095
|
+
var WhereText = class extends WhereField {
|
|
1096
|
+
value;
|
|
1097
|
+
exactValue = false;
|
|
1098
|
+
fuzzyMatching;
|
|
1099
|
+
levenshteinDistance;
|
|
1100
|
+
match(value, options = {
|
|
1101
|
+
fuzzyMatching: false,
|
|
1102
|
+
levenshteinDistance: 1
|
|
1103
|
+
}) {
|
|
1104
|
+
this.value = value.toString();
|
|
1105
|
+
this.fuzzyMatching = options.fuzzyMatching ?? false;
|
|
1106
|
+
this.levenshteinDistance = options.levenshteinDistance ?? 1;
|
|
1107
|
+
return this.search;
|
|
1108
|
+
}
|
|
1109
|
+
matchExact(value) {
|
|
1110
|
+
this.exact.value = value.toString();
|
|
1111
|
+
return this.search;
|
|
1112
|
+
}
|
|
1113
|
+
matches(value, options = {
|
|
1114
|
+
fuzzyMatching: false,
|
|
1115
|
+
levenshteinDistance: 1
|
|
1116
|
+
}) {
|
|
1117
|
+
return this.match(value, options);
|
|
1118
|
+
}
|
|
1119
|
+
matchExactly(value) {
|
|
1120
|
+
return this.matchExact(value);
|
|
1121
|
+
}
|
|
1122
|
+
matchesExactly(value) {
|
|
1123
|
+
return this.matchExact(value);
|
|
1124
|
+
}
|
|
1125
|
+
get exact() {
|
|
1126
|
+
this.exactValue = true;
|
|
1127
|
+
return this;
|
|
1128
|
+
}
|
|
1129
|
+
get exactly() {
|
|
1130
|
+
return this.exact;
|
|
1131
|
+
}
|
|
1132
|
+
eq(_) {
|
|
1133
|
+
return this.throwEqualsExcpetion();
|
|
1134
|
+
}
|
|
1135
|
+
equal(_) {
|
|
1136
|
+
return this.throwEqualsExcpetion();
|
|
1137
|
+
}
|
|
1138
|
+
equals(_) {
|
|
1139
|
+
return this.throwEqualsExcpetion();
|
|
1140
|
+
}
|
|
1141
|
+
equalTo(_) {
|
|
1142
|
+
return this.throwEqualsExcpetion();
|
|
1143
|
+
}
|
|
1144
|
+
toString() {
|
|
1145
|
+
const escapedValue = this.escapePunctuation(this.value);
|
|
1146
|
+
if (this.exactValue) {
|
|
1147
|
+
return this.buildQuery(`"${escapedValue}"`);
|
|
1148
|
+
} else if (this.fuzzyMatching) {
|
|
1149
|
+
return this.buildQuery(`${"%".repeat(this.levenshteinDistance)}${escapedValue}${"%".repeat(this.levenshteinDistance)}`);
|
|
1150
|
+
} else {
|
|
1151
|
+
return this.buildQuery(`'${escapedValue}'`);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
throwEqualsExcpetion() {
|
|
1155
|
+
throw new SemanticSearchError("Cannot call .equals on a field of type 'text', either use .match to perform full-text search or change the type to 'string' in the Schema.");
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
// lib/search/where-date.ts
|
|
1160
|
+
var WhereDate = class extends WhereField {
|
|
1161
|
+
lower = Number.NEGATIVE_INFINITY;
|
|
1162
|
+
upper = Number.POSITIVE_INFINITY;
|
|
1163
|
+
lowerExclusive = false;
|
|
1164
|
+
upperExclusive = false;
|
|
1165
|
+
eq(value) {
|
|
1166
|
+
const epoch = this.coerceDateToEpoch(value);
|
|
1167
|
+
this.lower = epoch;
|
|
1168
|
+
this.upper = epoch;
|
|
1169
|
+
return this.search;
|
|
1170
|
+
}
|
|
1171
|
+
gt(value) {
|
|
1172
|
+
const epoch = this.coerceDateToEpoch(value);
|
|
1173
|
+
this.lower = epoch;
|
|
1174
|
+
this.lowerExclusive = true;
|
|
1175
|
+
return this.search;
|
|
1176
|
+
}
|
|
1177
|
+
gte(value) {
|
|
1178
|
+
this.lower = this.coerceDateToEpoch(value);
|
|
1179
|
+
return this.search;
|
|
1180
|
+
}
|
|
1181
|
+
lt(value) {
|
|
1182
|
+
this.upper = this.coerceDateToEpoch(value);
|
|
1183
|
+
this.upperExclusive = true;
|
|
1184
|
+
return this.search;
|
|
1185
|
+
}
|
|
1186
|
+
lte(value) {
|
|
1187
|
+
this.upper = this.coerceDateToEpoch(value);
|
|
1188
|
+
return this.search;
|
|
1189
|
+
}
|
|
1190
|
+
between(lower, upper) {
|
|
1191
|
+
this.lower = this.coerceDateToEpoch(lower);
|
|
1192
|
+
this.upper = this.coerceDateToEpoch(upper);
|
|
1193
|
+
return this.search;
|
|
1194
|
+
}
|
|
1195
|
+
equal(value) {
|
|
1196
|
+
return this.eq(value);
|
|
1197
|
+
}
|
|
1198
|
+
equals(value) {
|
|
1199
|
+
return this.eq(value);
|
|
1200
|
+
}
|
|
1201
|
+
equalTo(value) {
|
|
1202
|
+
return this.eq(value);
|
|
1203
|
+
}
|
|
1204
|
+
greaterThan(value) {
|
|
1205
|
+
return this.gt(value);
|
|
1206
|
+
}
|
|
1207
|
+
greaterThanOrEqualTo(value) {
|
|
1208
|
+
return this.gte(value);
|
|
1209
|
+
}
|
|
1210
|
+
lessThan(value) {
|
|
1211
|
+
return this.lt(value);
|
|
1212
|
+
}
|
|
1213
|
+
lessThanOrEqualTo(value) {
|
|
1214
|
+
return this.lte(value);
|
|
1215
|
+
}
|
|
1216
|
+
on(value) {
|
|
1217
|
+
return this.eq(value);
|
|
1218
|
+
}
|
|
1219
|
+
after(value) {
|
|
1220
|
+
return this.gt(value);
|
|
1221
|
+
}
|
|
1222
|
+
before(value) {
|
|
1223
|
+
return this.lt(value);
|
|
1224
|
+
}
|
|
1225
|
+
onOrAfter(value) {
|
|
1226
|
+
return this.gte(value);
|
|
1227
|
+
}
|
|
1228
|
+
onOrBefore(value) {
|
|
1229
|
+
return this.lte(value);
|
|
1230
|
+
}
|
|
1231
|
+
toString() {
|
|
1232
|
+
const lower = this.makeLowerString();
|
|
1233
|
+
const upper = this.makeUpperString();
|
|
1234
|
+
return this.buildQuery(`[${lower} ${upper}]`);
|
|
1235
|
+
}
|
|
1236
|
+
makeLowerString() {
|
|
1237
|
+
if (this.lower === Number.NEGATIVE_INFINITY)
|
|
1238
|
+
return "-inf";
|
|
1239
|
+
if (this.lowerExclusive)
|
|
1240
|
+
return `(${this.lower}`;
|
|
1241
|
+
return this.lower.toString();
|
|
1242
|
+
}
|
|
1243
|
+
makeUpperString() {
|
|
1244
|
+
if (this.upper === Number.POSITIVE_INFINITY)
|
|
1245
|
+
return "+inf";
|
|
1246
|
+
if (this.upperExclusive)
|
|
1247
|
+
return `(${this.upper}`;
|
|
1248
|
+
return this.upper.toString();
|
|
1249
|
+
}
|
|
1250
|
+
coerceDateToEpoch(value) {
|
|
1251
|
+
if (value instanceof Date)
|
|
1252
|
+
return value.getTime() / 1e3;
|
|
1253
|
+
if (typeof value === "string")
|
|
1254
|
+
return new Date(value).getTime() / 1e3;
|
|
1255
|
+
return value;
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
|
|
1259
|
+
// lib/search/search.ts
|
|
1260
|
+
var AbstractSearch = class {
|
|
1261
|
+
schema;
|
|
1262
|
+
client;
|
|
1263
|
+
sortOptions;
|
|
1264
|
+
constructor(schema, client) {
|
|
1265
|
+
this.schema = schema;
|
|
1266
|
+
this.client = client;
|
|
1267
|
+
}
|
|
1268
|
+
sortAscending(field) {
|
|
1269
|
+
return this.sortBy(field, "ASC");
|
|
1270
|
+
}
|
|
1271
|
+
sortDesc(field) {
|
|
1272
|
+
return this.sortDescending(field);
|
|
1273
|
+
}
|
|
1274
|
+
sortDescending(field) {
|
|
1275
|
+
return this.sortBy(field, "DESC");
|
|
1276
|
+
}
|
|
1277
|
+
sortAsc(field) {
|
|
1278
|
+
return this.sortAscending(field);
|
|
1279
|
+
}
|
|
1280
|
+
sortBy(fieldName, order = "ASC") {
|
|
1281
|
+
const field = this.schema.fieldByName(fieldName);
|
|
1282
|
+
const dataStructure = this.schema.dataStructure;
|
|
1283
|
+
if (!field) {
|
|
1284
|
+
const message = `'sortBy' was called on field '${String(fieldName)}' which is not defined in the Schema.`;
|
|
1285
|
+
console.error(message);
|
|
1286
|
+
throw new RedisOmError(message);
|
|
1287
|
+
}
|
|
1288
|
+
const type = field.type;
|
|
1289
|
+
const markedSortable = field.sortable;
|
|
1290
|
+
const UNSORTABLE = ["point", "string[]"];
|
|
1291
|
+
const JSON_SORTABLE = ["number", "text", "date"];
|
|
1292
|
+
const HASH_SORTABLE = ["string", "boolean", "number", "text", "date"];
|
|
1293
|
+
if (UNSORTABLE.includes(type)) {
|
|
1294
|
+
const message = `'sortBy' was called on '${field.type}' field '${field.name}' which cannot be sorted.`;
|
|
1295
|
+
console.error(message);
|
|
1296
|
+
throw new RedisOmError(message);
|
|
1297
|
+
}
|
|
1298
|
+
if (dataStructure === "JSON" && JSON_SORTABLE.includes(type) && !markedSortable)
|
|
1299
|
+
console.warn(`'sortBy' was called on field '${field.name}' which is not marked as sortable in the Schema. This may result is slower searches. If possible, mark the field as sortable in the Schema.`);
|
|
1300
|
+
if (dataStructure === "HASH" && HASH_SORTABLE.includes(type) && !markedSortable)
|
|
1301
|
+
console.warn(`'sortBy' was called on field '${field.name}' which is not marked as sortable in the Schema. This may result is slower searches. If possible, mark the field as sortable in the Schema.`);
|
|
1302
|
+
this.sortOptions = { BY: field.name, DIRECTION: order };
|
|
1303
|
+
return this;
|
|
1304
|
+
}
|
|
1305
|
+
async min(field) {
|
|
1306
|
+
return await this.sortBy(field, "ASC").first();
|
|
1307
|
+
}
|
|
1308
|
+
async minId(field) {
|
|
1309
|
+
return await this.sortBy(field, "ASC").firstId();
|
|
1310
|
+
}
|
|
1311
|
+
async minKey(field) {
|
|
1312
|
+
return await this.sortBy(field, "ASC").firstKey();
|
|
1313
|
+
}
|
|
1314
|
+
async max(field) {
|
|
1315
|
+
return await this.sortBy(field, "DESC").first();
|
|
1316
|
+
}
|
|
1317
|
+
async maxId(field) {
|
|
1318
|
+
return await this.sortBy(field, "DESC").firstId();
|
|
1319
|
+
}
|
|
1320
|
+
async maxKey(field) {
|
|
1321
|
+
return await this.sortBy(field, "DESC").firstKey();
|
|
1322
|
+
}
|
|
1323
|
+
async count() {
|
|
1324
|
+
const searchResults = await this.callSearch();
|
|
1325
|
+
return extractCountFromSearchResults(searchResults);
|
|
1326
|
+
}
|
|
1327
|
+
async page(offset, count) {
|
|
1328
|
+
const searchResults = await this.callSearch(offset, count);
|
|
1329
|
+
return extractEntitiesFromSearchResults(this.schema, searchResults);
|
|
1330
|
+
}
|
|
1331
|
+
async pageOfIds(offset, count) {
|
|
1332
|
+
const searchResults = await this.callSearch(offset, count, true);
|
|
1333
|
+
return extractEntityIdsFromSearchResults(this.schema, searchResults);
|
|
1334
|
+
}
|
|
1335
|
+
async pageOfKeys(offset, count) {
|
|
1336
|
+
const searchResults = await this.callSearch(offset, count, true);
|
|
1337
|
+
return extractKeyNamesFromSearchResults(searchResults);
|
|
1338
|
+
}
|
|
1339
|
+
async first() {
|
|
1340
|
+
const foundEntity = await this.page(0, 1);
|
|
1341
|
+
return foundEntity[0] ?? null;
|
|
1342
|
+
}
|
|
1343
|
+
async firstId() {
|
|
1344
|
+
const foundIds = await this.pageOfIds(0, 1);
|
|
1345
|
+
return foundIds[0] ?? null;
|
|
1346
|
+
}
|
|
1347
|
+
async firstKey() {
|
|
1348
|
+
const foundKeys = await this.pageOfKeys(0, 1);
|
|
1349
|
+
return foundKeys[0] ?? null;
|
|
1350
|
+
}
|
|
1351
|
+
async all(options = { pageSize: 10 }) {
|
|
1352
|
+
return this.allThings(this.page, options);
|
|
1353
|
+
}
|
|
1354
|
+
async allIds(options = { pageSize: 10 }) {
|
|
1355
|
+
return this.allThings(this.pageOfIds, options);
|
|
1356
|
+
}
|
|
1357
|
+
async allKeys(options = { pageSize: 10 }) {
|
|
1358
|
+
return await this.allThings(this.pageOfKeys, options);
|
|
1359
|
+
}
|
|
1360
|
+
get return() {
|
|
1361
|
+
return this;
|
|
1362
|
+
}
|
|
1363
|
+
async returnMin(field) {
|
|
1364
|
+
return await this.min(field);
|
|
1365
|
+
}
|
|
1366
|
+
async returnMinId(field) {
|
|
1367
|
+
return await this.minId(field);
|
|
1368
|
+
}
|
|
1369
|
+
async returnMinKey(field) {
|
|
1370
|
+
return await this.minKey(field);
|
|
1371
|
+
}
|
|
1372
|
+
async returnMax(field) {
|
|
1373
|
+
return await this.max(field);
|
|
1374
|
+
}
|
|
1375
|
+
async returnMaxId(field) {
|
|
1376
|
+
return await this.maxId(field);
|
|
1377
|
+
}
|
|
1378
|
+
async returnMaxKey(field) {
|
|
1379
|
+
return await this.maxKey(field);
|
|
1380
|
+
}
|
|
1381
|
+
async returnCount() {
|
|
1382
|
+
return await this.count();
|
|
1383
|
+
}
|
|
1384
|
+
async returnPage(offset, count) {
|
|
1385
|
+
return await this.page(offset, count);
|
|
1386
|
+
}
|
|
1387
|
+
async returnPageOfIds(offset, count) {
|
|
1388
|
+
return await this.pageOfIds(offset, count);
|
|
1389
|
+
}
|
|
1390
|
+
async returnPageOfKeys(offset, count) {
|
|
1391
|
+
return await this.pageOfKeys(offset, count);
|
|
1392
|
+
}
|
|
1393
|
+
async returnFirst() {
|
|
1394
|
+
return await this.first();
|
|
1395
|
+
}
|
|
1396
|
+
async returnFirstId() {
|
|
1397
|
+
return await this.firstId();
|
|
1398
|
+
}
|
|
1399
|
+
async returnFirstKey() {
|
|
1400
|
+
return await this.firstKey();
|
|
1401
|
+
}
|
|
1402
|
+
async returnAll(options = { pageSize: 10 }) {
|
|
1403
|
+
return await this.all(options);
|
|
1404
|
+
}
|
|
1405
|
+
async returnAllIds(options = { pageSize: 10 }) {
|
|
1406
|
+
return await this.allIds(options);
|
|
1407
|
+
}
|
|
1408
|
+
async returnAllKeys(options = { pageSize: 10 }) {
|
|
1409
|
+
return await this.allKeys(options);
|
|
1410
|
+
}
|
|
1411
|
+
async allThings(pageFn, options = { pageSize: 10 }) {
|
|
1412
|
+
const things = [];
|
|
1413
|
+
let offset = 0;
|
|
1414
|
+
const pageSize = options.pageSize;
|
|
1415
|
+
while (true) {
|
|
1416
|
+
const foundThings = await pageFn.call(this, offset, pageSize);
|
|
1417
|
+
things.push(...foundThings);
|
|
1418
|
+
if (foundThings.length < pageSize)
|
|
1419
|
+
break;
|
|
1420
|
+
offset += pageSize;
|
|
1421
|
+
}
|
|
1422
|
+
return things;
|
|
1423
|
+
}
|
|
1424
|
+
async callSearch(offset = 0, count = 0, keysOnly = false) {
|
|
1425
|
+
const dataStructure = this.schema.dataStructure;
|
|
1426
|
+
const indexName = this.schema.indexName;
|
|
1427
|
+
const query = this.query;
|
|
1428
|
+
const options = {
|
|
1429
|
+
LIMIT: { from: offset, size: count }
|
|
1430
|
+
};
|
|
1431
|
+
if (this.sortOptions !== void 0)
|
|
1432
|
+
options.SORTBY = this.sortOptions;
|
|
1433
|
+
if (keysOnly) {
|
|
1434
|
+
options.RETURN = [];
|
|
1435
|
+
} else if (dataStructure === "JSON") {
|
|
1436
|
+
options.RETURN = "$";
|
|
1437
|
+
}
|
|
1438
|
+
let searchResults;
|
|
1439
|
+
try {
|
|
1440
|
+
searchResults = await this.client.search(indexName, query, options);
|
|
1441
|
+
} catch (error) {
|
|
1442
|
+
const message = error.message;
|
|
1443
|
+
if (message.startsWith("Syntax error")) {
|
|
1444
|
+
throw new SearchError(`The query to RediSearch had a syntax error: "${message}".
|
|
1445
|
+
This is often the result of using a stop word in the query. Either change the query to not use a stop word or change the stop words in the schema definition. You can check the RediSearch source for the default stop words at: https://github.com/RediSearch/RediSearch/blob/master/src/stopwords.h.`);
|
|
1446
|
+
}
|
|
1447
|
+
throw error;
|
|
1448
|
+
}
|
|
1449
|
+
return searchResults;
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
var RawSearch = class extends AbstractSearch {
|
|
1453
|
+
rawQuery;
|
|
1454
|
+
constructor(schema, client, query = "*") {
|
|
1455
|
+
super(schema, client);
|
|
1456
|
+
this.rawQuery = query;
|
|
1457
|
+
}
|
|
1458
|
+
get query() {
|
|
1459
|
+
return this.rawQuery;
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1462
|
+
var Search = class extends AbstractSearch {
|
|
1463
|
+
rootWhere;
|
|
1464
|
+
get query() {
|
|
1465
|
+
if (this.rootWhere === void 0)
|
|
1466
|
+
return "*";
|
|
1467
|
+
return `${this.rootWhere.toString()}`;
|
|
1468
|
+
}
|
|
1469
|
+
where(fieldOrFn) {
|
|
1470
|
+
return this.anyWhere(WhereAnd, fieldOrFn);
|
|
1471
|
+
}
|
|
1472
|
+
and(fieldOrFn) {
|
|
1473
|
+
return this.anyWhere(WhereAnd, fieldOrFn);
|
|
1474
|
+
}
|
|
1475
|
+
or(fieldOrFn) {
|
|
1476
|
+
return this.anyWhere(WhereOr, fieldOrFn);
|
|
1477
|
+
}
|
|
1478
|
+
anyWhere(ctor, fieldOrFn) {
|
|
1479
|
+
if (typeof fieldOrFn === "function") {
|
|
1480
|
+
return this.anyWhereForFunction(ctor, fieldOrFn);
|
|
1481
|
+
} else {
|
|
1482
|
+
return this.anyWhereForField(ctor, fieldOrFn);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
anyWhereForField(ctor, field) {
|
|
1486
|
+
const where = this.createWhere(field);
|
|
1487
|
+
if (this.rootWhere === void 0) {
|
|
1488
|
+
this.rootWhere = where;
|
|
1489
|
+
} else {
|
|
1490
|
+
this.rootWhere = new ctor(this.rootWhere, where);
|
|
1491
|
+
}
|
|
1492
|
+
return where;
|
|
1493
|
+
}
|
|
1494
|
+
anyWhereForFunction(ctor, subSearchFn) {
|
|
1495
|
+
const search = new Search(this.schema, this.client);
|
|
1496
|
+
const subSearch = subSearchFn(search);
|
|
1497
|
+
if (subSearch.rootWhere === void 0) {
|
|
1498
|
+
throw new SearchError("Sub-search without a root where was somehow defined.");
|
|
1499
|
+
} else {
|
|
1500
|
+
if (this.rootWhere === void 0) {
|
|
1501
|
+
this.rootWhere = subSearch.rootWhere;
|
|
1502
|
+
} else {
|
|
1503
|
+
this.rootWhere = new ctor(this.rootWhere, subSearch.rootWhere);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
return this;
|
|
1507
|
+
}
|
|
1508
|
+
createWhere(fieldName) {
|
|
1509
|
+
const field = this.schema.fieldByName(fieldName);
|
|
1510
|
+
if (field === null)
|
|
1511
|
+
throw new FieldNotInSchema(String(fieldName));
|
|
1512
|
+
if (field.type === "boolean" && this.schema.dataStructure === "HASH")
|
|
1513
|
+
return new WhereHashBoolean(this, field);
|
|
1514
|
+
if (field.type === "boolean" && this.schema.dataStructure === "JSON")
|
|
1515
|
+
return new WhereJsonBoolean(this, field);
|
|
1516
|
+
if (field.type === "date")
|
|
1517
|
+
return new WhereDate(this, field);
|
|
1518
|
+
if (field.type === "number")
|
|
1519
|
+
return new WhereNumber(this, field);
|
|
1520
|
+
if (field.type === "number[]")
|
|
1521
|
+
return new WhereNumber(this, field);
|
|
1522
|
+
if (field.type === "point")
|
|
1523
|
+
return new WherePoint(this, field);
|
|
1524
|
+
if (field.type === "text")
|
|
1525
|
+
return new WhereText(this, field);
|
|
1526
|
+
if (field.type === "string")
|
|
1527
|
+
return new WhereString(this, field);
|
|
1528
|
+
if (field.type === "string[]")
|
|
1529
|
+
return new WhereStringArray(this, field);
|
|
1530
|
+
throw new RedisOmError(`The field type of '${fieldDef.type}' is not a valid field type. Valid types include 'boolean', 'date', 'number', 'point', 'string', and 'string[]'.`);
|
|
1531
|
+
}
|
|
1532
|
+
};
|
|
1533
|
+
|
|
1534
|
+
// lib/repository/repository.ts
|
|
1535
|
+
var Repository = class {
|
|
1536
|
+
client;
|
|
1537
|
+
#schema;
|
|
1538
|
+
constructor(schema, clientOrConnection) {
|
|
1539
|
+
this.#schema = schema;
|
|
1540
|
+
if (clientOrConnection instanceof Client) {
|
|
1541
|
+
this.client = clientOrConnection;
|
|
1542
|
+
} else {
|
|
1543
|
+
this.client = new Client();
|
|
1544
|
+
this.client.useNoClose(clientOrConnection);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
async createIndex() {
|
|
1548
|
+
const currentIndexHash = await this.client.get(this.#schema.indexHashName);
|
|
1549
|
+
const incomingIndexHash = this.#schema.indexHash;
|
|
1550
|
+
if (currentIndexHash !== incomingIndexHash) {
|
|
1551
|
+
await this.dropIndex();
|
|
1552
|
+
const {
|
|
1553
|
+
indexName,
|
|
1554
|
+
indexHashName,
|
|
1555
|
+
dataStructure,
|
|
1556
|
+
schemaName: prefix,
|
|
1557
|
+
useStopWords,
|
|
1558
|
+
stopWords
|
|
1559
|
+
} = this.#schema;
|
|
1560
|
+
const schema = buildRediSearchSchema(this.#schema);
|
|
1561
|
+
const options = {
|
|
1562
|
+
ON: dataStructure,
|
|
1563
|
+
PREFIX: `${prefix}:`
|
|
1564
|
+
};
|
|
1565
|
+
if (useStopWords === "OFF") {
|
|
1566
|
+
options.STOPWORDS = [];
|
|
1567
|
+
} else if (useStopWords === "CUSTOM") {
|
|
1568
|
+
options.STOPWORDS = stopWords;
|
|
1569
|
+
}
|
|
1570
|
+
await Promise.all([
|
|
1571
|
+
this.client.createIndex(indexName, schema, options),
|
|
1572
|
+
this.client.set(indexHashName, incomingIndexHash)
|
|
1573
|
+
]);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
async dropIndex() {
|
|
1577
|
+
try {
|
|
1578
|
+
await Promise.all([
|
|
1579
|
+
this.client.unlink(this.#schema.indexHashName),
|
|
1580
|
+
this.client.dropIndex(this.#schema.indexName)
|
|
1581
|
+
]);
|
|
1582
|
+
} catch (e) {
|
|
1583
|
+
if (e instanceof Error && (e.message === "Unknown Index name" || e.message === "Unknown index name")) {
|
|
1584
|
+
} else {
|
|
1585
|
+
throw e;
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
async save(entityOrId, maybeEntity) {
|
|
1590
|
+
let entity;
|
|
1591
|
+
let entityId;
|
|
1592
|
+
if (typeof entityOrId !== "string") {
|
|
1593
|
+
entity = entityOrId;
|
|
1594
|
+
entityId = entity[EntityId] ?? await this.#schema.generateId();
|
|
1595
|
+
} else {
|
|
1596
|
+
entity = maybeEntity;
|
|
1597
|
+
entityId = entityOrId;
|
|
1598
|
+
}
|
|
1599
|
+
const keyName = `${this.#schema.schemaName}:${entityId}`;
|
|
1600
|
+
const clonedEntity = { ...entity, [EntityId]: entityId, [EntityKeyName]: keyName };
|
|
1601
|
+
await this.writeEntity(clonedEntity);
|
|
1602
|
+
return clonedEntity;
|
|
1603
|
+
}
|
|
1604
|
+
async fetch(ids) {
|
|
1605
|
+
if (arguments.length > 1)
|
|
1606
|
+
return this.readEntities([...arguments]);
|
|
1607
|
+
if (Array.isArray(ids))
|
|
1608
|
+
return this.readEntities(ids);
|
|
1609
|
+
const [entity] = await this.readEntities([ids]);
|
|
1610
|
+
return entity;
|
|
1611
|
+
}
|
|
1612
|
+
async remove(ids) {
|
|
1613
|
+
const keys = arguments.length > 1 ? this.makeKeys([...arguments]) : Array.isArray(ids) ? this.makeKeys(ids) : ids ? this.makeKeys([ids]) : [];
|
|
1614
|
+
if (keys.length === 0)
|
|
1615
|
+
return;
|
|
1616
|
+
await this.client.unlink(...keys);
|
|
1617
|
+
}
|
|
1618
|
+
async expire(idOrIds, ttlInSeconds) {
|
|
1619
|
+
const ids = typeof idOrIds === "string" ? [idOrIds] : idOrIds;
|
|
1620
|
+
await Promise.all(
|
|
1621
|
+
ids.map((id) => {
|
|
1622
|
+
const key = this.makeKey(id);
|
|
1623
|
+
return this.client.expire(key, ttlInSeconds);
|
|
1624
|
+
})
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
async expireAt(idOrIds, expirationDate) {
|
|
1628
|
+
const ids = typeof idOrIds === "string" ? [idOrIds] : idOrIds;
|
|
1629
|
+
if (Date.now() >= expirationDate.getTime()) {
|
|
1630
|
+
throw new Error(
|
|
1631
|
+
`${expirationDate.toString()} is invalid. Expiration date must be in the future.`
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
await Promise.all(
|
|
1635
|
+
ids.map((id) => {
|
|
1636
|
+
const key = this.makeKey(id);
|
|
1637
|
+
return this.client.expireAt(key, expirationDate);
|
|
1638
|
+
})
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
search() {
|
|
1642
|
+
return new Search(this.#schema, this.client);
|
|
1643
|
+
}
|
|
1644
|
+
searchRaw(query) {
|
|
1645
|
+
return new RawSearch(this.#schema, this.client, query);
|
|
1646
|
+
}
|
|
1647
|
+
async writeEntity(entity) {
|
|
1648
|
+
return this.#schema.dataStructure === "HASH" ? this.#writeEntityToHash(entity) : this.writeEntityToJson(entity);
|
|
1649
|
+
}
|
|
1650
|
+
async readEntities(ids) {
|
|
1651
|
+
return this.#schema.dataStructure === "HASH" ? this.readEntitiesFromHash(ids) : this.readEntitiesFromJson(ids);
|
|
1652
|
+
}
|
|
1653
|
+
async #writeEntityToHash(entity) {
|
|
1654
|
+
const keyName = entity[EntityKeyName];
|
|
1655
|
+
const hashData = toRedisHash(this.#schema, entity);
|
|
1656
|
+
if (Object.keys(hashData).length === 0) {
|
|
1657
|
+
await this.client.unlink(keyName);
|
|
1658
|
+
} else {
|
|
1659
|
+
await this.client.hsetall(keyName, hashData);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
async readEntitiesFromHash(ids) {
|
|
1663
|
+
return Promise.all(
|
|
1664
|
+
ids.map(async (entityId) => {
|
|
1665
|
+
const keyName = this.makeKey(entityId);
|
|
1666
|
+
const hashData = await this.client.hgetall(keyName);
|
|
1667
|
+
const entityData = fromRedisHash(this.#schema, hashData);
|
|
1668
|
+
return { ...entityData, [EntityId]: entityId, [EntityKeyName]: keyName };
|
|
1669
|
+
})
|
|
1670
|
+
);
|
|
1671
|
+
}
|
|
1672
|
+
async writeEntityToJson(entity) {
|
|
1673
|
+
const keyName = entity[EntityKeyName];
|
|
1674
|
+
const jsonData = toRedisJson(this.#schema, entity);
|
|
1675
|
+
await this.client.jsonset(keyName, jsonData);
|
|
1676
|
+
}
|
|
1677
|
+
async readEntitiesFromJson(ids) {
|
|
1678
|
+
return Promise.all(
|
|
1679
|
+
ids.map(async (entityId) => {
|
|
1680
|
+
const keyName = this.makeKey(entityId);
|
|
1681
|
+
const jsonData = await this.client.jsonget(keyName) ?? {};
|
|
1682
|
+
const entityData = fromRedisJson(this.#schema, jsonData);
|
|
1683
|
+
return { ...entityData, [EntityId]: entityId, [EntityKeyName]: keyName };
|
|
1684
|
+
})
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
1687
|
+
makeKeys(ids) {
|
|
1688
|
+
return ids.map((id) => this.makeKey(id));
|
|
1689
|
+
}
|
|
1690
|
+
makeKey(id) {
|
|
1691
|
+
return `${this.#schema.schemaName}:${id}`;
|
|
1692
|
+
}
|
|
1693
|
+
};
|
|
1694
|
+
|
|
1695
|
+
// lib/client/client.ts
|
|
1696
|
+
var Client = class {
|
|
1697
|
+
#redis;
|
|
1698
|
+
get redis() {
|
|
1699
|
+
return this.#redis;
|
|
1700
|
+
}
|
|
1701
|
+
async use(connection) {
|
|
1702
|
+
await this.close();
|
|
1703
|
+
return this.useNoClose(connection);
|
|
1704
|
+
}
|
|
1705
|
+
useNoClose(connection) {
|
|
1706
|
+
this.#redis = connection;
|
|
1707
|
+
return this;
|
|
1708
|
+
}
|
|
1709
|
+
async open(url = "redis://localhost:6379") {
|
|
1710
|
+
if (!this.isOpen()) {
|
|
1711
|
+
const redis = (0, import_redis2.createClient)({ url });
|
|
1712
|
+
await redis.connect();
|
|
1713
|
+
this.#redis = redis;
|
|
1714
|
+
}
|
|
1715
|
+
return this;
|
|
1716
|
+
}
|
|
1717
|
+
fetchRepository(schema) {
|
|
1718
|
+
this.#validateRedisOpen();
|
|
1719
|
+
return new Repository(schema, this);
|
|
1720
|
+
}
|
|
1721
|
+
async close() {
|
|
1722
|
+
if (this.#redis)
|
|
1723
|
+
await this.#redis.quit();
|
|
1724
|
+
this.#redis = void 0;
|
|
1725
|
+
}
|
|
1726
|
+
async createIndex(indexName, schema, options) {
|
|
1727
|
+
this.#validateRedisOpen();
|
|
1728
|
+
await this.redis.ft.create(indexName, schema, options);
|
|
1729
|
+
}
|
|
1730
|
+
async dropIndex(indexName) {
|
|
1731
|
+
this.#validateRedisOpen();
|
|
1732
|
+
await this.redis.ft.dropIndex(indexName);
|
|
1733
|
+
}
|
|
1734
|
+
async search(indexName, query, options) {
|
|
1735
|
+
this.#validateRedisOpen();
|
|
1736
|
+
if (options)
|
|
1737
|
+
return await this.redis.ft.search(indexName, query, options);
|
|
1738
|
+
return await this.redis.ft.search(indexName, query);
|
|
1739
|
+
}
|
|
1740
|
+
async unlink(...keys) {
|
|
1741
|
+
this.#validateRedisOpen();
|
|
1742
|
+
if (keys.length > 0)
|
|
1743
|
+
await this.redis.unlink(keys);
|
|
1744
|
+
}
|
|
1745
|
+
async expire(key, ttl) {
|
|
1746
|
+
this.#validateRedisOpen();
|
|
1747
|
+
await this.redis.expire(key, ttl);
|
|
1748
|
+
}
|
|
1749
|
+
async expireAt(key, timestamp) {
|
|
1750
|
+
this.#validateRedisOpen();
|
|
1751
|
+
await this.redis.expireAt(key, timestamp);
|
|
1752
|
+
}
|
|
1753
|
+
async get(key) {
|
|
1754
|
+
this.#validateRedisOpen();
|
|
1755
|
+
return this.redis.get(key);
|
|
1756
|
+
}
|
|
1757
|
+
async set(key, value) {
|
|
1758
|
+
this.#validateRedisOpen();
|
|
1759
|
+
await this.redis.set(key, value);
|
|
1760
|
+
}
|
|
1761
|
+
async hgetall(key) {
|
|
1762
|
+
this.#validateRedisOpen();
|
|
1763
|
+
return this.redis.hGetAll(key);
|
|
1764
|
+
}
|
|
1765
|
+
async hsetall(key, data) {
|
|
1766
|
+
this.#validateRedisOpen();
|
|
1767
|
+
await this.redis.multi().unlink(key).hSet(key, data).exec();
|
|
1768
|
+
}
|
|
1769
|
+
async jsonget(key) {
|
|
1770
|
+
this.#validateRedisOpen();
|
|
1771
|
+
const json = await this.redis.json.get(key, { path: "$" });
|
|
1772
|
+
return json === null ? null : json[0];
|
|
1773
|
+
}
|
|
1774
|
+
async jsonset(key, data) {
|
|
1775
|
+
this.#validateRedisOpen();
|
|
1776
|
+
await this.redis.json.set(key, "$", data);
|
|
1777
|
+
}
|
|
1778
|
+
isOpen() {
|
|
1779
|
+
return !!this.#redis;
|
|
1780
|
+
}
|
|
1781
|
+
#validateRedisOpen() {
|
|
1782
|
+
if (!this.redis)
|
|
1783
|
+
throw new RedisOmError("Redis connection needs to be open.");
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
|
|
1787
|
+
// lib/schema/field.ts
|
|
1788
|
+
var Field = class {
|
|
1789
|
+
#name;
|
|
1790
|
+
#definition;
|
|
1791
|
+
constructor(name, definition) {
|
|
1792
|
+
this.#name = name;
|
|
1793
|
+
this.#definition = definition;
|
|
1794
|
+
}
|
|
1795
|
+
get name() {
|
|
1796
|
+
return this.#name;
|
|
1797
|
+
}
|
|
1798
|
+
get type() {
|
|
1799
|
+
return this.#definition.type;
|
|
1800
|
+
}
|
|
1801
|
+
get hashField() {
|
|
1802
|
+
return this.#definition.field ?? this.#definition.alias ?? this.name;
|
|
1803
|
+
}
|
|
1804
|
+
get jsonPath() {
|
|
1805
|
+
if (this.#definition.path)
|
|
1806
|
+
return this.#definition.path;
|
|
1807
|
+
const alias = (this.#definition.alias ?? this.name).replace(/"/g, '\\"');
|
|
1808
|
+
return this.isArray ? `$["${alias}"][*]` : `$["${alias}"]`;
|
|
1809
|
+
}
|
|
1810
|
+
get separator() {
|
|
1811
|
+
return this.#definition.separator ?? "|";
|
|
1812
|
+
}
|
|
1813
|
+
get sortable() {
|
|
1814
|
+
return this.#definition.sortable ?? false;
|
|
1815
|
+
}
|
|
1816
|
+
get caseSensitive() {
|
|
1817
|
+
return this.#definition.caseSensitive ?? false;
|
|
1818
|
+
}
|
|
1819
|
+
get indexed() {
|
|
1820
|
+
return this.#definition.indexed ?? true;
|
|
1821
|
+
}
|
|
1822
|
+
get stemming() {
|
|
1823
|
+
return this.#definition.stemming ?? true;
|
|
1824
|
+
}
|
|
1825
|
+
get normalized() {
|
|
1826
|
+
return this.#definition.normalized ?? true;
|
|
1827
|
+
}
|
|
1828
|
+
get weight() {
|
|
1829
|
+
return this.#definition.weight ?? null;
|
|
1830
|
+
}
|
|
1831
|
+
get matcher() {
|
|
1832
|
+
return this.#definition.matcher ?? null;
|
|
1833
|
+
}
|
|
1834
|
+
get isArray() {
|
|
1835
|
+
return this.type.endsWith("[]");
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1838
|
+
|
|
1839
|
+
// lib/schema/schema.ts
|
|
1840
|
+
var import_crypto = require("crypto");
|
|
1841
|
+
|
|
1842
|
+
// node_modules/ulid/dist/index.esm.js
|
|
1843
|
+
function createError(message) {
|
|
1844
|
+
var err = new Error(message);
|
|
1845
|
+
err.source = "ulid";
|
|
1846
|
+
return err;
|
|
1847
|
+
}
|
|
1848
|
+
var ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
1849
|
+
var ENCODING_LEN = ENCODING.length;
|
|
1850
|
+
var TIME_MAX = Math.pow(2, 48) - 1;
|
|
1851
|
+
var TIME_LEN = 10;
|
|
1852
|
+
var RANDOM_LEN = 16;
|
|
1853
|
+
function randomChar(prng) {
|
|
1854
|
+
var rand = Math.floor(prng() * ENCODING_LEN);
|
|
1855
|
+
if (rand === ENCODING_LEN) {
|
|
1856
|
+
rand = ENCODING_LEN - 1;
|
|
1857
|
+
}
|
|
1858
|
+
return ENCODING.charAt(rand);
|
|
1859
|
+
}
|
|
1860
|
+
function encodeTime(now, len) {
|
|
1861
|
+
if (isNaN(now)) {
|
|
1862
|
+
throw new Error(now + " must be a number");
|
|
1863
|
+
}
|
|
1864
|
+
if (now > TIME_MAX) {
|
|
1865
|
+
throw createError("cannot encode time greater than " + TIME_MAX);
|
|
1866
|
+
}
|
|
1867
|
+
if (now < 0) {
|
|
1868
|
+
throw createError("time must be positive");
|
|
1869
|
+
}
|
|
1870
|
+
if (Number.isInteger(now) === false) {
|
|
1871
|
+
throw createError("time must be an integer");
|
|
1872
|
+
}
|
|
1873
|
+
var mod = void 0;
|
|
1874
|
+
var str = "";
|
|
1875
|
+
for (; len > 0; len--) {
|
|
1876
|
+
mod = now % ENCODING_LEN;
|
|
1877
|
+
str = ENCODING.charAt(mod) + str;
|
|
1878
|
+
now = (now - mod) / ENCODING_LEN;
|
|
1879
|
+
}
|
|
1880
|
+
return str;
|
|
1881
|
+
}
|
|
1882
|
+
function encodeRandom(len, prng) {
|
|
1883
|
+
var str = "";
|
|
1884
|
+
for (; len > 0; len--) {
|
|
1885
|
+
str = randomChar(prng) + str;
|
|
1886
|
+
}
|
|
1887
|
+
return str;
|
|
1888
|
+
}
|
|
1889
|
+
function detectPrng() {
|
|
1890
|
+
var allowInsecure = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false;
|
|
1891
|
+
var root = arguments[1];
|
|
1892
|
+
if (!root) {
|
|
1893
|
+
root = typeof window !== "undefined" ? window : null;
|
|
1894
|
+
}
|
|
1895
|
+
var browserCrypto = root && (root.crypto || root.msCrypto);
|
|
1896
|
+
if (browserCrypto) {
|
|
1897
|
+
return function() {
|
|
1898
|
+
var buffer = new Uint8Array(1);
|
|
1899
|
+
browserCrypto.getRandomValues(buffer);
|
|
1900
|
+
return buffer[0] / 255;
|
|
1901
|
+
};
|
|
1902
|
+
} else {
|
|
1903
|
+
try {
|
|
1904
|
+
var nodeCrypto = require("crypto");
|
|
1905
|
+
return function() {
|
|
1906
|
+
return nodeCrypto.randomBytes(1).readUInt8() / 255;
|
|
1907
|
+
};
|
|
1908
|
+
} catch (e) {
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
if (allowInsecure) {
|
|
1912
|
+
try {
|
|
1913
|
+
console.error("secure crypto unusable, falling back to insecure Math.random()!");
|
|
1914
|
+
} catch (e) {
|
|
1915
|
+
}
|
|
1916
|
+
return function() {
|
|
1917
|
+
return Math.random();
|
|
1918
|
+
};
|
|
1919
|
+
}
|
|
1920
|
+
throw createError("secure crypto unusable, insecure Math.random not allowed");
|
|
1921
|
+
}
|
|
1922
|
+
function factory(currPrng) {
|
|
1923
|
+
if (!currPrng) {
|
|
1924
|
+
currPrng = detectPrng();
|
|
1925
|
+
}
|
|
1926
|
+
return function ulid2(seedTime) {
|
|
1927
|
+
if (isNaN(seedTime)) {
|
|
1928
|
+
seedTime = Date.now();
|
|
1929
|
+
}
|
|
1930
|
+
return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN, currPrng);
|
|
1931
|
+
};
|
|
1932
|
+
}
|
|
1933
|
+
var ulid = factory();
|
|
1934
|
+
|
|
1935
|
+
// lib/schema/schema.ts
|
|
1936
|
+
var Schema = class {
|
|
1937
|
+
#schemaName;
|
|
1938
|
+
#fieldsByName = {};
|
|
1939
|
+
#definition;
|
|
1940
|
+
#options;
|
|
1941
|
+
constructor(schemaName, schemaDef, options) {
|
|
1942
|
+
this.#schemaName = schemaName;
|
|
1943
|
+
this.#definition = schemaDef;
|
|
1944
|
+
this.#options = options;
|
|
1945
|
+
this.#validateOptions();
|
|
1946
|
+
this.#createFields();
|
|
1947
|
+
}
|
|
1948
|
+
get schemaName() {
|
|
1949
|
+
return this.#schemaName;
|
|
1950
|
+
}
|
|
1951
|
+
get fields() {
|
|
1952
|
+
return Object.entries(this.#fieldsByName).map(([_name, field]) => field);
|
|
1953
|
+
}
|
|
1954
|
+
fieldByName(name) {
|
|
1955
|
+
return this.#fieldsByName[name] ?? null;
|
|
1956
|
+
}
|
|
1957
|
+
get indexName() {
|
|
1958
|
+
return this.#options?.indexName ?? `${this.schemaName}:index`;
|
|
1959
|
+
}
|
|
1960
|
+
get indexHashName() {
|
|
1961
|
+
return this.#options?.indexHashName ?? `${this.schemaName}:index:hash`;
|
|
1962
|
+
}
|
|
1963
|
+
get dataStructure() {
|
|
1964
|
+
return this.#options?.dataStructure ?? "JSON";
|
|
1965
|
+
}
|
|
1966
|
+
get useStopWords() {
|
|
1967
|
+
return this.#options?.useStopWords ?? "DEFAULT";
|
|
1968
|
+
}
|
|
1969
|
+
get stopWords() {
|
|
1970
|
+
return this.#options?.stopWords ?? [];
|
|
1971
|
+
}
|
|
1972
|
+
async generateId() {
|
|
1973
|
+
const ulidStrategy = () => ulid();
|
|
1974
|
+
return (this.#options?.idStrategy ?? ulidStrategy)();
|
|
1975
|
+
}
|
|
1976
|
+
get indexHash() {
|
|
1977
|
+
const data = JSON.stringify({
|
|
1978
|
+
definition: this.#definition,
|
|
1979
|
+
prefix: this.schemaName,
|
|
1980
|
+
indexName: this.indexName,
|
|
1981
|
+
indexHashName: this.indexHashName,
|
|
1982
|
+
dataStructure: this.dataStructure,
|
|
1983
|
+
useStopWords: this.useStopWords,
|
|
1984
|
+
stopWords: this.stopWords
|
|
1985
|
+
});
|
|
1986
|
+
return (0, import_crypto.createHash)("sha1").update(data).digest("base64");
|
|
1987
|
+
}
|
|
1988
|
+
#createFields() {
|
|
1989
|
+
const entries = Object.entries(this.#definition);
|
|
1990
|
+
return entries.forEach(([fieldName, fieldDef2]) => {
|
|
1991
|
+
const field = new Field(String(fieldName), fieldDef2);
|
|
1992
|
+
this.#validateField(field);
|
|
1993
|
+
this.#fieldsByName[fieldName] = field;
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1996
|
+
#validateOptions() {
|
|
1997
|
+
const { dataStructure, useStopWords } = this;
|
|
1998
|
+
if (dataStructure !== "HASH" && dataStructure !== "JSON")
|
|
1999
|
+
throw new InvalidSchema(`'${dataStructure}' in an invalid data structure. Valid data structures are 'HASH' and 'JSON'.`);
|
|
2000
|
+
if (useStopWords !== "OFF" && useStopWords !== "DEFAULT" && useStopWords !== "CUSTOM")
|
|
2001
|
+
throw new InvalidSchema(`'${useStopWords}' in an invalid value for stop words. Valid values are 'OFF', 'DEFAULT', and 'CUSTOM'.`);
|
|
2002
|
+
if (this.#options?.idStrategy && typeof this.#options.idStrategy !== "function")
|
|
2003
|
+
throw new InvalidSchema("ID strategy must be a function that takes no arguments and returns a string.");
|
|
2004
|
+
if (this.schemaName === "")
|
|
2005
|
+
throw new InvalidSchema(`Schema name must be a non-empty string.`);
|
|
2006
|
+
if (this.indexName === "")
|
|
2007
|
+
throw new InvalidSchema(`Index name must be a non-empty string.`);
|
|
2008
|
+
}
|
|
2009
|
+
#validateField(field) {
|
|
2010
|
+
const { type } = field;
|
|
2011
|
+
if (type !== "boolean" && type !== "date" && type !== "number" && type !== "number[]" && type !== "point" && type !== "string" && type !== "string[]" && type !== "text")
|
|
2012
|
+
throw new InvalidSchema(`The field '${field.name}' is configured with a type of '${field.type}'. Valid types include 'boolean', 'date', 'number', 'number[]', 'point', 'string', 'string[]', and 'text'.`);
|
|
2013
|
+
if (type === "number[]" && this.dataStructure === "HASH")
|
|
2014
|
+
throw new InvalidSchema(`The field '${field.name}' is configured with a type of '${field.type}'. This type is only valid with a data structure of 'JSON'.`);
|
|
2015
|
+
}
|
|
2016
|
+
};
|
|
2017
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2018
|
+
0 && (module.exports = {
|
|
2019
|
+
AbstractSearch,
|
|
2020
|
+
ArrayHashInput,
|
|
2021
|
+
Circle,
|
|
2022
|
+
Client,
|
|
2023
|
+
EntityId,
|
|
2024
|
+
EntityKeyName,
|
|
2025
|
+
Field,
|
|
2026
|
+
FieldNotInSchema,
|
|
2027
|
+
InvalidHashInput,
|
|
2028
|
+
InvalidHashValue,
|
|
2029
|
+
InvalidInput,
|
|
2030
|
+
InvalidJsonInput,
|
|
2031
|
+
InvalidJsonValue,
|
|
2032
|
+
InvalidSchema,
|
|
2033
|
+
InvalidValue,
|
|
2034
|
+
NestedHashInput,
|
|
2035
|
+
NullJsonInput,
|
|
2036
|
+
NullJsonValue,
|
|
2037
|
+
PointOutOfRange,
|
|
2038
|
+
RawSearch,
|
|
2039
|
+
RedisOmError,
|
|
2040
|
+
Repository,
|
|
2041
|
+
Schema,
|
|
2042
|
+
Search,
|
|
2043
|
+
SearchError,
|
|
2044
|
+
SemanticSearchError,
|
|
2045
|
+
Where,
|
|
2046
|
+
WhereField
|
|
2047
|
+
});
|