@twin.org/entity-storage-connector-scylladb 0.0.3-next.21 → 0.0.3-next.23

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.
@@ -1,7 +1,7 @@
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 } from "@twin.org/core";
4
+ import { Coerce, ComponentFactory, GeneralError, Guards, Is, Validation } from "@twin.org/core";
5
5
  import { ComparisonOperator, EntitySchemaFactory, EntitySchemaHelper, LogicalOperator, SortDirection } from "@twin.org/entity";
6
6
  import { EntityStorageHelper } from "@twin.org/entity-storage-models";
7
7
  import { types as CassandraTypes, Client } from "cassandra-driver";
@@ -165,6 +165,26 @@ export class AbstractScyllaDBConnector {
165
165
  let connection;
166
166
  const contextIds = await ContextIdStore.getContextIds();
167
167
  const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
168
+ EntityStorageHelper.validateSortProperties(this._entitySchema, sortProperties);
169
+ EntityStorageHelper.validateProperties(this._entitySchema, properties);
170
+ if (!Is.empty(limit)) {
171
+ const validationFailures = [];
172
+ Validation.integer("limit", limit, validationFailures, undefined, { minValue: 1 });
173
+ Validation.asValidationError(AbstractScyllaDBConnector.CLASS_NAME, "query", validationFailures);
174
+ }
175
+ // CQL ORDER BY is only valid on clustering columns (isPrimary). Secondary-index
176
+ // properties cannot be used; throw before reaching the try-catch so the error
177
+ // surfaces directly to the caller without being wrapped as findFailed.
178
+ if (Is.arrayValue(sortProperties)) {
179
+ for (const sortProperty of sortProperties) {
180
+ const propertySchema = this._entitySchema.properties?.find(p => p.property === sortProperty.property);
181
+ if (!propertySchema?.isPrimary) {
182
+ throw new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, "sortOnlyPrimaryKey", {
183
+ property: sortProperty.property
184
+ });
185
+ }
186
+ }
187
+ }
168
188
  // Validates and throws for unsupported conditions before entering the try-catch
169
189
  // so that comparisonNotSupported errors surface directly to the caller.
170
190
  const { whereClause, params, noResults } = this.buildCqlConditions(conditions, partitionKey);
@@ -253,7 +273,6 @@ export class AbstractScyllaDBConnector {
253
273
  }
254
274
  /**
255
275
  * Open a new database connection.
256
- * @param config The config for the connection.
257
276
  * @param skipKeySpace Don't include the keyspace in the connection.
258
277
  * @returns The new connection.
259
278
  * @internal
@@ -284,6 +303,7 @@ export class AbstractScyllaDBConnector {
284
303
  * can be reused by future operations; call `closePersistentClient()` to
285
304
  * explicitly shut it down (e.g. from `teardown()`).
286
305
  * @param connection The connection to close.
306
+ * @returns Nothing.
287
307
  * @internal
288
308
  */
