@twin.org/entity-storage-connector-scylladb 0.0.3-next.2 → 0.0.3-next.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -10
- package/dist/es/abstractScyllaDBConnector.js +203 -93
- package/dist/es/abstractScyllaDBConnector.js.map +1 -1
- package/dist/es/models/IScyllaDBTableConfig.js.map +1 -1
- package/dist/es/models/IScyllaDBViewConfig.js.map +1 -1
- package/dist/es/scyllaDBTableConnector.js +419 -49
- package/dist/es/scyllaDBTableConnector.js.map +1 -1
- package/dist/es/scyllaDBViewConnector.js +39 -3
- package/dist/es/scyllaDBViewConnector.js.map +1 -1
- package/dist/types/abstractScyllaDBConnector.d.ts +12 -0
- package/dist/types/models/IScyllaDBTableConfig.d.ts +1 -1
- package/dist/types/models/IScyllaDBViewConfig.d.ts +2 -2
- package/dist/types/scyllaDBTableConnector.d.ts +53 -6
- package/dist/types/scyllaDBViewConnector.d.ts +20 -0
- package/docs/changelog.md +410 -47
- package/docs/examples.md +123 -1
- package/docs/reference/classes/ScyllaDBTableConnector.md +308 -22
- package/docs/reference/classes/ScyllaDBViewConnector.md +154 -8
- package/docs/reference/interfaces/IScyllaDBConfig.md +5 -5
- package/docs/reference/interfaces/IScyllaDBTableConfig.md +7 -7
- package/docs/reference/interfaces/IScyllaDBTableConnectorConstructorOptions.md +6 -6
- package/docs/reference/interfaces/IScyllaDBViewConfig.md +10 -10
- package/docs/reference/interfaces/IScyllaDBViewConnectorConstructorOptions.md +7 -7
- package/locales/en.json +22 -5
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Entity Storage Connector ScyllaDB
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This package provides a ScyllaDB backend for distributed workloads that need low latency and high throughput. It is designed to work with the wider storage ecosystem so applications can keep behaviour consistent across connectors and environments.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,18 +8,12 @@ Entity Storage connector implementation using ScyllaDB.
|
|
|
8
8
|
npm install @twin.org/entity-storage-connector-scylladb
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Docker
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
To perform testing of this component it may be necessary to launch a local instance to communicate with.
|
|
14
14
|
|
|
15
15
|
```shell
|
|
16
|
-
docker run -
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Afterwards you can run the tests as follows:
|
|
20
|
-
|
|
21
|
-
```shell
|
|
22
|
-
npm run test
|
|
16
|
+
docker run -d --name twin-entity-storage-scylladb -p 9500:9042 scylladb/scylla:5.4.9
|
|
23
17
|
```
|
|
24
18
|
|
|
25
19
|
## Examples
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// Copyright 2024 IOTA Stiftung.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
3
|
import { ContextIdHelper, ContextIdStore } from "@twin.org/context";
|
|
4
|
-
import { Coerce, ComponentFactory, GeneralError, Guards, Is
|
|
4
|
+
import { Coerce, ComponentFactory, GeneralError, Guards, Is } from "@twin.org/core";
|
|
5
5
|
import { ComparisonOperator, EntitySchemaFactory, EntitySchemaHelper, LogicalOperator, SortDirection } from "@twin.org/entity";
|
|
6
|
+
import { EntityStorageHelper } from "@twin.org/entity-storage-models";
|
|
6
7
|
import { types as CassandraTypes, Client } from "cassandra-driver";
|
|
7
8
|
/**
|
|
8
9
|
* Store entities using ScyllaDB.
|
|
@@ -26,7 +27,7 @@ export class AbstractScyllaDBConnector {
|
|
|
26
27
|
* Limit the number of entities when finding.
|
|
27
28
|
* @internal
|
|
28
29
|
*/
|
|
29
|
-
static
|
|
30
|
+
static DEFAULT_LIMIT = 40;
|
|
30
31
|
/**
|
|
31
32
|
* The name of the database table.
|
|
32
33
|
* @internal
|
|
@@ -57,6 +58,13 @@ export class AbstractScyllaDBConnector {
|
|
|
57
58
|
* @internal
|
|
58
59
|
*/
|
|
59
60
|
_primaryKey;
|
|
61
|
+
/**
|
|
62
|
+
* Cached persistent client (keyspace-scoped). Reused across all operations on this
|
|
63
|
+
* connector instance so the expensive cassandra-driver `connect()` only runs once.
|
|
64
|
+
* Closed by `closePersistentClient()` which callers (e.g. `teardown()`) must invoke.
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
_persistentClient;
|
|
60
68
|
/**
|
|
61
69
|
* Create a new instance of AbstractScyllaDBConnector.
|
|
62
70
|
* @param options The options for the connector.
|
|
@@ -77,7 +85,7 @@ export class AbstractScyllaDBConnector {
|
|
|
77
85
|
this._partitionContextIds = options.partitionContextIds;
|
|
78
86
|
this._primaryKey = EntitySchemaHelper.getPrimaryKey(this._entitySchema);
|
|
79
87
|
this._config = options.config;
|
|
80
|
-
this._fullTableName =
|
|
88
|
+
this._fullTableName = options.config.tableName;
|
|
81
89
|
}
|
|
82
90
|
/**
|
|
83
91
|
* Returns the class name of the component.
|
|
@@ -117,7 +125,7 @@ export class AbstractScyllaDBConnector {
|
|
|
117
125
|
value: id
|
|
118
126
|
});
|
|
119
127
|
const { sqlCondition, conditionValues } = this.buildConditions(conditions);
|
|
120
|
-
let sql = `SELECT * FROM "${this._fullTableName}" WHERE ${sqlCondition}`;
|
|
128
|
+
let sql = `SELECT * FROM "${this.safeTableName(this._fullTableName)}" WHERE ${sqlCondition}`;
|
|
121
129
|
if (secondaryIndex) {
|
|
122
130
|
sql += " ALLOW FILTERING";
|
|
123
131
|
}
|
|
@@ -157,100 +165,28 @@ export class AbstractScyllaDBConnector {
|
|
|
157
165
|
let connection;
|
|
158
166
|
const contextIds = await ContextIdStore.getContextIds();
|
|
159
167
|
const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
|
|
168
|
+
// Validates and throws for unsupported conditions before entering the try-catch
|
|
169
|
+
// so that comparisonNotSupported errors surface directly to the caller.
|
|
170
|
+
const { whereClause, params } = this.buildCqlConditions(conditions, partitionKey);
|
|
160
171
|
try {
|
|
161
|
-
|
|
162
|
-
let sql = `SELECT * FROM "${this._fullTableName}"`;
|
|
172
|
+
const returnSize = limit ?? AbstractScyllaDBConnector.DEFAULT_LIMIT;
|
|
173
|
+
let sql = `SELECT * FROM "${this.safeTableName(this._fullTableName)}"`;
|
|
163
174
|
if (Is.array(properties)) {
|
|
164
175
|
const fields = [];
|
|
165
176
|
for (const property of properties) {
|
|
166
177
|
fields.push(property.toString());
|
|
167
178
|
}
|
|
168
|
-
|
|
169
|
-
sql = sql.replace("*", selectFields);
|
|
170
|
-
}
|
|
171
|
-
const conds = [];
|
|
172
|
-
let conditionQuery = "";
|
|
173
|
-
// The params to be used to execute the query
|
|
174
|
-
const params = [];
|
|
175
|
-
let finalConditionQuery = `"${AbstractScyllaDBConnector.PARTITION_KEY}" = ?`;
|
|
176
|
-
params.push(partitionKey ?? AbstractScyllaDBConnector.PARTITION_KEY_VALUE);
|
|
177
|
-
let theConditions = [];
|
|
178
|
-
if (!Is.undefined(conditions)) {
|
|
179
|
-
if ("conditions" in conditions) {
|
|
180
|
-
theConditions = conditions.conditions;
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
theConditions.push(conditions);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
// TODO: This code needs refactoring to support conditions for sub properties.
|
|
187
|
-
for (const cond of theConditions) {
|
|
188
|
-
const condition = cond;
|
|
189
|
-
const descriptor = this._entitySchema.properties?.find(p => p.property === condition.property);
|
|
190
|
-
if (condition.comparison === ComparisonOperator.Includes ||
|
|
191
|
-
condition.comparison === ComparisonOperator.NotIncludes) {
|
|
192
|
-
const propValue = `'%${condition.value?.toString()}%'`;
|
|
193
|
-
if (condition.comparison === ComparisonOperator.Includes) {
|
|
194
|
-
conds.push(`"${condition.property}" LIKE ${propValue}`);
|
|
195
|
-
}
|
|
196
|
-
else if (condition.comparison === ComparisonOperator.NotIncludes) {
|
|
197
|
-
conds.push(`"${condition.property}" NOT LIKE ${propValue}`);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
else if (condition.comparison === ComparisonOperator.In) {
|
|
201
|
-
let value = [];
|
|
202
|
-
if (!Is.arrayValue(condition.value)) {
|
|
203
|
-
value.push(this.propertyToDbValue(condition.value, descriptor));
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
value = condition.value.map(v => this.propertyToDbValue(v, descriptor));
|
|
207
|
-
}
|
|
208
|
-
params.push(value);
|
|
209
|
-
conds.push(`"${condition.property}" IN ?`);
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
const propValue = condition.value;
|
|
213
|
-
params.push(propValue);
|
|
214
|
-
if (condition.comparison === ComparisonOperator.Equals) {
|
|
215
|
-
conds.push(`"${condition.property}" = ?`);
|
|
216
|
-
}
|
|
217
|
-
else if (condition.comparison === ComparisonOperator.NotEquals) {
|
|
218
|
-
conds.push(`"${condition.property}" <> ?`);
|
|
219
|
-
}
|
|
220
|
-
else if (condition.comparison === ComparisonOperator.GreaterThan) {
|
|
221
|
-
conds.push(`"${condition.property}" > ?`);
|
|
222
|
-
}
|
|
223
|
-
else if (condition.comparison === ComparisonOperator.LessThan) {
|
|
224
|
-
conds.push(`"${condition.property}" < ?`);
|
|
225
|
-
}
|
|
226
|
-
else if (condition.comparison === ComparisonOperator.GreaterThanOrEqual) {
|
|
227
|
-
conds.push(`"${condition.property}" >= ?`);
|
|
228
|
-
}
|
|
229
|
-
else if (condition.comparison === ComparisonOperator.LessThanOrEqual) {
|
|
230
|
-
conds.push(`"${condition.property}" <= ?`);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
const operator = conditions.logicalOperator ?? LogicalOperator.And;
|
|
234
|
-
conditionQuery = `${conds.join(` ${operator} `)}`;
|
|
235
|
-
}
|
|
236
|
-
if (conditionQuery.length > 0) {
|
|
237
|
-
finalConditionQuery += ` AND (${conditionQuery})`;
|
|
179
|
+
sql = sql.replace("*", fields.join(","));
|
|
238
180
|
}
|
|
239
|
-
sql += ` WHERE ${
|
|
240
|
-
connection = await this.openConnection();
|
|
241
|
-
// TODO: Only supported one sort property at the moment. This code would need to be revised in a follow-up
|
|
181
|
+
sql += ` WHERE ${whereClause}`;
|
|
242
182
|
if (Is.array(sortProperties) && sortProperties.length >= 1) {
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
sqlSortDir = "desc";
|
|
249
|
-
}
|
|
250
|
-
sql += ` ORDER BY "${String(sortKey)}" ${sqlSortDir.toUpperCase()}`;
|
|
251
|
-
// Disabling paging in order by situations
|
|
252
|
-
returnSize = 0;
|
|
183
|
+
const orderClauses = sortProperties.map(sp => {
|
|
184
|
+
const dir = sp.sortDirection === SortDirection.Descending ? "DESC" : "ASC";
|
|
185
|
+
return `"${String(sp.property)}" ${dir}`;
|
|
186
|
+
});
|
|
187
|
+
sql += ` ORDER BY ${orderClauses.join(", ")}`;
|
|
253
188
|
}
|
|
189
|
+
connection = await this.openConnection();
|
|
254
190
|
sql += " ALLOW FILTERING";
|
|
255
191
|
await this._logging?.log({
|
|
256
192
|
level: "info",
|
|
@@ -264,13 +200,46 @@ export class AbstractScyllaDBConnector {
|
|
|
264
200
|
for (const row of result.rows) {
|
|
265
201
|
entities.push(this.convertRowToObject(this._entitySchema.properties, row));
|
|
266
202
|
}
|
|
203
|
+
// ScyllaDB may return a pageState even when the current page is the last one
|
|
204
|
+
// (when rows.length == fetchSize). Peek at the next page to verify there are
|
|
205
|
+
// actually more rows before surfacing the cursor to the caller.
|
|
206
|
+
let nextCursor;
|
|
207
|
+
if (returnSize > 0 && result.rows.length >= returnSize && Is.stringValue(result.pageState)) {
|
|
208
|
+
const peek = await this.queryDB(connection, sql, params, result.pageState, 1);
|
|
209
|
+
if (peek.rows.length > 0) {
|
|
210
|
+
nextCursor = result.pageState;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
267
213
|
return {
|
|
268
214
|
entities,
|
|
269
|
-
cursor:
|
|
215
|
+
cursor: nextCursor
|
|
270
216
|
};
|
|
271
217
|
}
|
|
272
218
|
catch (error) {
|
|
273
|
-
throw new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, "findFailed", { table: this._fullTableName }, error);
|
|
219
|
+
throw new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, "findFailed", { table: this.safeTableName(this._fullTableName) }, error);
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
await this.closeConnection(connection);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Count all the entities which match the conditions.
|
|
227
|
+
* @param conditions The optional conditions to match for the entities.
|
|
228
|
+
* @returns The total count of entities in the storage.
|
|
229
|
+
*/
|
|
230
|
+
async count(conditions) {
|
|
231
|
+
let connection;
|
|
232
|
+
try {
|
|
233
|
+
const contextIds = await ContextIdStore.getContextIds();
|
|
234
|
+
const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
|
|
235
|
+
const { whereClause, params } = this.buildCqlConditions(conditions, partitionKey);
|
|
236
|
+
const sql = `SELECT COUNT(*) FROM "${this.safeTableName(this._fullTableName)}" WHERE ${whereClause} ALLOW FILTERING`;
|
|
237
|
+
connection = await this.openConnection();
|
|
238
|
+
const result = await this.queryDB(connection, sql, params);
|
|
239
|
+
return Number(result.rows[0]?.get("count") ?? 0);
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
throw new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, "countFailed", undefined, err);
|
|
274
243
|
}
|
|
275
244
|
finally {
|
|
276
245
|
await this.closeConnection(connection);
|
|
@@ -284,6 +253,11 @@ export class AbstractScyllaDBConnector {
|
|
|
284
253
|
* @internal
|
|
285
254
|
*/
|
|
286
255
|
async openConnection(skipKeySpace = false) {
|
|
256
|
+
// Reuse the cached keyspace-scoped client when available (avoids repeated
|
|
257
|
+
// cassandra-driver cluster-discovery on every operation).
|
|
258
|
+
if (!skipKeySpace && this._persistentClient !== undefined) {
|
|
259
|
+
return this._persistentClient;
|
|
260
|
+
}
|
|
287
261
|
const client = new Client({
|
|
288
262
|
contactPoints: this._config.hosts,
|
|
289
263
|
localDataCenter: this._config.localDataCenter,
|
|
@@ -293,10 +267,16 @@ export class AbstractScyllaDBConnector {
|
|
|
293
267
|
}
|
|
294
268
|
});
|
|
295
269
|
await client.connect();
|
|
270
|
+
if (!skipKeySpace) {
|
|
271
|
+
this._persistentClient = client;
|
|
272
|
+
}
|
|
296
273
|
return client;
|
|
297
274
|
}
|
|
298
275
|
/**
|
|
299
276
|
* Close database connection.
|
|
277
|
+
* When `connection` is the cached persistent client it is kept alive so it
|
|
278
|
+
* can be reused by future operations; call `closePersistentClient()` to
|
|
279
|
+
* explicitly shut it down (e.g. from `teardown()`).
|
|
300
280
|
* @param connection The connection to close.
|
|
301
281
|
* @internal
|
|
302
282
|
*/
|
|
@@ -304,8 +284,23 @@ export class AbstractScyllaDBConnector {
|
|
|
304
284
|
if (!connection) {
|
|
305
285
|
return;
|
|
306
286
|
}
|
|
287
|
+
if (connection === this._persistentClient) {
|
|
288
|
+
// Keep the persistent client alive for reuse.
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
307
291
|
return connection.shutdown();
|
|
308
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Shut down and clear the persistent client. Call this from `teardown()`
|
|
295
|
+
* implementations to release the underlying TCP connection.
|
|
296
|
+
* @internal
|
|
297
|
+
*/
|
|
298
|
+
async closePersistentClient() {
|
|
299
|
+
if (this._persistentClient !== undefined) {
|
|
300
|
+
await this._persistentClient.shutdown();
|
|
301
|
+
this._persistentClient = undefined;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
309
304
|
/**
|
|
310
305
|
* Query the database.
|
|
311
306
|
* @param connection The connection to query.
|
|
@@ -321,7 +316,7 @@ export class AbstractScyllaDBConnector {
|
|
|
321
316
|
connection.eachRow(sql, params, {
|
|
322
317
|
prepare: true,
|
|
323
318
|
autoPage: false,
|
|
324
|
-
fetchSize: limit ?? AbstractScyllaDBConnector.
|
|
319
|
+
fetchSize: limit ?? AbstractScyllaDBConnector.DEFAULT_LIMIT,
|
|
325
320
|
pageState
|
|
326
321
|
}, (n, row) => {
|
|
327
322
|
rows.push(row);
|
|
@@ -475,7 +470,7 @@ export class AbstractScyllaDBConnector {
|
|
|
475
470
|
obj[field.property] = this.dbValueToProperty(value, field);
|
|
476
471
|
}
|
|
477
472
|
}
|
|
478
|
-
return
|
|
473
|
+
return EntityStorageHelper.unPrepareEntity(obj, [AbstractScyllaDBConnector.PARTITION_KEY]);
|
|
479
474
|
}
|
|
480
475
|
/**
|
|
481
476
|
* Wrap a string for DB format.
|
|
@@ -541,5 +536,120 @@ export class AbstractScyllaDBConnector {
|
|
|
541
536
|
}
|
|
542
537
|
return { sqlCondition: sqlConditions.join(" AND "), conditionValues };
|
|
543
538
|
}
|
|
539
|
+
/**
|
|
540
|
+
* Get a safe table name by replacing any non-alphanumeric characters.
|
|
541
|
+
* @param name The name to sanitize.
|
|
542
|
+
* @returns The safe table name.
|
|
543
|
+
*/
|
|
544
|
+
safeTableName(name) {
|
|
545
|
+
return name.replace(/[^\dA-Za-z]/g, "");
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Parse, validate, and build a CQL WHERE clause from an EntityCondition tree.
|
|
549
|
+
* The partition key equality is always the first clause; user conditions follow.
|
|
550
|
+
* @param conditions The optional conditions to match for the entities.
|
|
551
|
+
* @param partitionKey The partition key value to filter by.
|
|
552
|
+
* @returns The complete WHERE clause (without the WHERE keyword) and bound params.
|
|
553
|
+
* @internal
|
|
554
|
+
*/
|
|
555
|
+
buildCqlConditions(conditions, partitionKey) {
|
|
556
|
+
let conditionsList = [];
|
|
557
|
+
if (conditions !== undefined) {
|
|
558
|
+
if ("conditions" in conditions) {
|
|
559
|
+
if (conditions.logicalOperator === LogicalOperator.Or) {
|
|
560
|
+
throw new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, "orConditionNotSupported");
|
|
561
|
+
}
|
|
562
|
+
conditionsList = conditions.conditions;
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
conditionsList = [conditions];
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
for (const cond of conditionsList) {
|
|
569
|
+
const comparator = cond;
|
|
570
|
+
if (String(comparator.property).includes(".")) {
|
|
571
|
+
throw new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, "comparisonNotSupported", {
|
|
572
|
+
property: comparator.property,
|
|
573
|
+
reason: "dot-notation nested property paths are not supported in CQL"
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
if ((comparator.comparison === ComparisonOperator.Equals ||
|
|
577
|
+
comparator.comparison === ComparisonOperator.NotEquals) &&
|
|
578
|
+
(comparator.value === null || comparator.value === undefined)) {
|
|
579
|
+
throw new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, "comparisonNotSupported", {
|
|
580
|
+
property: comparator.property,
|
|
581
|
+
reason: "null/undefined comparisons are not supported in CQL WHERE clauses"
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
if (comparator.comparison === ComparisonOperator.NotEquals) {
|
|
585
|
+
throw new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, "notEqualsNotSupported", {
|
|
586
|
+
property: comparator.property
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
if (comparator.comparison === ComparisonOperator.NotIncludes) {
|
|
590
|
+
throw new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, "notIncludesNotSupported", {
|
|
591
|
+
property: comparator.property
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
const conds = [];
|
|
596
|
+
const params = [partitionKey ?? AbstractScyllaDBConnector.PARTITION_KEY_VALUE];
|
|
597
|
+
for (const cond of conditionsList) {
|
|
598
|
+
const condition = cond;
|
|
599
|
+
const descriptor = this._entitySchema.properties?.find(p => p.property === condition.property);
|
|
600
|
+
if (condition.comparison === ComparisonOperator.Includes ||
|
|
601
|
+
condition.comparison === ComparisonOperator.NotIncludes) {
|
|
602
|
+
const serialized = this.propertyToDbValue(condition.value, descriptor);
|
|
603
|
+
const propValue = `'%${Is.stringValue(serialized) ? serialized : ""}%'`;
|
|
604
|
+
if (condition.comparison === ComparisonOperator.Includes) {
|
|
605
|
+
conds.push(`"${condition.property}" LIKE ${propValue}`);
|
|
606
|
+
}
|
|
607
|
+
else if (condition.comparison === ComparisonOperator.NotIncludes) {
|
|
608
|
+
conds.push(`"${condition.property}" NOT LIKE ${propValue}`);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
else if (condition.comparison === ComparisonOperator.In) {
|
|
612
|
+
let value = [];
|
|
613
|
+
if (!Is.arrayValue(condition.value)) {
|
|
614
|
+
value.push(this.propertyToDbValue(condition.value, descriptor));
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
value = condition.value.map(v => this.propertyToDbValue(v, descriptor));
|
|
618
|
+
}
|
|
619
|
+
params.push(value);
|
|
620
|
+
conds.push(`"${condition.property}" IN ?`);
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
const propValue = this.propertyToDbValue(condition.value, descriptor);
|
|
624
|
+
params.push(propValue);
|
|
625
|
+
if (condition.comparison === ComparisonOperator.Equals) {
|
|
626
|
+
conds.push(`"${condition.property}" = ?`);
|
|
627
|
+
}
|
|
628
|
+
else if (condition.comparison === ComparisonOperator.NotEquals) {
|
|
629
|
+
conds.push(`"${condition.property}" != ?`);
|
|
630
|
+
}
|
|
631
|
+
else if (condition.comparison === ComparisonOperator.GreaterThan) {
|
|
632
|
+
conds.push(`"${condition.property}" > ?`);
|
|
633
|
+
}
|
|
634
|
+
else if (condition.comparison === ComparisonOperator.LessThan) {
|
|
635
|
+
conds.push(`"${condition.property}" < ?`);
|
|
636
|
+
}
|
|
637
|
+
else if (condition.comparison === ComparisonOperator.GreaterThanOrEqual) {
|
|
638
|
+
conds.push(`"${condition.property}" >= ?`);
|
|
639
|
+
}
|
|
640
|
+
else if (condition.comparison === ComparisonOperator.LessThanOrEqual) {
|
|
641
|
+
conds.push(`"${condition.property}" <= ?`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
const operator = "conditions" in (conditions ?? {})
|
|
646
|
+
? (conditions.logicalOperator ?? LogicalOperator.And)
|
|
647
|
+
: LogicalOperator.And;
|
|
648
|
+
let whereClause = `"${AbstractScyllaDBConnector.PARTITION_KEY}" = ?`;
|
|
649
|
+
if (conds.length > 0) {
|
|
650
|
+
whereClause += ` AND ${conds.join(` ${operator} `)}`;
|
|
651
|
+
}
|
|
652
|
+
return { whereClause, params };
|
|
653
|
+
}
|
|
544
654
|
}
|
|
545
655
|
//# sourceMappingURL=abstractScyllaDBConnector.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abstractScyllaDBConnector.js","sourceRoot":"","sources":["../../src/abstractScyllaDBConnector.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EACN,MAAM,EACN,gBAAgB,EAChB,YAAY,EACZ,MAAM,EACN,EAAE,EACF,YAAY,EACZ,YAAY,EACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,aAAa,EAMb,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,KAAK,IAAI,cAAc,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAInE;;GAEG;AACH,MAAM,OAAgB,yBAAyB;IAC9C;;OAEG;IACI,MAAM,CAAU,UAAU,+BAAwD;IAEzF;;;OAGG;IACO,MAAM,CAAU,aAAa,GAAW,aAAa,CAAC;IAEhE;;;OAGG;IACO,MAAM,CAAU,mBAAmB,GAAW,MAAM,CAAC;IAE/D;;;OAGG;IACK,MAAM,CAAU,cAAc,GAAW,EAAE,CAAC;IAEpD;;;OAGG;IACO,cAAc,CAAS;IAEjC;;;OAGG;IACgB,OAAO,CAAkB;IAE5C;;;OAGG;IACgB,QAAQ,CAAqB;IAEhD;;;OAGG;IACgB,aAAa,CAAmB;IAEnD;;;OAGG;IACgB,oBAAoB,CAAY;IAEnD;;;OAGG;IACgB,WAAW,CAA2B;IAEzD;;;;;;;OAOG;IACH,YAAY,OAKX;QACA,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QAC9E,MAAM,CAAC,WAAW,CACjB,yBAAyB,CAAC,UAAU,0BAEpC,OAAO,CAAC,YAAY,CACpB,CAAC;QACF,MAAM,CAAC,MAAM,CACZ,yBAAyB,CAAC,UAAU,oBAEpC,OAAO,CAAC,MAAM,CACd,CAAC;QACF,MAAM,CAAC,UAAU,CAChB,yBAAyB,CAAC,UAAU,0BAEpC,OAAO,CAAC,MAAM,CAAC,KAAK,CACpB,CAAC;QACF,MAAM,CAAC,WAAW,CACjB,yBAAyB,CAAC,UAAU,oCAEpC,OAAO,CAAC,MAAM,CAAC,eAAe,CAC9B,CAAC;QACF,MAAM,CAAC,WAAW,CACjB,yBAAyB,CAAC,UAAU,6BAEpC,OAAO,CAAC,MAAM,CAAC,QAAQ,CACvB,CAAC;QAEF,IAAI,CAAC,QAAQ,GAAG,gBAAgB,CAAC,WAAW,CAAC,OAAO,CAAC,oBAAoB,IAAI,SAAS,CAAC,CAAC;QAExF,IAAI,CAAC,aAAa,GAAG,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACnE,IAAI,CAAC,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;QACxD,IAAI,CAAC,WAAW,GAAG,kBAAkB,CAAC,aAAa,CAAI,IAAI,CAAC,aAAa,CAAC,CAAC;QAE3E,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,IAAI,CAAC,cAAc,GAAG,YAAY,CAAC,SAAS,CAC3C,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAC1F,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,yBAAyB,CAAC,UAAU,CAAC;IAC7C,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,IAAI,CAAC,aAA8B,CAAC;IAC5C,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,GAAG,CACf,EAAU,EACV,cAAwB,EACxB,UAAoD;QAEpD,MAAM,CAAC,WAAW,CAAC,yBAAyB,CAAC,UAAU,QAAc,EAAE,CAAC,CAAC;QAEzE,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;QACxD,MAAM,YAAY,GAAG,eAAe,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAE/F,IAAI,UAAU,CAAC;QACf,IAAI,CAAC;YACJ,MAAM,UAAU,GAAG,cAAc,IAAI,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC;YAEhE,UAAU,KAAK,EAAE,CAAC;YAClB,UAAU,CAAC,OAAO,CAAC;gBAClB,QAAQ,EAAE,yBAAyB,CAAC,aAAwB;gBAC5D,KAAK,EAAE,YAAY,IAAI,yBAAyB,CAAC,mBAAmB;aACpE,CAAC,CAAC;YACH,UAAU,CAAC,OAAO,CAAC;gBAClB,QAAQ,EAAE,UAAU;gBACpB,KAAK,EAAE,EAAE;aACT,CAAC,CAAC;YAEH,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;YAE3E,IAAI,GAAG,GAAG,kBAAkB,IAAI,CAAC,cAAc,WAAW,YAAY,EAAE,CAAC;YAEzE,IAAI,cAAc,EAAE,CAAC;gBACpB,GAAG,IAAI,kBAAkB,CAAC;YAC3B,CAAC;YAED,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;gBACxB,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,yBAAyB,CAAC,UAAU;gBAC5C,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;gBACd,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,EAAE,GAAG,EAAE;aACb,CAAC,CAAC;YAEH,UAAU,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;YAEzC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,EAAE,eAAe,CAAC,CAAC;YAEpE,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/E,CAAC;QACF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,YAAY,CACrB,yBAAyB,CAAC,UAAU,EACpC,WAAW,EACX;gBACC,EAAE;aACF,EACD,KAAK,CACL,CAAC;QACH,CAAC;gBAAS,CAAC;YACV,MAAM,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QACxC,CAAC;IACF,CAAC;IAED;;;;;;;;;OASG;IACI,KAAK,CAAC,KAAK,CACjB,UAA+B,EAC/B,cAGG,EACH,UAAwB,EACxB,MAAe,EACf,KAAc;QAWd,IAAI,UAAU,CAAC;QAEf,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;QACxD,MAAM,YAAY,GAAG,eAAe,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAE/F,IAAI,CAAC;YACJ,IAAI,UAAU,GAAG,KAAK,IAAI,yBAAyB,CAAC,cAAc,CAAC;YACnE,IAAI,GAAG,GAAG,kBAAkB,IAAI,CAAC,cAAc,GAAG,CAAC;YAEnD,IAAI,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC1B,MAAM,MAAM,GAAa,EAAE,CAAC;gBAE5B,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;oBACnC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAClC,CAAC;gBAED,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACtC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;YACtC,CAAC;YAED,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,IAAI,cAAc,GAAG,EAAE,CAAC;YACxB,6CAA6C;YAC7C,MAAM,MAAM,GAAc,EAAE,CAAC;YAE7B,IAAI,mBAAmB,GAAG,IAAI,yBAAyB,CAAC,aAAa,OAAO,CAAC;YAC7E,MAAM,CAAC,IAAI,CAAC,YAAY,IAAI,yBAAyB,CAAC,mBAAmB,CAAC,CAAC;YAE3E,IAAI,aAAa,GAAyB,EAAE,CAAC;YAC7C,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC/B,IAAI,YAAY,IAAI,UAAU,EAAE,CAAC;oBAChC,aAAa,GAAI,UAA+B,CAAC,UAAU,CAAC;gBAC7D,CAAC;qBAAM,CAAC;oBACP,aAAa,CAAC,IAAI,CAAC,UAAgC,CAAC,CAAC;gBACtD,CAAC;YACF,CAAC;YAED,8EAA8E;YAC9E,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;gBAClC,MAAM,SAAS,GAAG,IAAmB,CAAC;gBAEtC,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CACrD,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,QAAQ,CACtC,CAAC;gBACF,IACC,SAAS,CAAC,UAAU,KAAK,kBAAkB,CAAC,QAAQ;oBACpD,SAAS,CAAC,UAAU,KAAK,kBAAkB,CAAC,WAAW,EACtD,CAAC;oBACF,MAAM,SAAS,GAAG,KAAK,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC;oBACvD,IAAI,SAAS,CAAC,UAAU,KAAK,kBAAkB,CAAC,QAAQ,EAAE,CAAC;wBAC1D,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,QAAQ,UAAU,SAAS,EAAE,CAAC,CAAC;oBACzD,CAAC;yBAAM,IAAI,SAAS,CAAC,UAAU,KAAK,kBAAkB,CAAC,WAAW,EAAE,CAAC;wBACpE,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,QAAQ,cAAc,SAAS,EAAE,CAAC,CAAC;oBAC7D,CAAC;gBACF,CAAC;qBAAM,IAAI,SAAS,CAAC,UAAU,KAAK,kBAAkB,CAAC,EAAE,EAAE,CAAC;oBAC3D,IAAI,KAAK,GAAc,EAAE,CAAC;oBAC1B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;wBACrC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;oBACjE,CAAC;yBAAM,CAAC;wBACP,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;oBACzE,CAAC;oBACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBACnB,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,QAAQ,QAAQ,CAAC,CAAC;gBAC5C,CAAC;qBAAM,CAAC;oBACP,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC;oBAClC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBACvB,IAAI,SAAS,CAAC,UAAU,KAAK,kBAAkB,CAAC,MAAM,EAAE,CAAC;wBACxD,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,QAAQ,OAAO,CAAC,CAAC;oBAC3C,CAAC;yBAAM,IAAI,SAAS,CAAC,UAAU,KAAK,kBAAkB,CAAC,SAAS,EAAE,CAAC;wBAClE,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,QAAQ,QAAQ,CAAC,CAAC;oBAC5C,CAAC;yBAAM,IAAI,SAAS,CAAC,UAAU,KAAK,kBAAkB,CAAC,WAAW,EAAE,CAAC;wBACpE,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,QAAQ,OAAO,CAAC,CAAC;oBAC3C,CAAC;yBAAM,IAAI,SAAS,CAAC,UAAU,KAAK,kBAAkB,CAAC,QAAQ,EAAE,CAAC;wBACjE,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,QAAQ,OAAO,CAAC,CAAC;oBAC3C,CAAC;yBAAM,IAAI,SAAS,CAAC,UAAU,KAAK,kBAAkB,CAAC,kBAAkB,EAAE,CAAC;wBAC3E,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,QAAQ,QAAQ,CAAC,CAAC;oBAC5C,CAAC;yBAAM,IAAI,SAAS,CAAC,UAAU,KAAK,kBAAkB,CAAC,eAAe,EAAE,CAAC;wBACxE,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,QAAQ,QAAQ,CAAC,CAAC;oBAC5C,CAAC;gBACF,CAAC;gBAED,MAAM,QAAQ,GAAI,UAA+B,CAAC,eAAe,IAAI,eAAe,CAAC,GAAG,CAAC;gBACzF,cAAc,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACnD,CAAC;YAED,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC/B,mBAAmB,IAAI,SAAS,cAAc,GAAG,CAAC;YACnD,CAAC;YAED,GAAG,IAAI,UAAU,mBAAmB,EAAE,CAAC;YAEvC,UAAU,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;YAEzC,0GAA0G;YAC1G,IAAI,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBAC5D,MAAM,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;gBACxE,MAAM,OAAO,GACZ,cAAc,CAAC,CAAC,CAAC,CAAC,aAAa;oBAC/B,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,EAAE,aAAa,CAAC;gBAEjF,IAAI,UAAU,GAAG,KAAK,CAAC;gBACvB,IAAI,OAAO,KAAK,aAAa,CAAC,UAAU,EAAE,CAAC;oBAC1C,UAAU,GAAG,MAAM,CAAC;gBACrB,CAAC;gBAED,GAAG,IAAI,cAAc,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC;gBACpE,0CAA0C;gBAC1C,UAAU,GAAG,CAAC,CAAC;YAChB,CAAC;YAED,GAAG,IAAI,kBAAkB,CAAC;YAE1B,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;gBACxB,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,yBAAyB,CAAC,UAAU;gBAC5C,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;gBACd,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,EAAE,GAAG,EAAE;aACb,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;YAE/E,MAAM,QAAQ,GAAiB,EAAE,CAAC;YAElC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC/B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC;YAC5E,CAAC;YAED,OAAO;gBACN,QAAQ;gBACR,MAAM,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;aACvE,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,YAAY,CACrB,yBAAyB,CAAC,UAAU,EACpC,YAAY,EACZ,EAAE,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,EAC9B,KAAK,CACL,CAAC;QACH,CAAC;gBAAS,CAAC;YACV,MAAM,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QACxC,CAAC;IACF,CAAC;IAED;;;;;;OAMG;IACO,KAAK,CAAC,cAAc,CAAC,eAAwB,KAAK;QAC3D,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;YACzB,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK;YACjC,eAAe,EAAE,IAAI,CAAC,OAAO,CAAC,eAAe;YAC7C,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ;YAC1D,eAAe,EAAE;gBAChB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;aACvB;SACD,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QAEvB,OAAO,MAAM,CAAC;IACf,CAAC;IAED;;;;OAIG;IACO,KAAK,CAAC,eAAe,CAAC,UAAmB;QAClD,IAAI,CAAC,UAAU,EAAE,CAAC;YACjB,OAAO;QACR,CAAC;QACD,OAAO,UAAU,CAAC,QAAQ,EAAE,CAAC;IAC9B,CAAC;IAED;;;;;;;;OAQG;IACO,KAAK,CAAC,OAAO,CACtB,UAAkB,EAClB,GAAW,EACX,MAAiB,EACjB,SAAkB,EAClB,KAAc;QAEd,OAAO,IAAI,OAAO,CAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAChE,MAAM,IAAI,GAAyB,EAAE,CAAC;YAEtC,UAAU,CAAC,OAAO,CACjB,GAAG,EACH,MAAM,EACN;gBACC,OAAO,EAAE,IAAI;gBACb,QAAQ,EAAE,KAAK;gBACf,SAAS,EAAE,KAAK,IAAI,yBAAyB,CAAC,cAAc;gBAC5D,SAAS;aACT,EACD,CAAC,CAAS,EAAE,GAAuB,EAAE,EAAE;gBACtC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChB,CAAC,EACD,CAAC,GAAU,EAAE,GAA6B,EAAE,EAAE;gBAC7C,IAAI,GAAG,EAAE,CAAC;oBACT,MAAM,CAAC,GAAG,CAAC,CAAC;oBACZ,OAAO;gBACR,CAAC;gBACD,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;gBAChB,OAAO,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CACD,CAAC;QACH,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACO,KAAK,CAAC,OAAO,CACtB,UAAkB,EAClB,GAAW,EACX,MAAkB;QAElB,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED;;;;;OAKG;IACO,KAAK,CAAC,cAAc,CAC7B,UAAkB,EAClB,YAAoB;QAEpB,OAAO,IAAI,CAAC,OAAO,CAClB,UAAU,EACV,kCAAkC,YAAY,8EAA8E,CAC5H,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACO,KAAK,CAAC,mBAAmB,CAAC,UAAkB,EAAE,YAAoB;QAC3E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAChC,UAAU,EACV,2EAA2E,EAC3E,CAAC,YAAY,CAAC,CACd,CAAC;QAEF,OAAO,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED;;;;;;OAMG;IACO,KAAK,CAAC,eAAe,CAAC,UAAkB,EAAE,QAAgB;QACnE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAChC,UAAU,EACV,+DAA+D,EAC/D,CAAC,QAAQ,CAAC,CACV,CAAC;QAEF,OAAO,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED;;;;;;;OAOG;IACO,KAAK,CAAC,gBAAgB,CAC/B,UAAkB,EAClB,YAAoB,EACpB,SAAiB;QAEjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAChC,UAAU,EACV,wFAAwF,EACxF,CAAC,YAAY,EAAE,SAAS,CAAC,CACzB,CAAC;QAEF,OAAO,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED;;;;;;OAMG;IACO,iBAAiB,CAAC,KAAc,EAAE,eAAyC;QACpF,IACC,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,WAAW,CAAC;YAC3C,CAAC,eAAe,CAAC,IAAI,KAAK,QAAQ,IAAI,eAAe,CAAC,IAAI,KAAK,OAAO,CAAC,EACtE,CAAC;YACF,MAAM,SAAS,GAAG,mBAAmB,CAAC,GAAG,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;YACvE,OAAO,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,UAAU,EAAE,KAAkC,CAAC,CAAC;QAC1F,CAAC;aAAM;QACN,8BAA8B;QAC9B,CAAC,eAAe,CAAC,IAAI,KAAK,QAAQ,IAAI,eAAe,CAAC,MAAM,KAAK,MAAM,CAAC;YACxE,gDAAgD;YAChD,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,WAAW,CAAC;gBAC5C,CAAC,eAAe,CAAC,IAAI,KAAK,QAAQ,IAAI,eAAe,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,EACxE,CAAC;YACF,IAAI,CAAC;gBACJ,OAAO,IAAI,CAAC,KAAK,CAAC,KAAe,CAAC,CAAC;YACpC,CAAC;YAAC,MAAM,CAAC;gBACR,MAAM,IAAI,YAAY,CAAC,yBAAyB,CAAC,UAAU,EAAE,iBAAiB,EAAE;oBAC/E,IAAI,EAAE,eAAe,CAAC,QAAQ;oBAC9B,KAAK;iBACL,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;aAAM,IACN,eAAe,CAAC,IAAI,KAAK,QAAQ;YACjC,CAAC,eAAe,CAAC,MAAM,KAAK,WAAW,IAAI,eAAe,CAAC,MAAM,KAAK,MAAM,CAAC;YAC7E,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EACb,CAAC;YACF,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;aAAM,IAAI,eAAe,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9C,IACC,KAAK,KAAK,MAAM;gBAChB,KAAK,KAAK,WAAW;gBACrB,KAAK,KAAK,EAAE;gBACZ,KAAK,KAAK,IAAI;gBACd,KAAK,KAAK,SAAS,EAClB,CAAC;gBACF,OAAO,IAAI,CAAC;YACb,CAAC;QACF,CAAC;aAAM,IAAI,eAAe,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC9C,OAAQ,KAA6B,CAAC,QAAQ,EAAE,CAAC;QAClD,CAAC;QAED,OAAO,KAAK,CAAC;IACd,CAAC;IAED;;;;;;OAMG;IACO,iBAAiB,CAAC,KAAc,EAAE,eAA0C;QACrF,IAAI,eAAe,EAAE,CAAC;YACrB,8BAA8B;YAC9B,IACC,CAAC,eAAe,CAAC,IAAI,KAAK,QAAQ,IAAI,eAAe,CAAC,MAAM,KAAK,MAAM,CAAC;gBACxE,gDAAgD;gBAChD,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,WAAW,CAAC;oBAC5C,CAAC,eAAe,CAAC,IAAI,KAAK,QAAQ,IAAI,eAAe,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,EACxE,CAAC;gBACF,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACxD,CAAC;iBAAM,IAAI,eAAe,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC9C,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;oBACvB,OAAO;gBACR,CAAC;gBACD,OAAO,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAC9C,CAAC;YACD,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;IAED;;;;;;OAMG;IACO,kBAAkB,CAC3B,UAAkD,EAClD,GAA8B;QAE9B,MAAM,GAAG,GAA8B,EAAE,CAAC;QAE1C,KAAK,MAAM,KAAK,IAAI,UAAU,IAAI,EAAE,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,QAAkB,CAAC,CAAC;YAC5C,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtB,GAAG,CAAC,KAAK,CAAC,QAAkB,CAAC,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YACtE,CAAC;QACF,CAAC;QAED,OAAO,YAAY,CAAC,qBAAqB,CAAC,GAAQ,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED;;;;;OAKG;IACO,UAAU,CAAC,KAAa;QACjC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAC3C,OAAO,IAAI,CAAC;QACb,CAAC;QAED,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;IACzC,CAAC;IAED;;;;;OAKG;IACO,QAAQ,CAAC,KAAc;QAChC,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAEjC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAAC,EAAE;YAChD,QAAQ,CAAC,EAAE,CAAC;gBACX,KAAK,IAAI;oBACR,OAAO,MAAM,CAAC,GAAG,CAAA,IAAI,CAAC;gBACvB,KAAK,IAAI;oBACR,OAAO,MAAM,CAAC,GAAG,CAAA,IAAI,CAAC;gBACvB,KAAK,IAAI;oBACR,OAAO,MAAM,CAAC,GAAG,CAAA,IAAI,CAAC;gBACvB,KAAK,IAAI;oBACR,OAAO,MAAM,CAAC,GAAG,CAAA,IAAI,CAAC;gBACvB,KAAK,IAAI;oBACR,OAAO,MAAM,CAAC,GAAG,CAAA,IAAI,CAAC;gBACvB,KAAK,QAAQ;oBACZ,OAAO,MAAM,CAAC,GAAG,CAAA,IAAI,CAAC;gBACvB;oBACC,OAAO,KAAK,CAAC,EAAE,CAAC;YAClB,CAAC;QACF,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;OAKG;IACO,eAAe,CAAC,UAA+D;QAIxF,MAAM,eAAe,GAAc,EAAE,CAAC;QACtC,MAAM,aAAa,GAAa,EAAE,CAAC;QAEnC,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;YAC/D;gBACC,QAAQ,EAAE,yBAAyB,CAAC,aAAa;gBACjD,IAAI,EAAE,QAAQ;aACc;SAC7B,CAAC,CAAC;QAEH,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;gBACpC,aAAa,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,QAAkB,KAAK,CAAC,CAAC;gBAC1D,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,QAAQ,CAAC,CAAC;gBAC/E,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC;YAC/E,CAAC;QACF,CAAC;QACD,OAAO,EAAE,YAAY,EAAE,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IACvE,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ContextIdHelper, ContextIdStore } from \"@twin.org/context\";\nimport {\n\tCoerce,\n\tComponentFactory,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tObjectHelper,\n\tStringHelper\n} from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\tEntitySchemaFactory,\n\tEntitySchemaHelper,\n\tLogicalOperator,\n\tSortDirection,\n\ttype EntityCondition,\n\ttype IComparator,\n\ttype IComparatorGroup,\n\ttype IEntitySchema,\n\ttype IEntitySchemaProperty\n} from \"@twin.org/entity\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { types as CassandraTypes, Client } from \"cassandra-driver\";\nimport type { IScyllaDBConfig } from \"./models/IScyllaDBConfig.js\";\nimport type { IScyllaDBTableConfig } from \"./models/IScyllaDBTableConfig.js\";\n\n/**\n * Store entities using ScyllaDB.\n */\nexport abstract class AbstractScyllaDBConnector<T> {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<AbstractScyllaDBConnector<unknown>>();\n\n\t/**\n\t * Partition id field name.\n\t * @internal\n\t */\n\tprotected static readonly PARTITION_KEY: string = \"partitionId\";\n\n\t/**\n\t * Partition id field value.\n\t * @internal\n\t */\n\tprotected static readonly PARTITION_KEY_VALUE: string = \"root\";\n\n\t/**\n\t * Limit the number of entities when finding.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LIMIT: number = 40;\n\n\t/**\n\t * The name of the database table.\n\t * @internal\n\t */\n\tprotected _fullTableName: string;\n\n\t/**\n\t * Configuration to connection to ScyllaDB.\n\t * @internal\n\t */\n\tprotected readonly _config: IScyllaDBConfig;\n\n\t/**\n\t * The logging component.\n\t * @internal\n\t */\n\tprotected readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The schema for the entity.\n\t * @internal\n\t */\n\tprotected readonly _entitySchema: IEntitySchema<T>;\n\n\t/**\n\t * The keys to use from the context ids to create partitions.\n\t * @internal\n\t */\n\tprotected readonly _partitionContextIds?: string[];\n\n\t/**\n\t * The primary key.\n\t * @internal\n\t */\n\tprotected readonly _primaryKey: IEntitySchemaProperty<T>;\n\n\t/**\n\t * Create a new instance of AbstractScyllaDBConnector.\n\t * @param options The options for the connector.\n\t * @param options.loggingComponentType The type of logging component to use, defaults to no logging.\n\t * @param options.entitySchema The name of the entity schema.\n\t * @param options.partitionContextIds The keys to use from the context ids to create partitions.\n\t * @param options.config The configuration for the connector.\n\t */\n\tconstructor(options: {\n\t\tloggingComponentType?: string;\n\t\tentitySchema: string;\n\t\tpartitionContextIds?: string[];\n\t\tconfig: IScyllaDBTableConfig;\n\t}) {\n\t\tGuards.object(AbstractScyllaDBConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.stringValue(\n\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\tnameof(options.entitySchema),\n\t\t\toptions.entitySchema\n\t\t);\n\t\tGuards.object<IScyllaDBConfig>(\n\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\tnameof(options.config),\n\t\t\toptions.config\n\t\t);\n\t\tGuards.arrayValue(\n\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\tnameof(options.config.hosts),\n\t\t\toptions.config.hosts\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\tnameof(options.config.localDataCenter),\n\t\t\toptions.config.localDataCenter\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\tnameof(options.config.keyspace),\n\t\t\toptions.config.keyspace\n\t\t);\n\n\t\tthis._logging = ComponentFactory.getIfExists(options.loggingComponentType ?? \"logging\");\n\n\t\tthis._entitySchema = EntitySchemaFactory.get(options.entitySchema);\n\t\tthis._partitionContextIds = options.partitionContextIds;\n\t\tthis._primaryKey = EntitySchemaHelper.getPrimaryKey<T>(this._entitySchema);\n\n\t\tthis._config = options.config;\n\t\tthis._fullTableName = StringHelper.camelCase(\n\t\t\tIs.stringValue(options.config.tableName) ? options.config.tableName : options.entitySchema\n\t\t);\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn AbstractScyllaDBConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Get the schema for the entities.\n\t * @returns The schema for the entities.\n\t */\n\tpublic getSchema(): IEntitySchema {\n\t\treturn this._entitySchema as IEntitySchema;\n\t}\n\n\t/**\n\t * Get an entity.\n\t * @param id The id of the entity to get.\n\t * @param secondaryIndex Get the item using a secondary index.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The object if it can be found or undefined.\n\t */\n\tpublic async get(\n\t\tid: string,\n\t\tsecondaryIndex?: keyof T,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<T | undefined> {\n\t\tGuards.stringValue(AbstractScyllaDBConnector.CLASS_NAME, nameof(id), id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tlet connection;\n\t\ttry {\n\t\t\tconst indexField = secondaryIndex ?? this._primaryKey?.property;\n\n\t\t\tconditions ??= [];\n\t\t\tconditions.unshift({\n\t\t\t\tproperty: AbstractScyllaDBConnector.PARTITION_KEY as keyof T,\n\t\t\t\tvalue: partitionKey ?? AbstractScyllaDBConnector.PARTITION_KEY_VALUE\n\t\t\t});\n\t\t\tconditions.unshift({\n\t\t\t\tproperty: indexField,\n\t\t\t\tvalue: id\n\t\t\t});\n\n\t\t\tconst { sqlCondition, conditionValues } = this.buildConditions(conditions);\n\n\t\t\tlet sql = `SELECT * FROM \"${this._fullTableName}\" WHERE ${sqlCondition}`;\n\n\t\t\tif (secondaryIndex) {\n\t\t\t\tsql += \" ALLOW FILTERING\";\n\t\t\t}\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: AbstractScyllaDBConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"sql\",\n\t\t\t\tdata: { sql }\n\t\t\t});\n\n\t\t\tconnection = await this.openConnection();\n\n\t\t\tconst result = await this.queryDB(connection, sql, conditionValues);\n\n\t\t\tif (result.rows.length === 1) {\n\t\t\t\treturn this.convertRowToObject(this._entitySchema.properties, result.rows[0]);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\t\t\"getFailed\",\n\t\t\t\t{\n\t\t\t\t\tid\n\t\t\t\t},\n\t\t\t\terror\n\t\t\t);\n\t\t} finally {\n\t\t\tawait this.closeConnection(connection);\n\t\t}\n\t}\n\n\t/**\n\t * Find all the entities which match the conditions.\n\t * @param conditions The conditions to match for the entities.\n\t * @param sortProperties The optional sort order.\n\t * @param properties The optional properties to return, defaults to all.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tpublic async query(\n\t\tconditions?: EntityCondition<T>,\n\t\tsortProperties?: {\n\t\t\tproperty: keyof T;\n\t\t\tsortDirection: SortDirection;\n\t\t}[],\n\t\tproperties?: (keyof T)[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The entities, which can be partial if a limited keys list was provided.\n\t\t */\n\t\tentities: Partial<T>[];\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more entities.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\tlet connection;\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tlet returnSize = limit ?? AbstractScyllaDBConnector._DEFAULT_LIMIT;\n\t\t\tlet sql = `SELECT * FROM \"${this._fullTableName}\"`;\n\n\t\t\tif (Is.array(properties)) {\n\t\t\t\tconst fields: string[] = [];\n\n\t\t\t\tfor (const property of properties) {\n\t\t\t\t\tfields.push(property.toString());\n\t\t\t\t}\n\n\t\t\t\tconst selectFields = fields.join(\",\");\n\t\t\t\tsql = sql.replace(\"*\", selectFields);\n\t\t\t}\n\n\t\t\tconst conds: string[] = [];\n\t\t\tlet conditionQuery = \"\";\n\t\t\t// The params to be used to execute the query\n\t\t\tconst params: unknown[] = [];\n\n\t\t\tlet finalConditionQuery = `\"${AbstractScyllaDBConnector.PARTITION_KEY}\" = ?`;\n\t\t\tparams.push(partitionKey ?? AbstractScyllaDBConnector.PARTITION_KEY_VALUE);\n\n\t\t\tlet theConditions: EntityCondition<T>[] = [];\n\t\t\tif (!Is.undefined(conditions)) {\n\t\t\t\tif (\"conditions\" in conditions) {\n\t\t\t\t\ttheConditions = (conditions as IComparatorGroup).conditions;\n\t\t\t\t} else {\n\t\t\t\t\ttheConditions.push(conditions as EntityCondition<T>);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// TODO: This code needs refactoring to support conditions for sub properties.\n\t\t\tfor (const cond of theConditions) {\n\t\t\t\tconst condition = cond as IComparator;\n\n\t\t\t\tconst descriptor = this._entitySchema.properties?.find(\n\t\t\t\t\tp => p.property === condition.property\n\t\t\t\t);\n\t\t\t\tif (\n\t\t\t\t\tcondition.comparison === ComparisonOperator.Includes ||\n\t\t\t\t\tcondition.comparison === ComparisonOperator.NotIncludes\n\t\t\t\t) {\n\t\t\t\t\tconst propValue = `'%${condition.value?.toString()}%'`;\n\t\t\t\t\tif (condition.comparison === ComparisonOperator.Includes) {\n\t\t\t\t\t\tconds.push(`\"${condition.property}\" LIKE ${propValue}`);\n\t\t\t\t\t} else if (condition.comparison === ComparisonOperator.NotIncludes) {\n\t\t\t\t\t\tconds.push(`\"${condition.property}\" NOT LIKE ${propValue}`);\n\t\t\t\t\t}\n\t\t\t\t} else if (condition.comparison === ComparisonOperator.In) {\n\t\t\t\t\tlet value: unknown[] = [];\n\t\t\t\t\tif (!Is.arrayValue(condition.value)) {\n\t\t\t\t\t\tvalue.push(this.propertyToDbValue(condition.value, descriptor));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvalue = condition.value.map(v => this.propertyToDbValue(v, descriptor));\n\t\t\t\t\t}\n\t\t\t\t\tparams.push(value);\n\t\t\t\t\tconds.push(`\"${condition.property}\" IN ?`);\n\t\t\t\t} else {\n\t\t\t\t\tconst propValue = condition.value;\n\t\t\t\t\tparams.push(propValue);\n\t\t\t\t\tif (condition.comparison === ComparisonOperator.Equals) {\n\t\t\t\t\t\tconds.push(`\"${condition.property}\" = ?`);\n\t\t\t\t\t} else if (condition.comparison === ComparisonOperator.NotEquals) {\n\t\t\t\t\t\tconds.push(`\"${condition.property}\" <> ?`);\n\t\t\t\t\t} else if (condition.comparison === ComparisonOperator.GreaterThan) {\n\t\t\t\t\t\tconds.push(`\"${condition.property}\" > ?`);\n\t\t\t\t\t} else if (condition.comparison === ComparisonOperator.LessThan) {\n\t\t\t\t\t\tconds.push(`\"${condition.property}\" < ?`);\n\t\t\t\t\t} else if (condition.comparison === ComparisonOperator.GreaterThanOrEqual) {\n\t\t\t\t\t\tconds.push(`\"${condition.property}\" >= ?`);\n\t\t\t\t\t} else if (condition.comparison === ComparisonOperator.LessThanOrEqual) {\n\t\t\t\t\t\tconds.push(`\"${condition.property}\" <= ?`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst operator = (conditions as IComparatorGroup).logicalOperator ?? LogicalOperator.And;\n\t\t\t\tconditionQuery = `${conds.join(` ${operator} `)}`;\n\t\t\t}\n\n\t\t\tif (conditionQuery.length > 0) {\n\t\t\t\tfinalConditionQuery += ` AND (${conditionQuery})`;\n\t\t\t}\n\n\t\t\tsql += ` WHERE ${finalConditionQuery}`;\n\n\t\t\tconnection = await this.openConnection();\n\n\t\t\t// TODO: Only supported one sort property at the moment. This code would need to be revised in a follow-up\n\t\t\tif (Is.array(sortProperties) && sortProperties.length >= 1) {\n\t\t\t\tconst sortKey = sortProperties[0].property ?? this._primaryKey.property;\n\t\t\t\tconst sortDir =\n\t\t\t\t\tsortProperties[0].sortDirection ??\n\t\t\t\t\tthis._entitySchema.properties?.find(e => e.property === sortKey)?.sortDirection;\n\n\t\t\t\tlet sqlSortDir = \"asc\";\n\t\t\t\tif (sortDir === SortDirection.Descending) {\n\t\t\t\t\tsqlSortDir = \"desc\";\n\t\t\t\t}\n\n\t\t\t\tsql += ` ORDER BY \"${String(sortKey)}\" ${sqlSortDir.toUpperCase()}`;\n\t\t\t\t// Disabling paging in order by situations\n\t\t\t\treturnSize = 0;\n\t\t\t}\n\n\t\t\tsql += \" ALLOW FILTERING\";\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: AbstractScyllaDBConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"sql\",\n\t\t\t\tdata: { sql }\n\t\t\t});\n\n\t\t\tconst result = await this.queryDB(connection, sql, params, cursor, returnSize);\n\n\t\t\tconst entities: Partial<T>[] = [];\n\n\t\t\tfor (const row of result.rows) {\n\t\t\t\tentities.push(this.convertRowToObject(this._entitySchema.properties, row));\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tentities,\n\t\t\t\tcursor: Is.stringValue(result.pageState) ? result.pageState : undefined\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\t\t\"findFailed\",\n\t\t\t\t{ table: this._fullTableName },\n\t\t\t\terror\n\t\t\t);\n\t\t} finally {\n\t\t\tawait this.closeConnection(connection);\n\t\t}\n\t}\n\n\t/**\n\t * Open a new database connection.\n\t * @param config The config for the connection.\n\t * @param skipKeySpace Don't include the keyspace in the connection.\n\t * @returns The new connection.\n\t * @internal\n\t */\n\tprotected async openConnection(skipKeySpace: boolean = false): Promise<Client> {\n\t\tconst client = new Client({\n\t\t\tcontactPoints: this._config.hosts,\n\t\t\tlocalDataCenter: this._config.localDataCenter,\n\t\t\tkeyspace: skipKeySpace ? undefined : this._config.keyspace,\n\t\t\tprotocolOptions: {\n\t\t\t\tport: this._config.port\n\t\t\t}\n\t\t});\n\t\tawait client.connect();\n\n\t\treturn client;\n\t}\n\n\t/**\n\t * Close database connection.\n\t * @param connection The connection to close.\n\t * @internal\n\t */\n\tprotected async closeConnection(connection?: Client): Promise<void> {\n\t\tif (!connection) {\n\t\t\treturn;\n\t\t}\n\t\treturn connection.shutdown();\n\t}\n\n\t/**\n\t * Query the database.\n\t * @param connection The connection to query.\n\t * @param sql The sql statement to execute.\n\t * @param params The params to use when executing the query.\n\t * @param state The state to use when it comes to pagination.\n\t * @returns The rows.\n\t * @internal\n\t */\n\tprotected async queryDB(\n\t\tconnection: Client,\n\t\tsql: string,\n\t\tparams: unknown[],\n\t\tpageState?: string,\n\t\tlimit?: number\n\t): Promise<CassandraTypes.ResultSet> {\n\t\treturn new Promise<CassandraTypes.ResultSet>((resolve, reject) => {\n\t\t\tconst rows: CassandraTypes.Row[] = [];\n\n\t\t\tconnection.eachRow(\n\t\t\t\tsql,\n\t\t\t\tparams,\n\t\t\t\t{\n\t\t\t\t\tprepare: true,\n\t\t\t\t\tautoPage: false,\n\t\t\t\t\tfetchSize: limit ?? AbstractScyllaDBConnector._DEFAULT_LIMIT,\n\t\t\t\t\tpageState\n\t\t\t\t},\n\t\t\t\t(n: number, row: CassandraTypes.Row) => {\n\t\t\t\t\trows.push(row);\n\t\t\t\t},\n\t\t\t\t(err: Error, res: CassandraTypes.ResultSet) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tres.rows = rows;\n\t\t\t\t\tresolve(res);\n\t\t\t\t}\n\t\t\t);\n\t\t});\n\t}\n\n\t/**\n\t * Execute on the database.\n\t * @param connection The connection to execute.\n\t * @param sql The sql statement to execute.\n\t * @internal\n\t */\n\tprotected async execute(\n\t\tconnection: Client,\n\t\tsql: string,\n\t\tparams?: unknown[]\n\t): Promise<CassandraTypes.ResultSet> {\n\t\treturn connection.execute(sql, params, { prepare: true });\n\t}\n\n\t/**\n\t * Create keyspace if it doesn't exist.\n\t * @param connection The connection to perform the query with.\n\t * @param keyspaceName The name of the keyspace to create.\n\t * @internal\n\t */\n\tprotected async createKeyspace(\n\t\tconnection: Client,\n\t\tkeyspaceName: string\n\t): Promise<CassandraTypes.ResultSet> {\n\t\treturn this.execute(\n\t\t\tconnection,\n\t\t\t`CREATE KEYSPACE IF NOT EXISTS \"${keyspaceName}\" WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1}`\n\t\t);\n\t}\n\n\t/**\n\t * Check if a keyspace exists.\n\t * @param connection The connection to perform the query with.\n\t * @param keyspaceName The name of the keyspace to check.\n\t * @returns True if the keyspace exists, false otherwise.\n\t * @internal\n\t */\n\tprotected async checkKeyspaceExists(connection: Client, keyspaceName: string): Promise<boolean> {\n\t\tconst result = await this.queryDB(\n\t\t\tconnection,\n\t\t\t\"SELECT keyspace_name FROM system_schema.keyspaces WHERE keyspace_name = ?\",\n\t\t\t[keyspaceName]\n\t\t);\n\n\t\treturn result.rowLength > 0;\n\t}\n\n\t/**\n\t * Check if a type exists.\n\t * @param connection The connection to perform the query with.\n\t * @param typeName The name of the type to check.\n\t * @returns True if the type exists, false otherwise.\n\t * @internal\n\t */\n\tprotected async checkTypeExists(connection: Client, typeName: string): Promise<boolean> {\n\t\tconst result = await this.queryDB(\n\t\t\tconnection,\n\t\t\t\"SELECT type_name FROM system_schema.types WHERE type_name = ?\",\n\t\t\t[typeName]\n\t\t);\n\n\t\treturn result.rowLength > 0;\n\t}\n\n\t/**\n\t * Check if a table exists.\n\t * @param connection The connection to perform the query with.\n\t * @param keyspaceName The name of the keyspace to check.\n\t * @param tableName The name of the table to check.\n\t * @returns True if the table exists, false otherwise.\n\t * @internal\n\t */\n\tprotected async checkTableExists(\n\t\tconnection: Client,\n\t\tkeyspaceName: string,\n\t\ttableName: string\n\t): Promise<boolean> {\n\t\tconst result = await this.queryDB(\n\t\t\tconnection,\n\t\t\t\"SELECT table_name FROM system_schema.tables WHERE keyspace_name = ? AND table_name = ?\",\n\t\t\t[keyspaceName, tableName]\n\t\t);\n\n\t\treturn result.rowLength > 0;\n\t}\n\n\t/**\n\t * Format a field from the DB.\n\t * @param value The value to convert to original form.\n\t * @param fieldDescriptor The descriptor for the field.\n\t * @returns The value as a property for the object.\n\t * @internal\n\t */\n\tprotected dbValueToProperty(value: unknown, fieldDescriptor: IEntitySchemaProperty<T>): unknown {\n\t\tif (\n\t\t\tIs.stringValue(fieldDescriptor.itemTypeRef) &&\n\t\t\t(fieldDescriptor.type === \"object\" || fieldDescriptor.type === \"array\")\n\t\t) {\n\t\t\tconst objSchema = EntitySchemaFactory.get(fieldDescriptor.itemTypeRef);\n\t\t\treturn this.convertRowToObject(objSchema.properties, value as { [id: string]: unknown });\n\t\t} else if (\n\t\t\t// If the field is json format\n\t\t\t(fieldDescriptor.type === \"string\" && fieldDescriptor.format === \"json\") ||\n\t\t\t// Or its and object or array without a type ref\n\t\t\t(!Is.stringValue(fieldDescriptor.itemTypeRef) &&\n\t\t\t\t(fieldDescriptor.type === \"object\" || fieldDescriptor.type === \"array\"))\n\t\t) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(value as string);\n\t\t\t} catch {\n\t\t\t\tthrow new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, \"parseJSONFailed\", {\n\t\t\t\t\tname: fieldDescriptor.property,\n\t\t\t\t\tvalue\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (\n\t\t\tfieldDescriptor.type === \"string\" &&\n\t\t\t(fieldDescriptor.format === \"date-time\" || fieldDescriptor.format === \"date\") &&\n\t\t\tIs.date(value)\n\t\t) {\n\t\t\treturn Coerce.string(value);\n\t\t} else if (fieldDescriptor.type === \"object\") {\n\t\t\tif (\n\t\t\t\tvalue === \"null\" ||\n\t\t\t\tvalue === \"undefined\" ||\n\t\t\t\tvalue === \"\" ||\n\t\t\t\tvalue === null ||\n\t\t\t\tvalue === undefined\n\t\t\t) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} else if (fieldDescriptor.format === \"uuid\") {\n\t\t\treturn (value as CassandraTypes.Uuid).toString();\n\t\t}\n\n\t\treturn value;\n\t}\n\n\t/**\n\t * Format a value for the DB. As the driver takes care of conversion from Javascript\n\t * @param value The value to format.\n\t * @param fieldDescriptor The descriptor for the field\n\t * @returns The value after conversion.\n\t * @internal\n\t */\n\tprotected propertyToDbValue(value: unknown, fieldDescriptor?: IEntitySchemaProperty<T>): unknown {\n\t\tif (fieldDescriptor) {\n\t\t\t// If the field is json format\n\t\t\tif (\n\t\t\t\t(fieldDescriptor.type === \"string\" && fieldDescriptor.format === \"json\") ||\n\t\t\t\t// Or its and object or array without a type ref\n\t\t\t\t(!Is.stringValue(fieldDescriptor.itemTypeRef) &&\n\t\t\t\t\t(fieldDescriptor.type === \"object\" || fieldDescriptor.type === \"array\"))\n\t\t\t) {\n\t\t\t\treturn Is.empty(value) ? \"null\" : this.jsonWrap(value);\n\t\t\t} else if (fieldDescriptor.format === \"uuid\") {\n\t\t\t\tif (!Is.string(value)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\treturn CassandraTypes.Uuid.fromString(value);\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\t}\n\n\t/**\n\t * Convert a row back to an object.\n\t * @param properties The optional properties to convert.\n\t * @param row The row to convert.\n\t * @returns The row as an object.\n\t * @internal\n\t */\n\tprotected convertRowToObject(\n\t\tproperties: IEntitySchemaProperty<T>[] | undefined,\n\t\trow: { [id: string]: unknown }\n\t): T {\n\t\tconst obj: { [id: string]: unknown } = {};\n\n\t\tfor (const field of properties ?? []) {\n\t\t\tconst value = row[field.property as string];\n\t\t\tif (!Is.empty(value)) {\n\t\t\t\tobj[field.property as string] = this.dbValueToProperty(value, field);\n\t\t\t}\n\t\t}\n\n\t\treturn ObjectHelper.removeEmptyProperties(obj as T, { removeNull: true });\n\t}\n\n\t/**\n\t * Wrap a string for DB format.\n\t * @param value The value to wrap.\n\t * @returns The wrapped string.\n\t * @internal\n\t */\n\tprotected stringWrap(value: string): string {\n\t\tif (value === undefined || value === null) {\n\t\t\treturn \"''\";\n\t\t}\n\n\t\treturn `'${value.replace(/'/g, \"''\")}'`;\n\t}\n\n\t/**\n\t * Wrap an object for json in DB format.\n\t * @param value The value to wrap.\n\t * @returns The wrapped string.\n\t * @internal\n\t */\n\tprotected jsonWrap(value: unknown): string {\n\t\tlet json = JSON.stringify(value);\n\n\t\tjson = json.replace(/[\\b\\0\\t\\n\\r\\u001A\\\\]/g, s => {\n\t\t\tswitch (s) {\n\t\t\t\tcase \"\\0\":\n\t\t\t\t\treturn String.raw`\\0`;\n\t\t\t\tcase \"\\n\":\n\t\t\t\t\treturn String.raw`\\n`;\n\t\t\t\tcase \"\\r\":\n\t\t\t\t\treturn String.raw`\\r`;\n\t\t\t\tcase \"\\b\":\n\t\t\t\t\treturn String.raw`\\b`;\n\t\t\t\tcase \"\\t\":\n\t\t\t\t\treturn String.raw`\\t`;\n\t\t\t\tcase \"\\u001A\":\n\t\t\t\t\treturn String.raw`\\Z`;\n\t\t\t\tdefault:\n\t\t\t\t\treturn `\\\\${s}`;\n\t\t\t}\n\t\t});\n\t\treturn json;\n\t}\n\n\t/**\n\t * Build the conditions for the query.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The SQL conditions and the values.\n\t * @internal\n\t */\n\tprotected buildConditions(conditions: { property: keyof T; value: unknown }[] | undefined): {\n\t\tsqlCondition: string;\n\t\tconditionValues: unknown[];\n\t} {\n\t\tconst conditionValues: unknown[] = [];\n\t\tconst sqlConditions: string[] = [];\n\n\t\tconst properties = (this._entitySchema.properties ?? []).concat([\n\t\t\t{\n\t\t\t\tproperty: AbstractScyllaDBConnector.PARTITION_KEY,\n\t\t\t\ttype: \"string\"\n\t\t\t} as IEntitySchemaProperty<T>\n\t\t]);\n\n\t\tif (Is.arrayValue(conditions)) {\n\t\t\tfor (const condition of conditions) {\n\t\t\t\tsqlConditions.push(`\"${condition.property as string}\"=?`);\n\t\t\t\tconst schemaProperty = properties.find(s => s.property === condition.property);\n\t\t\t\tconditionValues.push(this.propertyToDbValue(condition.value, schemaProperty));\n\t\t\t}\n\t\t}\n\t\treturn { sqlCondition: sqlConditions.join(\" AND \"), conditionValues };\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"abstractScyllaDBConnector.js","sourceRoot":"","sources":["../../src/abstractScyllaDBConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ContextIdHelper, ContextIdStore } from \"@twin.org/context\";\nimport { Coerce, ComponentFactory, GeneralError, Guards, Is } from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\tEntitySchemaFactory,\n\tEntitySchemaHelper,\n\tLogicalOperator,\n\tSortDirection,\n\ttype EntityCondition,\n\ttype IComparator,\n\ttype IComparatorGroup,\n\ttype IEntitySchema,\n\ttype IEntitySchemaProperty\n} from \"@twin.org/entity\";\nimport { EntityStorageHelper } from \"@twin.org/entity-storage-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { types as CassandraTypes, Client } from \"cassandra-driver\";\nimport type { IScyllaDBConfig } from \"./models/IScyllaDBConfig.js\";\nimport type { IScyllaDBTableConfig } from \"./models/IScyllaDBTableConfig.js\";\n\n/**\n * Store entities using ScyllaDB.\n */\nexport abstract class AbstractScyllaDBConnector<T> {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<AbstractScyllaDBConnector<unknown>>();\n\n\t/**\n\t * Partition id field name.\n\t * @internal\n\t */\n\tprotected static readonly PARTITION_KEY: string = \"partitionId\";\n\n\t/**\n\t * Partition id field value.\n\t * @internal\n\t */\n\tprotected static readonly PARTITION_KEY_VALUE: string = \"root\";\n\n\t/**\n\t * Limit the number of entities when finding.\n\t * @internal\n\t */\n\tprotected static readonly DEFAULT_LIMIT: number = 40;\n\n\t/**\n\t * The name of the database table.\n\t * @internal\n\t */\n\tprotected _fullTableName: string;\n\n\t/**\n\t * Configuration to connection to ScyllaDB.\n\t * @internal\n\t */\n\tprotected readonly _config: IScyllaDBTableConfig;\n\n\t/**\n\t * The logging component.\n\t * @internal\n\t */\n\tprotected readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The schema for the entity.\n\t * @internal\n\t */\n\tprotected _entitySchema: IEntitySchema<T>;\n\n\t/**\n\t * The keys to use from the context ids to create partitions.\n\t * @internal\n\t */\n\tprotected readonly _partitionContextIds?: string[];\n\n\t/**\n\t * The primary key.\n\t * @internal\n\t */\n\tprotected readonly _primaryKey: IEntitySchemaProperty<T>;\n\n\t/**\n\t * Cached persistent client (keyspace-scoped). Reused across all operations on this\n\t * connector instance so the expensive cassandra-driver `connect()` only runs once.\n\t * Closed by `closePersistentClient()` which callers (e.g. `teardown()`) must invoke.\n\t * @internal\n\t */\n\tprivate _persistentClient: Client | undefined;\n\n\t/**\n\t * Create a new instance of AbstractScyllaDBConnector.\n\t * @param options The options for the connector.\n\t * @param options.loggingComponentType The type of logging component to use, defaults to no logging.\n\t * @param options.entitySchema The name of the entity schema.\n\t * @param options.partitionContextIds The keys to use from the context ids to create partitions.\n\t * @param options.config The configuration for the connector.\n\t */\n\tconstructor(options: {\n\t\tloggingComponentType?: string;\n\t\tentitySchema: string;\n\t\tpartitionContextIds?: string[];\n\t\tconfig: IScyllaDBTableConfig;\n\t}) {\n\t\tGuards.object(AbstractScyllaDBConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.stringValue(\n\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\tnameof(options.entitySchema),\n\t\t\toptions.entitySchema\n\t\t);\n\t\tGuards.object<IScyllaDBConfig>(\n\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\tnameof(options.config),\n\t\t\toptions.config\n\t\t);\n\t\tGuards.arrayValue(\n\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\tnameof(options.config.hosts),\n\t\t\toptions.config.hosts\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\tnameof(options.config.localDataCenter),\n\t\t\toptions.config.localDataCenter\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\tnameof(options.config.keyspace),\n\t\t\toptions.config.keyspace\n\t\t);\n\n\t\tthis._logging = ComponentFactory.getIfExists(options.loggingComponentType ?? \"logging\");\n\n\t\tthis._entitySchema = EntitySchemaFactory.get(options.entitySchema);\n\t\tthis._partitionContextIds = options.partitionContextIds;\n\t\tthis._primaryKey = EntitySchemaHelper.getPrimaryKey<T>(this._entitySchema);\n\n\t\tthis._config = options.config;\n\t\tthis._fullTableName = options.config.tableName;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn AbstractScyllaDBConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Get the schema for the entities.\n\t * @returns The schema for the entities.\n\t */\n\tpublic getSchema(): IEntitySchema {\n\t\treturn this._entitySchema as IEntitySchema;\n\t}\n\n\t/**\n\t * Get an entity.\n\t * @param id The id of the entity to get.\n\t * @param secondaryIndex Get the item using a secondary index.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The object if it can be found or undefined.\n\t */\n\tpublic async get(\n\t\tid: string,\n\t\tsecondaryIndex?: keyof T,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<T | undefined> {\n\t\tGuards.stringValue(AbstractScyllaDBConnector.CLASS_NAME, nameof(id), id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tlet connection;\n\t\ttry {\n\t\t\tconst indexField = secondaryIndex ?? this._primaryKey?.property;\n\n\t\t\tconditions ??= [];\n\t\t\tconditions.unshift({\n\t\t\t\tproperty: AbstractScyllaDBConnector.PARTITION_KEY as keyof T,\n\t\t\t\tvalue: partitionKey ?? AbstractScyllaDBConnector.PARTITION_KEY_VALUE\n\t\t\t});\n\t\t\tconditions.unshift({\n\t\t\t\tproperty: indexField,\n\t\t\t\tvalue: id\n\t\t\t});\n\n\t\t\tconst { sqlCondition, conditionValues } = this.buildConditions(conditions);\n\n\t\t\tlet sql = `SELECT * FROM \"${this.safeTableName(this._fullTableName)}\" WHERE ${sqlCondition}`;\n\n\t\t\tif (secondaryIndex) {\n\t\t\t\tsql += \" ALLOW FILTERING\";\n\t\t\t}\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: AbstractScyllaDBConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"sql\",\n\t\t\t\tdata: { sql }\n\t\t\t});\n\n\t\t\tconnection = await this.openConnection();\n\n\t\t\tconst result = await this.queryDB(connection, sql, conditionValues);\n\n\t\t\tif (result.rows.length === 1) {\n\t\t\t\treturn this.convertRowToObject(this._entitySchema.properties, result.rows[0]);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\t\t\"getFailed\",\n\t\t\t\t{\n\t\t\t\t\tid\n\t\t\t\t},\n\t\t\t\terror\n\t\t\t);\n\t\t} finally {\n\t\t\tawait this.closeConnection(connection);\n\t\t}\n\t}\n\n\t/**\n\t * Find all the entities which match the conditions.\n\t * @param conditions The conditions to match for the entities.\n\t * @param sortProperties The optional sort order.\n\t * @param properties The optional properties to return, defaults to all.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tpublic async query(\n\t\tconditions?: EntityCondition<T>,\n\t\tsortProperties?: {\n\t\t\tproperty: keyof T;\n\t\t\tsortDirection: SortDirection;\n\t\t}[],\n\t\tproperties?: (keyof T)[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The entities, which can be partial if a limited keys list was provided.\n\t\t */\n\t\tentities: Partial<T>[];\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more entities.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\tlet connection;\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\t// Validates and throws for unsupported conditions before entering the try-catch\n\t\t// so that comparisonNotSupported errors surface directly to the caller.\n\t\tconst { whereClause, params } = this.buildCqlConditions(conditions, partitionKey);\n\n\t\ttry {\n\t\t\tconst returnSize = limit ?? AbstractScyllaDBConnector.DEFAULT_LIMIT;\n\t\t\tlet sql = `SELECT * FROM \"${this.safeTableName(this._fullTableName)}\"`;\n\n\t\t\tif (Is.array(properties)) {\n\t\t\t\tconst fields: string[] = [];\n\t\t\t\tfor (const property of properties) {\n\t\t\t\t\tfields.push(property.toString());\n\t\t\t\t}\n\t\t\t\tsql = sql.replace(\"*\", fields.join(\",\"));\n\t\t\t}\n\n\t\t\tsql += ` WHERE ${whereClause}`;\n\n\t\t\tif (Is.array(sortProperties) && sortProperties.length >= 1) {\n\t\t\t\tconst orderClauses = sortProperties.map(sp => {\n\t\t\t\t\tconst dir = sp.sortDirection === SortDirection.Descending ? \"DESC\" : \"ASC\";\n\t\t\t\t\treturn `\"${String(sp.property)}\" ${dir}`;\n\t\t\t\t});\n\t\t\t\tsql += ` ORDER BY ${orderClauses.join(\", \")}`;\n\t\t\t}\n\n\t\t\tconnection = await this.openConnection();\n\n\t\t\tsql += \" ALLOW FILTERING\";\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: AbstractScyllaDBConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"sql\",\n\t\t\t\tdata: { sql }\n\t\t\t});\n\n\t\t\tconst result = await this.queryDB(connection, sql, params, cursor, returnSize);\n\n\t\t\tconst entities: Partial<T>[] = [];\n\n\t\t\tfor (const row of result.rows) {\n\t\t\t\tentities.push(this.convertRowToObject(this._entitySchema.properties, row));\n\t\t\t}\n\n\t\t\t// ScyllaDB may return a pageState even when the current page is the last one\n\t\t\t// (when rows.length == fetchSize). Peek at the next page to verify there are\n\t\t\t// actually more rows before surfacing the cursor to the caller.\n\t\t\tlet nextCursor: string | undefined;\n\t\t\tif (returnSize > 0 && result.rows.length >= returnSize && Is.stringValue(result.pageState)) {\n\t\t\t\tconst peek = await this.queryDB(connection, sql, params, result.pageState, 1);\n\t\t\t\tif (peek.rows.length > 0) {\n\t\t\t\t\tnextCursor = result.pageState;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tentities,\n\t\t\t\tcursor: nextCursor\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\t\t\"findFailed\",\n\t\t\t\t{ table: this.safeTableName(this._fullTableName) },\n\t\t\t\terror\n\t\t\t);\n\t\t} finally {\n\t\t\tawait this.closeConnection(connection);\n\t\t}\n\t}\n\n\t/**\n\t * Count all the entities which match the conditions.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The total count of entities in the storage.\n\t */\n\tpublic async count(conditions?: EntityCondition<T>): Promise<number> {\n\t\tlet connection;\n\t\ttry {\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tconst partitionKey = ContextIdHelper.combinedContextKey(\n\t\t\t\tcontextIds,\n\t\t\t\tthis._partitionContextIds\n\t\t\t);\n\n\t\t\tconst { whereClause, params } = this.buildCqlConditions(conditions, partitionKey);\n\n\t\t\tconst sql = `SELECT COUNT(*) FROM \"${this.safeTableName(this._fullTableName)}\" WHERE ${whereClause} ALLOW FILTERING`;\n\n\t\t\tconnection = await this.openConnection();\n\t\t\tconst result = await this.queryDB(connection, sql, params);\n\t\t\treturn Number(result.rows[0]?.get(\"count\") ?? 0);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, \"countFailed\", undefined, err);\n\t\t} finally {\n\t\t\tawait this.closeConnection(connection);\n\t\t}\n\t}\n\n\t/**\n\t * Open a new database connection.\n\t * @param config The config for the connection.\n\t * @param skipKeySpace Don't include the keyspace in the connection.\n\t * @returns The new connection.\n\t * @internal\n\t */\n\tprotected async openConnection(skipKeySpace: boolean = false): Promise<Client> {\n\t\t// Reuse the cached keyspace-scoped client when available (avoids repeated\n\t\t// cassandra-driver cluster-discovery on every operation).\n\t\tif (!skipKeySpace && this._persistentClient !== undefined) {\n\t\t\treturn this._persistentClient;\n\t\t}\n\n\t\tconst client = new Client({\n\t\t\tcontactPoints: this._config.hosts,\n\t\t\tlocalDataCenter: this._config.localDataCenter,\n\t\t\tkeyspace: skipKeySpace ? undefined : this._config.keyspace,\n\t\t\tprotocolOptions: {\n\t\t\t\tport: this._config.port\n\t\t\t}\n\t\t});\n\t\tawait client.connect();\n\n\t\tif (!skipKeySpace) {\n\t\t\tthis._persistentClient = client;\n\t\t}\n\n\t\treturn client;\n\t}\n\n\t/**\n\t * Close database connection.\n\t * When `connection` is the cached persistent client it is kept alive so it\n\t * can be reused by future operations; call `closePersistentClient()` to\n\t * explicitly shut it down (e.g. from `teardown()`).\n\t * @param connection The connection to close.\n\t * @internal\n\t */\n\tprotected async closeConnection(connection?: Client): Promise<void> {\n\t\tif (!connection) {\n\t\t\treturn;\n\t\t}\n\t\tif (connection === this._persistentClient) {\n\t\t\t// Keep the persistent client alive for reuse.\n\t\t\treturn;\n\t\t}\n\t\treturn connection.shutdown();\n\t}\n\n\t/**\n\t * Shut down and clear the persistent client. Call this from `teardown()`\n\t * implementations to release the underlying TCP connection.\n\t * @internal\n\t */\n\tprotected async closePersistentClient(): Promise<void> {\n\t\tif (this._persistentClient !== undefined) {\n\t\t\tawait this._persistentClient.shutdown();\n\t\t\tthis._persistentClient = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Query the database.\n\t * @param connection The connection to query.\n\t * @param sql The sql statement to execute.\n\t * @param params The params to use when executing the query.\n\t * @param state The state to use when it comes to pagination.\n\t * @returns The rows.\n\t * @internal\n\t */\n\tprotected async queryDB(\n\t\tconnection: Client,\n\t\tsql: string,\n\t\tparams: unknown[],\n\t\tpageState?: string,\n\t\tlimit?: number\n\t): Promise<CassandraTypes.ResultSet> {\n\t\treturn new Promise<CassandraTypes.ResultSet>((resolve, reject) => {\n\t\t\tconst rows: CassandraTypes.Row[] = [];\n\n\t\t\tconnection.eachRow(\n\t\t\t\tsql,\n\t\t\t\tparams,\n\t\t\t\t{\n\t\t\t\t\tprepare: true,\n\t\t\t\t\tautoPage: false,\n\t\t\t\t\tfetchSize: limit ?? AbstractScyllaDBConnector.DEFAULT_LIMIT,\n\t\t\t\t\tpageState\n\t\t\t\t},\n\t\t\t\t(n: number, row: CassandraTypes.Row) => {\n\t\t\t\t\trows.push(row);\n\t\t\t\t},\n\t\t\t\t(err: Error, res: CassandraTypes.ResultSet) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tres.rows = rows;\n\t\t\t\t\tresolve(res);\n\t\t\t\t}\n\t\t\t);\n\t\t});\n\t}\n\n\t/**\n\t * Execute on the database.\n\t * @param connection The connection to execute.\n\t * @param sql The sql statement to execute.\n\t * @internal\n\t */\n\tprotected async execute(\n\t\tconnection: Client,\n\t\tsql: string,\n\t\tparams?: unknown[]\n\t): Promise<CassandraTypes.ResultSet> {\n\t\treturn connection.execute(sql, params, { prepare: true });\n\t}\n\n\t/**\n\t * Create keyspace if it doesn't exist.\n\t * @param connection The connection to perform the query with.\n\t * @param keyspaceName The name of the keyspace to create.\n\t * @internal\n\t */\n\tprotected async createKeyspace(\n\t\tconnection: Client,\n\t\tkeyspaceName: string\n\t): Promise<CassandraTypes.ResultSet> {\n\t\treturn this.execute(\n\t\t\tconnection,\n\t\t\t`CREATE KEYSPACE IF NOT EXISTS \"${keyspaceName}\" WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1}`\n\t\t);\n\t}\n\n\t/**\n\t * Check if a keyspace exists.\n\t * @param connection The connection to perform the query with.\n\t * @param keyspaceName The name of the keyspace to check.\n\t * @returns True if the keyspace exists, false otherwise.\n\t * @internal\n\t */\n\tprotected async checkKeyspaceExists(connection: Client, keyspaceName: string): Promise<boolean> {\n\t\tconst result = await this.queryDB(\n\t\t\tconnection,\n\t\t\t\"SELECT keyspace_name FROM system_schema.keyspaces WHERE keyspace_name = ?\",\n\t\t\t[keyspaceName]\n\t\t);\n\n\t\treturn result.rowLength > 0;\n\t}\n\n\t/**\n\t * Check if a type exists.\n\t * @param connection The connection to perform the query with.\n\t * @param typeName The name of the type to check.\n\t * @returns True if the type exists, false otherwise.\n\t * @internal\n\t */\n\tprotected async checkTypeExists(connection: Client, typeName: string): Promise<boolean> {\n\t\tconst result = await this.queryDB(\n\t\t\tconnection,\n\t\t\t\"SELECT type_name FROM system_schema.types WHERE type_name = ?\",\n\t\t\t[typeName]\n\t\t);\n\n\t\treturn result.rowLength > 0;\n\t}\n\n\t/**\n\t * Check if a table exists.\n\t * @param connection The connection to perform the query with.\n\t * @param keyspaceName The name of the keyspace to check.\n\t * @param tableName The name of the table to check.\n\t * @returns True if the table exists, false otherwise.\n\t * @internal\n\t */\n\tprotected async checkTableExists(\n\t\tconnection: Client,\n\t\tkeyspaceName: string,\n\t\ttableName: string\n\t): Promise<boolean> {\n\t\tconst result = await this.queryDB(\n\t\t\tconnection,\n\t\t\t\"SELECT table_name FROM system_schema.tables WHERE keyspace_name = ? AND table_name = ?\",\n\t\t\t[keyspaceName, tableName]\n\t\t);\n\n\t\treturn result.rowLength > 0;\n\t}\n\n\t/**\n\t * Format a field from the DB.\n\t * @param value The value to convert to original form.\n\t * @param fieldDescriptor The descriptor for the field.\n\t * @returns The value as a property for the object.\n\t * @internal\n\t */\n\tprotected dbValueToProperty(value: unknown, fieldDescriptor: IEntitySchemaProperty<T>): unknown {\n\t\tif (\n\t\t\tIs.stringValue(fieldDescriptor.itemTypeRef) &&\n\t\t\t(fieldDescriptor.type === \"object\" || fieldDescriptor.type === \"array\")\n\t\t) {\n\t\t\tconst objSchema = EntitySchemaFactory.get(fieldDescriptor.itemTypeRef);\n\t\t\treturn this.convertRowToObject(objSchema.properties, value as { [id: string]: unknown });\n\t\t} else if (\n\t\t\t// If the field is json format\n\t\t\t(fieldDescriptor.type === \"string\" && fieldDescriptor.format === \"json\") ||\n\t\t\t// Or its and object or array without a type ref\n\t\t\t(!Is.stringValue(fieldDescriptor.itemTypeRef) &&\n\t\t\t\t(fieldDescriptor.type === \"object\" || fieldDescriptor.type === \"array\"))\n\t\t) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(value as string);\n\t\t\t} catch {\n\t\t\t\tthrow new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, \"parseJSONFailed\", {\n\t\t\t\t\tname: fieldDescriptor.property,\n\t\t\t\t\tvalue\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (\n\t\t\tfieldDescriptor.type === \"string\" &&\n\t\t\t(fieldDescriptor.format === \"date-time\" || fieldDescriptor.format === \"date\") &&\n\t\t\tIs.date(value)\n\t\t) {\n\t\t\treturn Coerce.string(value);\n\t\t} else if (fieldDescriptor.type === \"object\") {\n\t\t\tif (\n\t\t\t\tvalue === \"null\" ||\n\t\t\t\tvalue === \"undefined\" ||\n\t\t\t\tvalue === \"\" ||\n\t\t\t\tvalue === null ||\n\t\t\t\tvalue === undefined\n\t\t\t) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} else if (fieldDescriptor.format === \"uuid\") {\n\t\t\treturn (value as CassandraTypes.Uuid).toString();\n\t\t}\n\n\t\treturn value;\n\t}\n\n\t/**\n\t * Format a value for the DB. As the driver takes care of conversion from Javascript\n\t * @param value The value to format.\n\t * @param fieldDescriptor The descriptor for the field\n\t * @returns The value after conversion.\n\t * @internal\n\t */\n\tprotected propertyToDbValue(value: unknown, fieldDescriptor?: IEntitySchemaProperty<T>): unknown {\n\t\tif (fieldDescriptor) {\n\t\t\t// If the field is json format\n\t\t\tif (\n\t\t\t\t(fieldDescriptor.type === \"string\" && fieldDescriptor.format === \"json\") ||\n\t\t\t\t// Or its and object or array without a type ref\n\t\t\t\t(!Is.stringValue(fieldDescriptor.itemTypeRef) &&\n\t\t\t\t\t(fieldDescriptor.type === \"object\" || fieldDescriptor.type === \"array\"))\n\t\t\t) {\n\t\t\t\treturn Is.empty(value) ? \"null\" : this.jsonWrap(value);\n\t\t\t} else if (fieldDescriptor.format === \"uuid\") {\n\t\t\t\tif (!Is.string(value)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\treturn CassandraTypes.Uuid.fromString(value);\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\t}\n\n\t/**\n\t * Convert a row back to an object.\n\t * @param properties The optional properties to convert.\n\t * @param row The row to convert.\n\t * @returns The row as an object.\n\t * @internal\n\t */\n\tprotected convertRowToObject(\n\t\tproperties: IEntitySchemaProperty<T>[] | undefined,\n\t\trow: { [id: string]: unknown }\n\t): T {\n\t\tconst obj: { [id: string]: unknown } = {};\n\n\t\tfor (const field of properties ?? []) {\n\t\t\tconst value = row[field.property as string];\n\t\t\tif (!Is.empty(value)) {\n\t\t\t\tobj[field.property as string] = this.dbValueToProperty(value, field);\n\t\t\t}\n\t\t}\n\n\t\treturn EntityStorageHelper.unPrepareEntity(obj as T, [AbstractScyllaDBConnector.PARTITION_KEY]);\n\t}\n\n\t/**\n\t * Wrap a string for DB format.\n\t * @param value The value to wrap.\n\t * @returns The wrapped string.\n\t * @internal\n\t */\n\tprotected stringWrap(value: string): string {\n\t\tif (value === undefined || value === null) {\n\t\t\treturn \"''\";\n\t\t}\n\n\t\treturn `'${value.replace(/'/g, \"''\")}'`;\n\t}\n\n\t/**\n\t * Wrap an object for json in DB format.\n\t * @param value The value to wrap.\n\t * @returns The wrapped string.\n\t * @internal\n\t */\n\tprotected jsonWrap(value: unknown): string {\n\t\tlet json = JSON.stringify(value);\n\n\t\tjson = json.replace(/[\\b\\0\\t\\n\\r\\u001A\\\\]/g, s => {\n\t\t\tswitch (s) {\n\t\t\t\tcase \"\\0\":\n\t\t\t\t\treturn String.raw`\\0`;\n\t\t\t\tcase \"\\n\":\n\t\t\t\t\treturn String.raw`\\n`;\n\t\t\t\tcase \"\\r\":\n\t\t\t\t\treturn String.raw`\\r`;\n\t\t\t\tcase \"\\b\":\n\t\t\t\t\treturn String.raw`\\b`;\n\t\t\t\tcase \"\\t\":\n\t\t\t\t\treturn String.raw`\\t`;\n\t\t\t\tcase \"\\u001A\":\n\t\t\t\t\treturn String.raw`\\Z`;\n\t\t\t\tdefault:\n\t\t\t\t\treturn `\\\\${s}`;\n\t\t\t}\n\t\t});\n\t\treturn json;\n\t}\n\n\t/**\n\t * Build the conditions for the query.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The SQL conditions and the values.\n\t * @internal\n\t */\n\tprotected buildConditions(conditions: { property: keyof T; value: unknown }[] | undefined): {\n\t\tsqlCondition: string;\n\t\tconditionValues: unknown[];\n\t} {\n\t\tconst conditionValues: unknown[] = [];\n\t\tconst sqlConditions: string[] = [];\n\n\t\tconst properties = (this._entitySchema.properties ?? []).concat([\n\t\t\t{\n\t\t\t\tproperty: AbstractScyllaDBConnector.PARTITION_KEY,\n\t\t\t\ttype: \"string\"\n\t\t\t} as IEntitySchemaProperty<T>\n\t\t]);\n\n\t\tif (Is.arrayValue(conditions)) {\n\t\t\tfor (const condition of conditions) {\n\t\t\t\tsqlConditions.push(`\"${condition.property as string}\"=?`);\n\t\t\t\tconst schemaProperty = properties.find(s => s.property === condition.property);\n\t\t\t\tconditionValues.push(this.propertyToDbValue(condition.value, schemaProperty));\n\t\t\t}\n\t\t}\n\t\treturn { sqlCondition: sqlConditions.join(\" AND \"), conditionValues };\n\t}\n\n\t/**\n\t * Get a safe table name by replacing any non-alphanumeric characters.\n\t * @param name The name to sanitize.\n\t * @returns The safe table name.\n\t */\n\tprotected safeTableName(name: string): string {\n\t\treturn name.replace(/[^\\dA-Za-z]/g, \"\");\n\t}\n\n\t/**\n\t * Parse, validate, and build a CQL WHERE clause from an EntityCondition tree.\n\t * The partition key equality is always the first clause; user conditions follow.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @param partitionKey The partition key value to filter by.\n\t * @returns The complete WHERE clause (without the WHERE keyword) and bound params.\n\t * @internal\n\t */\n\tprivate buildCqlConditions(\n\t\tconditions: EntityCondition<T> | undefined,\n\t\tpartitionKey: string | undefined\n\t): { whereClause: string; params: unknown[] } {\n\t\tlet conditionsList: EntityCondition<T>[] = [];\n\t\tif (conditions !== undefined) {\n\t\t\tif (\"conditions\" in conditions) {\n\t\t\t\tif ((conditions as IComparatorGroup).logicalOperator === LogicalOperator.Or) {\n\t\t\t\t\tthrow new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, \"orConditionNotSupported\");\n\t\t\t\t}\n\t\t\t\tconditionsList = conditions.conditions;\n\t\t\t} else {\n\t\t\t\tconditionsList = [conditions];\n\t\t\t}\n\t\t}\n\n\t\tfor (const cond of conditionsList) {\n\t\t\tconst comparator = cond as IComparator;\n\t\t\tif (String(comparator.property).includes(\".\")) {\n\t\t\t\tthrow new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, \"comparisonNotSupported\", {\n\t\t\t\t\tproperty: comparator.property,\n\t\t\t\t\treason: \"dot-notation nested property paths are not supported in CQL\"\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (\n\t\t\t\t(comparator.comparison === ComparisonOperator.Equals ||\n\t\t\t\t\tcomparator.comparison === ComparisonOperator.NotEquals) &&\n\t\t\t\t(comparator.value === null || comparator.value === undefined)\n\t\t\t) {\n\t\t\t\tthrow new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, \"comparisonNotSupported\", {\n\t\t\t\t\tproperty: comparator.property,\n\t\t\t\t\treason: \"null/undefined comparisons are not supported in CQL WHERE clauses\"\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (comparator.comparison === ComparisonOperator.NotEquals) {\n\t\t\t\tthrow new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, \"notEqualsNotSupported\", {\n\t\t\t\t\tproperty: comparator.property\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (comparator.comparison === ComparisonOperator.NotIncludes) {\n\t\t\t\tthrow new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, \"notIncludesNotSupported\", {\n\t\t\t\t\tproperty: comparator.property\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tconst conds: string[] = [];\n\t\tconst params: unknown[] = [partitionKey ?? AbstractScyllaDBConnector.PARTITION_KEY_VALUE];\n\n\t\tfor (const cond of conditionsList) {\n\t\t\tconst condition = cond as IComparator;\n\t\t\tconst descriptor = this._entitySchema.properties?.find(\n\t\t\t\tp => p.property === condition.property\n\t\t\t);\n\t\t\tif (\n\t\t\t\tcondition.comparison === ComparisonOperator.Includes ||\n\t\t\t\tcondition.comparison === ComparisonOperator.NotIncludes\n\t\t\t) {\n\t\t\t\tconst serialized = this.propertyToDbValue(condition.value, descriptor);\n\t\t\t\tconst propValue = `'%${Is.stringValue(serialized) ? serialized : \"\"}%'`;\n\t\t\t\tif (condition.comparison === ComparisonOperator.Includes) {\n\t\t\t\t\tconds.push(`\"${condition.property}\" LIKE ${propValue}`);\n\t\t\t\t} else if (condition.comparison === ComparisonOperator.NotIncludes) {\n\t\t\t\t\tconds.push(`\"${condition.property}\" NOT LIKE ${propValue}`);\n\t\t\t\t}\n\t\t\t} else if (condition.comparison === ComparisonOperator.In) {\n\t\t\t\tlet value: unknown[] = [];\n\t\t\t\tif (!Is.arrayValue(condition.value)) {\n\t\t\t\t\tvalue.push(this.propertyToDbValue(condition.value, descriptor));\n\t\t\t\t} else {\n\t\t\t\t\tvalue = condition.value.map(v => this.propertyToDbValue(v, descriptor));\n\t\t\t\t}\n\t\t\t\tparams.push(value);\n\t\t\t\tconds.push(`\"${condition.property}\" IN ?`);\n\t\t\t} else {\n\t\t\t\tconst propValue = this.propertyToDbValue(condition.value, descriptor);\n\t\t\t\tparams.push(propValue);\n\t\t\t\tif (condition.comparison === ComparisonOperator.Equals) {\n\t\t\t\t\tconds.push(`\"${condition.property}\" = ?`);\n\t\t\t\t} else if (condition.comparison === ComparisonOperator.NotEquals) {\n\t\t\t\t\tconds.push(`\"${condition.property}\" != ?`);\n\t\t\t\t} else if (condition.comparison === ComparisonOperator.GreaterThan) {\n\t\t\t\t\tconds.push(`\"${condition.property}\" > ?`);\n\t\t\t\t} else if (condition.comparison === ComparisonOperator.LessThan) {\n\t\t\t\t\tconds.push(`\"${condition.property}\" < ?`);\n\t\t\t\t} else if (condition.comparison === ComparisonOperator.GreaterThanOrEqual) {\n\t\t\t\t\tconds.push(`\"${condition.property}\" >= ?`);\n\t\t\t\t} else if (condition.comparison === ComparisonOperator.LessThanOrEqual) {\n\t\t\t\t\tconds.push(`\"${condition.property}\" <= ?`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst operator =\n\t\t\t\"conditions\" in (conditions ?? {})\n\t\t\t\t? ((conditions as IComparatorGroup).logicalOperator ?? LogicalOperator.And)\n\t\t\t\t: LogicalOperator.And;\n\n\t\tlet whereClause = `\"${AbstractScyllaDBConnector.PARTITION_KEY}\" = ?`;\n\t\tif (conds.length > 0) {\n\t\t\twhereClause += ` AND ${conds.join(` ${operator} `)}`;\n\t\t}\n\n\t\treturn { whereClause, params };\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IScyllaDBTableConfig.js","sourceRoot":"","sources":["../../../src/models/IScyllaDBTableConfig.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IScyllaDBConfig } from \"./IScyllaDBConfig.js\";\n\n/**\n * Definition of MySQL DB configuration.\n */\nexport interface IScyllaDBTableConfig extends IScyllaDBConfig {\n\t/**\n\t * The name of the table for the storage.\n\t * @default To the camel case of the entity name.\n\t */\n\ttableName
|
|
1
|
+
{"version":3,"file":"IScyllaDBTableConfig.js","sourceRoot":"","sources":["../../../src/models/IScyllaDBTableConfig.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IScyllaDBConfig } from \"./IScyllaDBConfig.js\";\n\n/**\n * Definition of MySQL DB configuration.\n */\nexport interface IScyllaDBTableConfig extends IScyllaDBConfig {\n\t/**\n\t * The name of the table for the storage.\n\t * @default To the camel case of the entity name.\n\t */\n\ttableName: string;\n}\n"]}
|