@goatlab/fluent-loki 0.8.3 → 0.9.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/dist/LokiConnector.d.ts +2 -1
- package/dist/LokiConnector.js +223 -299
- package/dist/LokiConnector.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
package/dist/LokiConnector.js
CHANGED
|
@@ -17,11 +17,14 @@ class LokiConnector extends fluent_1.BaseConnector {
|
|
|
17
17
|
this.outputSchema =
|
|
18
18
|
outputSchema || inputSchema;
|
|
19
19
|
this.entity = entity;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
// Use Set for O(1) lookup instead of array
|
|
21
|
+
const dbModels = new Set();
|
|
22
|
+
const collections = dataSource.collections;
|
|
23
|
+
const collectionsLength = collections.length;
|
|
24
|
+
for (let i = 0; i < collectionsLength; i++) {
|
|
25
|
+
dbModels.add(collections[i].name);
|
|
23
26
|
}
|
|
24
|
-
if (!dbModels.
|
|
27
|
+
if (!dbModels.has(entity.name)) {
|
|
25
28
|
dataSource.addCollection(entity.name);
|
|
26
29
|
}
|
|
27
30
|
this.dataSource = dataSource;
|
|
@@ -54,21 +57,26 @@ class LokiConnector extends fluent_1.BaseConnector {
|
|
|
54
57
|
}
|
|
55
58
|
async insertMany(data) {
|
|
56
59
|
const validatedData = this.inputSchema.array().parse(data);
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
const dataLength = validatedData.length;
|
|
61
|
+
const insertedElements = new Array(dataLength);
|
|
62
|
+
const now = new Date();
|
|
63
|
+
const nowTime = now.getTime(); // Cache timestamp for reuse
|
|
64
|
+
for (let i = 0; i < dataLength; i++) {
|
|
65
|
+
const id = js_utils_1.Ids.uuid();
|
|
66
|
+
insertedElements[i] = {
|
|
67
|
+
...validatedData[i],
|
|
68
|
+
id,
|
|
63
69
|
created: now,
|
|
64
70
|
createdAt: now,
|
|
65
71
|
updatedAt: now
|
|
66
|
-
}
|
|
72
|
+
};
|
|
67
73
|
}
|
|
68
74
|
await this.collection.insert(insertedElements);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
const cleanedResults = new Array(dataLength);
|
|
76
|
+
for (let i = 0; i < dataLength; i++) {
|
|
77
|
+
cleanedResults[i] = js_utils_1.Objects.clearEmpties(js_utils_1.Objects.deleteNulls(insertedElements[i]));
|
|
78
|
+
}
|
|
79
|
+
return this.outputSchema.array().parse(cleanedResults);
|
|
72
80
|
}
|
|
73
81
|
/**
|
|
74
82
|
*
|
|
@@ -83,40 +91,51 @@ class LokiConnector extends fluent_1.BaseConnector {
|
|
|
83
91
|
}
|
|
84
92
|
async findMany(query) {
|
|
85
93
|
const where = this.getLokiWhere(query?.where);
|
|
86
|
-
const sort = [];
|
|
87
94
|
let baseQuery = this.collection
|
|
88
95
|
.chain()
|
|
89
96
|
.find(where);
|
|
90
|
-
//
|
|
91
|
-
if (query?.paginated) {
|
|
92
|
-
baseQuery.limit(query.paginated.perPage);
|
|
93
|
-
baseQuery.offset((query.paginated?.page - 1) * query.paginated.perPage);
|
|
94
|
-
}
|
|
97
|
+
// Build sort array if needed - pre-calculate total size
|
|
95
98
|
if (query?.orderBy) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
let totalSortFields = 0;
|
|
100
|
+
const orderLength = query.orderBy.length;
|
|
101
|
+
// First pass: count total fields
|
|
102
|
+
for (let i = 0; i < orderLength; i++) {
|
|
103
|
+
const flattenObject = js_utils_1.Objects.flatten(query.orderBy[i]);
|
|
104
|
+
totalSortFields += Object.keys(flattenObject).length;
|
|
105
|
+
}
|
|
106
|
+
// Pre-allocate sort array
|
|
107
|
+
const sort = new Array(totalSortFields);
|
|
108
|
+
let sortIndex = 0;
|
|
109
|
+
// Second pass: populate array
|
|
110
|
+
for (let i = 0; i < orderLength; i++) {
|
|
111
|
+
const flattenObject = js_utils_1.Objects.flatten(query.orderBy[i]);
|
|
112
|
+
const attributes = Object.keys(flattenObject);
|
|
113
|
+
const attrLength = attributes.length;
|
|
114
|
+
for (let j = 0; j < attrLength; j++) {
|
|
115
|
+
const attribute = attributes[j];
|
|
116
|
+
const isDescending = flattenObject[attribute] === 'desc';
|
|
117
|
+
sort[sortIndex++] = [attribute, isDescending];
|
|
101
118
|
}
|
|
102
119
|
}
|
|
103
120
|
baseQuery = baseQuery.compoundsort(sort);
|
|
104
121
|
}
|
|
105
|
-
// Apply offset and limit
|
|
106
|
-
if (query?.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (query?.limit) {
|
|
110
|
-
baseQuery = baseQuery.limit(query.limit);
|
|
122
|
+
// Apply pagination, offset and limit
|
|
123
|
+
if (query?.paginated) {
|
|
124
|
+
const offset = (query.paginated.page - 1) * query.paginated.perPage;
|
|
125
|
+
baseQuery = baseQuery.offset(offset).limit(query.paginated.perPage);
|
|
111
126
|
}
|
|
112
|
-
else
|
|
113
|
-
|
|
114
|
-
|
|
127
|
+
else {
|
|
128
|
+
if (query?.offset) {
|
|
129
|
+
baseQuery = baseQuery.offset(query.offset);
|
|
130
|
+
}
|
|
131
|
+
baseQuery = baseQuery.limit(query?.limit || 10);
|
|
115
132
|
}
|
|
116
133
|
let found = baseQuery.data();
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
134
|
+
// Clean data in-place
|
|
135
|
+
const foundLength = found.length;
|
|
136
|
+
for (let i = 0; i < foundLength; i++) {
|
|
137
|
+
js_utils_1.Objects.clearEmpties(js_utils_1.Objects.deleteNulls(found[i]));
|
|
138
|
+
}
|
|
120
139
|
if (query?.paginated) {
|
|
121
140
|
const paginationInfo = {
|
|
122
141
|
total: 0,
|
|
@@ -139,15 +158,19 @@ class LokiConnector extends fluent_1.BaseConnector {
|
|
|
139
158
|
}
|
|
140
159
|
// Validate Output against schema
|
|
141
160
|
// Use partial schema to handle objects that may have been replaced with partial data
|
|
142
|
-
|
|
161
|
+
// Optimize validation by caching partial schema
|
|
162
|
+
const partialSchema = this.outputSchema.partial();
|
|
163
|
+
const validatedResults = new Array(found.length);
|
|
164
|
+
for (let i = 0; i < found.length; i++) {
|
|
165
|
+
const item = found[i];
|
|
143
166
|
try {
|
|
144
|
-
|
|
167
|
+
validatedResults[i] = this.outputSchema.parse(item);
|
|
145
168
|
}
|
|
146
169
|
catch (e) {
|
|
147
170
|
// If full validation fails, try partial validation
|
|
148
|
-
|
|
171
|
+
validatedResults[i] = partialSchema.parse(item);
|
|
149
172
|
}
|
|
150
|
-
}
|
|
173
|
+
}
|
|
151
174
|
return validatedResults;
|
|
152
175
|
}
|
|
153
176
|
/**
|
|
@@ -193,11 +216,16 @@ class LokiConnector extends fluent_1.BaseConnector {
|
|
|
193
216
|
* @param data
|
|
194
217
|
*/
|
|
195
218
|
async replaceById(id, data) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
219
|
+
const value = await this.collection.findOne({ id });
|
|
220
|
+
// Avoid JSON parse/stringify overhead
|
|
221
|
+
const clonedValue = typeof structuredClone !== 'undefined' ? structuredClone(value) : JSON.parse(JSON.stringify(value));
|
|
222
|
+
const flatValue = js_utils_1.Objects.flatten(clonedValue);
|
|
223
|
+
const keys = Object.keys(flatValue);
|
|
224
|
+
const keysLength = keys.length;
|
|
225
|
+
const nullValue = null;
|
|
226
|
+
for (let i = 0; i < keysLength; i++) {
|
|
227
|
+
flatValue[keys[i]] = nullValue;
|
|
228
|
+
}
|
|
201
229
|
const nullObject = js_utils_1.Objects.nest(flatValue);
|
|
202
230
|
const newValue = { ...nullObject, ...data };
|
|
203
231
|
delete newValue._id;
|
|
@@ -207,7 +235,7 @@ class LokiConnector extends fluent_1.BaseConnector {
|
|
|
207
235
|
const dataToInsert = this.outputKeys.includes('updated')
|
|
208
236
|
? {
|
|
209
237
|
...data,
|
|
210
|
-
|
|
238
|
+
updated: new Date()
|
|
211
239
|
}
|
|
212
240
|
: data;
|
|
213
241
|
// For replace operations, use partial validation since we're replacing with only provided fields
|
|
@@ -220,12 +248,16 @@ class LokiConnector extends fluent_1.BaseConnector {
|
|
|
220
248
|
};
|
|
221
249
|
// Remove all fields except LokiJS metadata and validated fields
|
|
222
250
|
const lokiMetaFields = ['$loki', 'meta'];
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
251
|
+
const validatedKeys = Object.keys(validatedData);
|
|
252
|
+
const allowedFields = new Set([...lokiMetaFields, 'id', 'created', ...validatedKeys]);
|
|
253
|
+
const updatedKeys = Object.keys(updatedValue);
|
|
254
|
+
const updatedKeysLength = updatedKeys.length;
|
|
255
|
+
for (let i = 0; i < updatedKeysLength; i++) {
|
|
256
|
+
const key = updatedKeys[i];
|
|
257
|
+
if (!allowedFields.has(key)) {
|
|
226
258
|
delete updatedValue[key];
|
|
227
259
|
}
|
|
228
|
-
}
|
|
260
|
+
}
|
|
229
261
|
await this.collection.update(updatedValue);
|
|
230
262
|
const val = await this.collection.findOne({ id });
|
|
231
263
|
// For replace operations, use partial output schema since we only have the replaced fields
|
|
@@ -233,61 +265,44 @@ class LokiConnector extends fluent_1.BaseConnector {
|
|
|
233
265
|
return partialOutputSchema.parse(js_utils_1.Objects.clearEmpties(js_utils_1.Objects.deleteNulls(val)));
|
|
234
266
|
}
|
|
235
267
|
getLokiWhere(where) {
|
|
236
|
-
/*
|
|
237
|
-
|
|
238
|
-
if (this.relationQuery && this.relationQuery.data) {
|
|
239
|
-
const ids = this.relationQuery.data.map(
|
|
240
|
-
d => Ids.objectID(d.id) as unknown as ObjectID
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
andFilters.push([
|
|
244
|
-
this.relationQuery.relation.inverseSidePropertyPath,
|
|
245
|
-
'in',
|
|
246
|
-
ids
|
|
247
|
-
])
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (!andFilters || andFilters.length === 0) {
|
|
251
|
-
return filters
|
|
252
|
-
}
|
|
253
|
-
*/
|
|
254
268
|
if (!where || Object.keys(where).length === 0) {
|
|
255
269
|
return {};
|
|
256
270
|
}
|
|
257
271
|
const Filters = {
|
|
258
272
|
where: { $or: [{ $and: [] }] }
|
|
259
273
|
};
|
|
260
|
-
|
|
261
|
-
const
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
274
|
+
// Avoid cloning overhead - use destructuring
|
|
275
|
+
const { AND, OR, ...rootConditions } = where;
|
|
276
|
+
const orConditions = (0, fluent_1.extractConditions)((OR || []));
|
|
277
|
+
const andConditions = (0, fluent_1.extractConditions)((AND || []));
|
|
278
|
+
const rootLevelConditions = (0, fluent_1.extractConditions)([rootConditions]);
|
|
279
|
+
// Create operator map for O(1) lookup
|
|
280
|
+
const simpleOperatorMap = new Map([
|
|
281
|
+
[fluent_1.LogicOperator.equals, '$eq'],
|
|
282
|
+
[fluent_1.LogicOperator.isNot, '$neq'],
|
|
283
|
+
[fluent_1.LogicOperator.greaterThan, '$gt'],
|
|
284
|
+
[fluent_1.LogicOperator.greaterOrEqualThan, '$gte'],
|
|
285
|
+
[fluent_1.LogicOperator.lessThan, '$lt'],
|
|
286
|
+
[fluent_1.LogicOperator.lessOrEqualThan, '$lte'],
|
|
287
|
+
[fluent_1.LogicOperator.in, '$in'],
|
|
288
|
+
[fluent_1.LogicOperator.exists, '$exists'],
|
|
289
|
+
[fluent_1.LogicOperator.notExists, '$exists'],
|
|
290
|
+
[fluent_1.LogicOperator.regexp, '$regex']
|
|
291
|
+
]);
|
|
292
|
+
// Helper function to process conditions
|
|
293
|
+
const processCondition = (condition, target) => {
|
|
294
|
+
const { element, operator, value } = condition;
|
|
281
295
|
// Handle nested properties for LokiJS
|
|
282
296
|
if (element.includes('.')) {
|
|
283
297
|
const parts = element.split('.');
|
|
284
298
|
const nestedFilter = {};
|
|
285
299
|
let current = nestedFilter;
|
|
286
|
-
|
|
300
|
+
const partsLength = parts.length - 1;
|
|
301
|
+
for (let i = 0; i < partsLength; i++) {
|
|
287
302
|
current[parts[i]] = {};
|
|
288
303
|
current = current[parts[i]];
|
|
289
304
|
}
|
|
290
|
-
const lastPart = parts[
|
|
305
|
+
const lastPart = parts[partsLength];
|
|
291
306
|
switch (operator) {
|
|
292
307
|
case fluent_1.LogicOperator.equals:
|
|
293
308
|
current[lastPart] = value;
|
|
@@ -323,184 +338,60 @@ class LokiConnector extends fluent_1.BaseConnector {
|
|
|
323
338
|
current[lastPart] = { $regex: value };
|
|
324
339
|
break;
|
|
325
340
|
}
|
|
326
|
-
|
|
341
|
+
target.push(nestedFilter);
|
|
327
342
|
}
|
|
328
343
|
else {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
break;
|
|
345
|
-
case fluent_1.LogicOperator.lessOrEqualThan:
|
|
346
|
-
Filters.where.$or[0].$and.push({ [element]: { $lte: value } });
|
|
347
|
-
break;
|
|
348
|
-
case fluent_1.LogicOperator.in:
|
|
349
|
-
Filters.where.$or[0].$and.push({ [element]: { $in: value } });
|
|
350
|
-
break;
|
|
351
|
-
case fluent_1.LogicOperator.notIn:
|
|
352
|
-
Filters.where.$or[0].$and.push({
|
|
353
|
-
[element]: { $not: { $in: value } }
|
|
354
|
-
});
|
|
355
|
-
break;
|
|
356
|
-
case fluent_1.LogicOperator.exists:
|
|
357
|
-
Filters.where.$or[0].$and.push({ [element]: { $exists: true } });
|
|
358
|
-
break;
|
|
359
|
-
case fluent_1.LogicOperator.notExists:
|
|
360
|
-
Filters.where.$or[0].$and.push({ [element]: { $exists: false } });
|
|
361
|
-
break;
|
|
362
|
-
case fluent_1.LogicOperator.regexp:
|
|
363
|
-
Filters.where.$or[0].$and.push({ [element]: { $regex: value } });
|
|
364
|
-
break;
|
|
344
|
+
// Use map for O(1) operator lookup
|
|
345
|
+
const lokiOp = simpleOperatorMap.get(operator);
|
|
346
|
+
if (lokiOp) {
|
|
347
|
+
if (operator === fluent_1.LogicOperator.notExists) {
|
|
348
|
+
target.push({ [element]: { [lokiOp]: false } });
|
|
349
|
+
}
|
|
350
|
+
else if (operator === fluent_1.LogicOperator.exists) {
|
|
351
|
+
target.push({ [element]: { [lokiOp]: true } });
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
target.push({ [element]: { [lokiOp]: value } });
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
else if (operator === fluent_1.LogicOperator.notIn) {
|
|
358
|
+
target.push({ [element]: { $not: { $in: value } } });
|
|
365
359
|
}
|
|
366
360
|
}
|
|
361
|
+
};
|
|
362
|
+
// Process AND conditions
|
|
363
|
+
const andLength = andConditions.length;
|
|
364
|
+
for (let i = 0; i < andLength; i++) {
|
|
365
|
+
processCondition(andConditions[i], Filters.where.$or[0].$and);
|
|
367
366
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
current[parts[i]] = {};
|
|
385
|
-
current = current[parts[i]];
|
|
367
|
+
// Process root level conditions
|
|
368
|
+
const rootLength = rootLevelConditions.length;
|
|
369
|
+
for (let i = 0; i < rootLength; i++) {
|
|
370
|
+
processCondition(rootLevelConditions[i], Filters.where.$or[0].$and);
|
|
371
|
+
}
|
|
372
|
+
// Process OR conditions
|
|
373
|
+
const orLength = orConditions.length;
|
|
374
|
+
for (let i = 0; i < orLength; i++) {
|
|
375
|
+
const condition = orConditions[i];
|
|
376
|
+
const { element, operator, value } = condition;
|
|
377
|
+
const orFilter = {};
|
|
378
|
+
// Reuse operator map for consistency
|
|
379
|
+
const lokiOp = simpleOperatorMap.get(operator);
|
|
380
|
+
if (lokiOp) {
|
|
381
|
+
if (operator === fluent_1.LogicOperator.notExists) {
|
|
382
|
+
orFilter[element] = { [lokiOp]: false };
|
|
386
383
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
case fluent_1.LogicOperator.equals:
|
|
390
|
-
current[lastPart] = value;
|
|
391
|
-
break;
|
|
392
|
-
case fluent_1.LogicOperator.isNot:
|
|
393
|
-
current[lastPart] = { $ne: value };
|
|
394
|
-
break;
|
|
395
|
-
case fluent_1.LogicOperator.greaterThan:
|
|
396
|
-
current[lastPart] = { $gt: value };
|
|
397
|
-
break;
|
|
398
|
-
case fluent_1.LogicOperator.greaterOrEqualThan:
|
|
399
|
-
current[lastPart] = { $gte: value };
|
|
400
|
-
break;
|
|
401
|
-
case fluent_1.LogicOperator.lessThan:
|
|
402
|
-
current[lastPart] = { $lt: value };
|
|
403
|
-
break;
|
|
404
|
-
case fluent_1.LogicOperator.lessOrEqualThan:
|
|
405
|
-
current[lastPart] = { $lte: value };
|
|
406
|
-
break;
|
|
407
|
-
case fluent_1.LogicOperator.in:
|
|
408
|
-
current[lastPart] = { $in: value };
|
|
409
|
-
break;
|
|
410
|
-
case fluent_1.LogicOperator.notIn:
|
|
411
|
-
current[lastPart] = { $nin: value };
|
|
412
|
-
break;
|
|
413
|
-
case fluent_1.LogicOperator.exists:
|
|
414
|
-
current[lastPart] = { $exists: true };
|
|
415
|
-
break;
|
|
416
|
-
case fluent_1.LogicOperator.notExists:
|
|
417
|
-
current[lastPart] = { $exists: false };
|
|
418
|
-
break;
|
|
419
|
-
case fluent_1.LogicOperator.regexp:
|
|
420
|
-
current[lastPart] = { $regex: value };
|
|
421
|
-
break;
|
|
384
|
+
else if (operator === fluent_1.LogicOperator.exists) {
|
|
385
|
+
orFilter[element] = { [lokiOp]: true };
|
|
422
386
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
else {
|
|
426
|
-
switch (operator) {
|
|
427
|
-
case fluent_1.LogicOperator.equals:
|
|
428
|
-
Filters.where.$or[0].$and.push({ [element]: { $eq: value } });
|
|
429
|
-
break;
|
|
430
|
-
case fluent_1.LogicOperator.isNot:
|
|
431
|
-
Filters.where.$or[0].$and.push({ [element]: { $neq: value } });
|
|
432
|
-
break;
|
|
433
|
-
case fluent_1.LogicOperator.greaterThan:
|
|
434
|
-
Filters.where.$or[0].$and.push({ [element]: { $gt: value } });
|
|
435
|
-
break;
|
|
436
|
-
case fluent_1.LogicOperator.greaterOrEqualThan:
|
|
437
|
-
Filters.where.$or[0].$and.push({ [element]: { $gte: value } });
|
|
438
|
-
break;
|
|
439
|
-
case fluent_1.LogicOperator.lessThan:
|
|
440
|
-
Filters.where.$or[0].$and.push({ [element]: { $lt: value } });
|
|
441
|
-
break;
|
|
442
|
-
case fluent_1.LogicOperator.lessOrEqualThan:
|
|
443
|
-
Filters.where.$or[0].$and.push({ [element]: { $lte: value } });
|
|
444
|
-
break;
|
|
445
|
-
case fluent_1.LogicOperator.in:
|
|
446
|
-
Filters.where.$or[0].$and.push({ [element]: { $in: value } });
|
|
447
|
-
break;
|
|
448
|
-
case fluent_1.LogicOperator.notIn:
|
|
449
|
-
Filters.where.$or[0].$and.push({
|
|
450
|
-
[element]: { $not: { $in: value } }
|
|
451
|
-
});
|
|
452
|
-
break;
|
|
453
|
-
case fluent_1.LogicOperator.exists:
|
|
454
|
-
Filters.where.$or[0].$and.push({ [element]: { $exists: true } });
|
|
455
|
-
break;
|
|
456
|
-
case fluent_1.LogicOperator.notExists:
|
|
457
|
-
Filters.where.$or[0].$and.push({ [element]: { $exists: false } });
|
|
458
|
-
break;
|
|
459
|
-
case fluent_1.LogicOperator.regexp:
|
|
460
|
-
Filters.where.$or[0].$and.push({ [element]: { $regex: value } });
|
|
461
|
-
break;
|
|
387
|
+
else {
|
|
388
|
+
orFilter[element] = { [lokiOp]: value };
|
|
462
389
|
}
|
|
463
390
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
let { element, operator, value } = condition;
|
|
467
|
-
switch (operator) {
|
|
468
|
-
case fluent_1.LogicOperator.equals:
|
|
469
|
-
Filters.where.$or.push({ [element]: { $eq: value } });
|
|
470
|
-
break;
|
|
471
|
-
case fluent_1.LogicOperator.isNot:
|
|
472
|
-
Filters.where.$or.push({ [element]: { $neq: value } });
|
|
473
|
-
break;
|
|
474
|
-
case fluent_1.LogicOperator.greaterThan:
|
|
475
|
-
Filters.where.$or.push({ [element]: { $gt: value } });
|
|
476
|
-
break;
|
|
477
|
-
case fluent_1.LogicOperator.greaterOrEqualThan:
|
|
478
|
-
Filters.where.$or.push({ [element]: { $gte: value } });
|
|
479
|
-
break;
|
|
480
|
-
case fluent_1.LogicOperator.lessThan:
|
|
481
|
-
Filters.where.$or.push({ [element]: { $lt: value } });
|
|
482
|
-
break;
|
|
483
|
-
case fluent_1.LogicOperator.lessOrEqualThan:
|
|
484
|
-
Filters.where.$or.push({ [element]: { $lte: value } });
|
|
485
|
-
break;
|
|
486
|
-
case fluent_1.LogicOperator.in:
|
|
487
|
-
Filters.where.$or.push({ [element]: { $in: value } });
|
|
488
|
-
break;
|
|
489
|
-
case fluent_1.LogicOperator.notIn:
|
|
490
|
-
Filters.where.$or.push({
|
|
491
|
-
[element]: { $not: { $in: value } }
|
|
492
|
-
});
|
|
493
|
-
break;
|
|
494
|
-
case fluent_1.LogicOperator.exists:
|
|
495
|
-
Filters.where.$or.push({ [element]: { $exists: true } });
|
|
496
|
-
break;
|
|
497
|
-
case fluent_1.LogicOperator.notExists:
|
|
498
|
-
Filters.where.$or.push({ [element]: { $exists: false } });
|
|
499
|
-
break;
|
|
500
|
-
case fluent_1.LogicOperator.regexp:
|
|
501
|
-
Filters.where.$or.push({ [element]: { $regex: value } });
|
|
502
|
-
break;
|
|
391
|
+
else if (operator === fluent_1.LogicOperator.notIn) {
|
|
392
|
+
orFilter[element] = { $not: { $in: value } };
|
|
503
393
|
}
|
|
394
|
+
Filters.where.$or.push(orFilter);
|
|
504
395
|
}
|
|
505
396
|
// For simple queries without OR conditions, return a simpler format
|
|
506
397
|
if (Filters.where.$or.length === 1 && Filters.where.$or[0].$and.length === 1) {
|
|
@@ -508,23 +399,45 @@ class LokiConnector extends fluent_1.BaseConnector {
|
|
|
508
399
|
// For nested objects, LokiJS needs { "breed.family": "Angora" } format
|
|
509
400
|
// Check if this is a nested object filter
|
|
510
401
|
const keys = Object.keys(filter);
|
|
511
|
-
if (keys.length === 1
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
402
|
+
if (keys.length === 1) {
|
|
403
|
+
const firstKey = keys[0];
|
|
404
|
+
const firstValue = filter[firstKey];
|
|
405
|
+
if (typeof firstValue === 'object' &&
|
|
406
|
+
firstValue !== null &&
|
|
407
|
+
!Array.isArray(firstValue) &&
|
|
408
|
+
!firstValue.$eq && !firstValue.$ne &&
|
|
409
|
+
!firstValue.$gt && !firstValue.$gte &&
|
|
410
|
+
!firstValue.$lt && !firstValue.$lte &&
|
|
411
|
+
!firstValue.$in && !firstValue.$nin &&
|
|
412
|
+
!firstValue.$exists && !firstValue.$regex) {
|
|
413
|
+
// This is a nested object filter like { breed: { family: "Angora" } }
|
|
414
|
+
// Convert to dot notation for LokiJS
|
|
415
|
+
const result = {};
|
|
416
|
+
const flattenNestedObject = (obj, prefix = '') => {
|
|
417
|
+
const objKeys = Object.keys(obj);
|
|
418
|
+
const objKeysLength = objKeys.length;
|
|
419
|
+
for (let i = 0; i < objKeysLength; i++) {
|
|
420
|
+
const key = objKeys[i];
|
|
421
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
422
|
+
const value = obj[key];
|
|
423
|
+
if (typeof value === 'object' &&
|
|
424
|
+
value !== null &&
|
|
425
|
+
!Array.isArray(value) &&
|
|
426
|
+
!value.$eq && !value.$ne &&
|
|
427
|
+
!value.$gt && !value.$gte &&
|
|
428
|
+
!value.$lt && !value.$lte &&
|
|
429
|
+
!value.$in && !value.$nin &&
|
|
430
|
+
!value.$exists && !value.$regex) {
|
|
431
|
+
flattenNestedObject(value, fullKey);
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
result[fullKey] = value;
|
|
435
|
+
}
|
|
520
436
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
};
|
|
526
|
-
flattenNestedObject(filter);
|
|
527
|
-
return result;
|
|
437
|
+
};
|
|
438
|
+
flattenNestedObject(filter);
|
|
439
|
+
return result;
|
|
440
|
+
}
|
|
528
441
|
}
|
|
529
442
|
return filter;
|
|
530
443
|
}
|
|
@@ -545,16 +458,24 @@ class LokiConnector extends fluent_1.BaseConnector {
|
|
|
545
458
|
loadFirst(query) {
|
|
546
459
|
// Create a clone of the original class
|
|
547
460
|
// to avoid polluting attributes (relatedQuery)
|
|
548
|
-
const detachedClass =
|
|
461
|
+
const detachedClass = this.clone();
|
|
549
462
|
detachedClass.setRelatedQuery({
|
|
550
463
|
entity: this.entity,
|
|
551
464
|
repository: this,
|
|
552
|
-
query
|
|
465
|
+
query: {
|
|
466
|
+
...query,
|
|
467
|
+
limit: 1
|
|
468
|
+
}
|
|
553
469
|
});
|
|
554
470
|
return detachedClass;
|
|
555
471
|
}
|
|
556
472
|
clone() {
|
|
557
|
-
return new this.constructor(
|
|
473
|
+
return new this.constructor({
|
|
474
|
+
entity: this.entity,
|
|
475
|
+
dataSource: this.dataSource,
|
|
476
|
+
inputSchema: this.inputSchema,
|
|
477
|
+
outputSchema: this.outputSchema
|
|
478
|
+
});
|
|
558
479
|
}
|
|
559
480
|
loadById(id) {
|
|
560
481
|
// Create a new instance to avoid polluting the original one
|
|
@@ -582,34 +503,37 @@ class LokiConnector extends fluent_1.BaseConnector {
|
|
|
582
503
|
// Handle both string and object path formats
|
|
583
504
|
const pathKey = typeof path === 'string' ? path : Object.keys(js_utils_1.Objects.flatten(path))[0];
|
|
584
505
|
const result = [];
|
|
585
|
-
|
|
586
|
-
|
|
506
|
+
const dataArray = data;
|
|
507
|
+
const dataLength = dataArray.length;
|
|
508
|
+
for (let i = 0; i < dataLength; i++) {
|
|
509
|
+
const extracted = js_utils_1.Objects.getFromPath(dataArray[i], String(pathKey), undefined);
|
|
587
510
|
if (typeof extracted.value !== 'undefined') {
|
|
588
511
|
result.push(extracted.value);
|
|
589
512
|
}
|
|
590
513
|
}
|
|
591
514
|
return result;
|
|
592
515
|
}
|
|
516
|
+
// Static map for better performance
|
|
517
|
+
static lokiOperatorMap = new Map([
|
|
518
|
+
['=', '$eq'],
|
|
519
|
+
['<', '$lt'],
|
|
520
|
+
['>', '$gt'],
|
|
521
|
+
['<=', '$lte'],
|
|
522
|
+
['>=', '$gte'],
|
|
523
|
+
['<>', '$ne'],
|
|
524
|
+
['!=', '$ne'],
|
|
525
|
+
['in', '$in'],
|
|
526
|
+
['nin', '$nin'],
|
|
527
|
+
['like', '$aeq'],
|
|
528
|
+
['regexp', '$regex'],
|
|
529
|
+
['startsWith', '$regex|^{{$var}}'],
|
|
530
|
+
['endsWith', '$regex|{{$var}}$'],
|
|
531
|
+
['contains', '$regex|{{$var}}']
|
|
532
|
+
]);
|
|
593
533
|
getLokiOperator(operator) {
|
|
594
|
-
const
|
|
595
|
-
'=': '$eq',
|
|
596
|
-
'<': '$lt',
|
|
597
|
-
'>': '$gt',
|
|
598
|
-
'<=': '$lte',
|
|
599
|
-
'>=': '$gte',
|
|
600
|
-
'<>': '$ne',
|
|
601
|
-
'!=': '$ne',
|
|
602
|
-
in: '$in',
|
|
603
|
-
nin: '$nin',
|
|
604
|
-
like: '$aeq',
|
|
605
|
-
regexp: '$regex',
|
|
606
|
-
startsWith: '$regex|^{{$var}}',
|
|
607
|
-
endsWith: '$regex|{{$var}}$',
|
|
608
|
-
contains: '$regex|{{$var}}'
|
|
609
|
-
};
|
|
610
|
-
const converted = js_utils_1.Objects.get(() => lokiOperators[operator], undefined);
|
|
534
|
+
const converted = LokiConnector.lokiOperatorMap.get(operator);
|
|
611
535
|
if (!converted) {
|
|
612
|
-
throw new Error(`The operator "${operator}" is not supported in Loki
|
|
536
|
+
throw new Error(`The operator "${operator}" is not supported in Loki`);
|
|
613
537
|
}
|
|
614
538
|
return converted;
|
|
615
539
|
}
|