289
309
  async closeConnection(connection) {
@@ -312,7 +332,8 @@ export class AbstractScyllaDBConnector {
312
332
  * @param connection The connection to query.
313
333
  * @param sql The sql statement to execute.
314
334
  * @param params The params to use when executing the query.
315
- * @param state The state to use when it comes to pagination.
335
+ * @param pageState The page state to use when it comes to pagination.
336
+ * @param limit The maximum number of rows to return.
316
337
  * @returns The rows.
317
338
  * @internal
318
339
  */
@@ -340,6 +361,8 @@ export class AbstractScyllaDBConnector {
340
361
  * Execute on the database.
341
362
  * @param connection The connection to execute.
342
363
  * @param sql The sql statement to execute.
364
+ * @param params The optional params to use when executing the statement.
365
+ * @returns The result set.
343
366
  * @internal
344
367
  */
345
368
  async execute(connection, sql, params) {
@@ -349,6 +372,7 @@ export class AbstractScyllaDBConnector {
349
372
  * Create keyspace if it doesn't exist.
350
373
  * @param connection The connection to perform the query with.
351
374
  * @param keyspaceName The name of the keyspace to create.
375
+ * @returns The result set.
352
376
  * @internal
353
377
  */
354
378
  async createKeyspace(connection, keyspaceName) {
@@ -393,6 +417,7 @@ export class AbstractScyllaDBConnector {
393
417
  * @param value The value to convert to original form.
394
418
  * @param fieldDescriptor The descriptor for the field.
395
419
  * @returns The value as a property for the object.
420
+ * @throws GeneralError if parsing JSON fails.
396
421
  * @internal
397
422
  */
398
423
  dbValueToProperty(value, fieldDescriptor) {
@@ -437,9 +462,9 @@ export class AbstractScyllaDBConnector {
437
462
  return value;
438
463
  }
439
464
  /**
440
- * Format a value for the DB. As the driver takes care of conversion from Javascript
465
+ * Format a value for the DB.
441
466
  * @param value The value to format.
442
- * @param fieldDescriptor The descriptor for the field
467
+ * @param fieldDescriptor The descriptor for the field.
443
468
  * @returns The value after conversion.
444
469
  * @internal
445
470
  */
@@ -556,6 +581,7 @@ export class AbstractScyllaDBConnector {
556
581
  * @param conditions The optional conditions to match for the entities.
557
582
  * @param partitionKey The partition key value to filter by.
558
583
  * @returns The complete WHERE clause (without the WHERE keyword) and bound params.
584
+ * @throws GeneralError if OR conditions, dot-notation paths, null comparisons, NotEquals, or NotIncludes operators are used.
559
585
  * @internal
560
586
  */
561
587
  buildCqlConditions(conditions, partitionKey) {
@@ -1 +1 @@
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, noResults } = this.buildCqlConditions(conditions, partitionKey);\n\n\t\tif (noResults) {\n\t\t\treturn { entities: [], cursor: undefined };\n\t\t}\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, noResults } = this.buildCqlConditions(conditions, partitionKey);\n\n\t\t\tif (noResults) {\n\t\t\t\treturn 0;\n\t\t\t}\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[]; noResults?: boolean } {\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\t// Guard must come first: Is.arrayValue([]) returns false for an empty array,\n\t\t\t\t// so an empty value would be wrapped as a single element below and bypass\n\t\t\t\t// the length check. Check Is.array (true for any array) before branching (#141).\n\t\t\t\tif (Is.array(condition.value) && condition.value.length === 0) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\twhereClause: \"\",\n\t\t\t\t\t\tparams: [],\n\t\t\t\t\t\tnoResults: true\n\t\t\t\t\t};\n\t\t\t\t}\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
+ {"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 {\n\tCoerce,\n\tComponentFactory,\n\tGeneralError,\n\tGuards,\n\tIs,\n\ttype IValidationFailure,\n\tValidation\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 { 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\tEntityStorageHelper.validateSortProperties(this._entitySchema, sortProperties);\n\t\tEntityStorageHelper.validateProperties(this._entitySchema, properties);\n\n\t\tif (!Is.empty(limit)) {\n\t\t\tconst validationFailures: IValidationFailure[] = [];\n\t\t\tValidation.integer(nameof(limit), limit, validationFailures, undefined, { minValue: 1 });\n\t\t\tValidation.asValidationError(\n\t\t\t\tAbstractScyllaDBConnector.CLASS_NAME,\n\t\t\t\t\"query\",\n\t\t\t\tvalidationFailures\n\t\t\t);\n\t\t}\n\n\t\t// CQL ORDER BY is only valid on clustering columns (isPrimary). Secondary-index\n\t\t// properties cannot be used; throw before reaching the try-catch so the error\n\t\t// surfaces directly to the caller without being wrapped as findFailed.\n\t\tif (Is.arrayValue(sortProperties)) {\n\t\t\tfor (const sortProperty of sortProperties) {\n\t\t\t\tconst propertySchema = this._entitySchema.properties?.find(\n\t\t\t\t\tp => p.property === sortProperty.property\n\t\t\t\t);\n\t\t\t\tif (!propertySchema?.isPrimary) {\n\t\t\t\t\tthrow new GeneralError(AbstractScyllaDBConnector.CLASS_NAME, \"sortOnlyPrimaryKey\", {\n\t\t\t\t\t\tproperty: sortProperty.property\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\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, noResults } = this.buildCqlConditions(conditions, partitionKey);\n\n\t\tif (noResults) {\n\t\t\treturn { entities: [], cursor: undefined };\n\t\t}\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, noResults } = this.buildCqlConditions(conditions, partitionKey);\n\n\t\t\tif (noResults) {\n\t\t\t\treturn 0;\n\t\t\t}\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 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 * @returns Nothing.\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 pageState The page state to use when it comes to pagination.\n\t * @param limit The maximum number of rows to return.\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 * @param params The optional params to use when executing the statement.\n\t * @returns The result set.\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 * @returns The result set.\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 * @throws GeneralError if parsing JSON fails.\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.\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 * @throws GeneralError if OR conditions, dot-notation paths, null comparisons, NotEquals, or NotIncludes operators are used.\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[]; noResults?: boolean } {\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\t// Guard must come first: Is.arrayValue([]) returns false for an empty array,\n\t\t\t\t// so an empty value would be wrapped as a single element below and bypass\n\t\t\t\t// the length check. Check Is.array (true for any array) before branching (#141).\n\t\t\t\tif (Is.array(condition.value) && condition.value.length === 0) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\twhereClause: \"\",\n\t\t\t\t\t\tparams: [],\n\t\t\t\t\t\tnoResults: true\n\t\t\t\t\t};\n\t\t\t\t}\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"]}
package/docs/changelog.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.3-next.23](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-scylladb-v0.0.3-next.22...entity-storage-connector-scylladb-v0.0.3-next.23) (2026-06-08)
4
+
5
+
6
+ ### Features
7
+
8
+ * entity storage conditions ([#115](https://github.com/iotaledger/twin-entity-storage/issues/115)) ([7a53884](https://github.com/iotaledger/twin-entity-storage/commit/7a53884f6acb856d77733e4e0f23ec1c00b74cb4))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/entity-storage-models bumped from 0.0.3-next.22 to 0.0.3-next.23
16
+ * devDependencies
17
+ * @twin.org/entity-storage-connector-memory bumped from 0.0.3-next.22 to 0.0.3-next.23
18
+
19
+ ## [0.0.3-next.22](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-scylladb-v0.0.3-next.21...entity-storage-connector-scylladb-v0.0.3-next.22) (2026-06-08)
20
+
21
+
22
+ ### Features
23
+
24
+ * add ISchemaMigration chain, SchemaVersionMigrator runner and version store ([#110](https://github.com/iotaledger/twin-entity-storage/issues/110)) ([2dac924](https://github.com/iotaledger/twin-entity-storage/commit/2dac9244a752cb58304d1649ff03c3a2469783dd))
25
+
26
+
27
+ ### Dependencies
28
+
29
+ * The following workspace dependencies were updated
30
+ * dependencies
31
+ * @twin.org/entity-storage-models bumped from 0.0.3-next.21 to 0.0.3-next.22
32
+ * devDependencies
33
+ * @twin.org/entity-storage-connector-memory bumped from 0.0.3-next.21 to 0.0.3-next.22
34
+
3
35
  ## [0.0.3-next.21](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-scylladb-v0.0.3-next.20...entity-storage-connector-scylladb-v0.0.3-next.21) (2026-06-01)
4
36
 
5
37
 
package/locales/en.json CHANGED
@@ -43,7 +43,8 @@
43
43
  "notEqualsNotSupported": "The NotEquals operator is not supported in CQL queries for property \"{property}\"",
44
44
  "notIncludesNotSupported": "The NotIncludes operator is not supported in CQL queries for property \"{property}\"",
45
45
  "orConditionNotSupported": "OR logical operators are not supported in CQL queries",
46
- "countFailed": "Unable to count entities"
46
+ "countFailed": "Unable to count entities",
47
+ "sortOnlyPrimaryKey": "ScyllaDB only supports ORDER BY on clustering (primary key) columns; \"{property}\" is a secondary index and cannot be used for sorting"
47
48
  }
48
49
  },
49
50
  "health": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/entity-storage-connector-scylladb",
3
- "version": "0.0.3-next.21",
3
+ "version": "0.0.3-next.23",
4
4
  "description": "ScyllaDB connector for distributed, high-throughput persistence.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,7 +17,7 @@
17
17
  "@twin.org/context": "next",
18
18
  "@twin.org/core": "next",
19
19
  "@twin.org/entity": "next",
20
- "@twin.org/entity-storage-models": "0.0.3-next.21",
20
+ "@twin.org/entity-storage-models": "0.0.3-next.23",
21
21
  "@twin.org/logging-models": "next",
22
22
  "@twin.org/nameof": "next",
23
23
  "cassandra-driver": "4.9.0"