@naturalcycles/db-lib 9.15.1 → 9.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,8 +31,9 @@ function queryInMemory(q, rows = []) {
31
31
  if (order) {
32
32
  const { name, descending } = order;
33
33
  rows = rows.sort((a, b) => {
34
+ // biome-ignore lint/suspicious/noDoubleEquals: ok
34
35
  if (a[name] == b[name])
35
- return 0; // eslint-disable-line eqeqeq
36
+ return 0;
36
37
  if (descending) {
37
38
  return a[name] < b[name] ? 1 : -1;
38
39
  }
@@ -12,15 +12,15 @@ export declare class BaseCommonDB implements CommonDB {
12
12
  support: CommonDBSupport;
13
13
  ping(): Promise<void>;
14
14
  getTables(): Promise<string[]>;
15
- getTableSchema<ROW extends ObjectWithId>(table: string): Promise<JsonSchemaRootObject<ROW>>;
16
- createTable<ROW extends ObjectWithId>(table: string, schema: JsonSchemaObject<ROW>): Promise<void>;
17
- getByIds<ROW extends ObjectWithId>(table: string, ids: string[]): Promise<ROW[]>;
18
- deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<number>;
19
- updateByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: DBPatch<ROW>, opt?: CommonDBOptions): Promise<number>;
20
- runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<RunQueryResult<ROW>>;
21
- runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<number>;
22
- saveBatch<ROW extends ObjectWithId>(table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
23
- streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): ReadableTyped<ROW>;
24
- deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number>;
25
- runInTransaction(fn: DBTransactionFn, opt?: CommonDBTransactionOptions): Promise<void>;
15
+ getTableSchema<ROW extends ObjectWithId>(_table: string): Promise<JsonSchemaRootObject<ROW>>;
16
+ createTable<ROW extends ObjectWithId>(_table: string, _schema: JsonSchemaObject<ROW>): Promise<void>;
17
+ getByIds<ROW extends ObjectWithId>(_table: string, _ids: string[]): Promise<ROW[]>;
18
+ deleteByQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number>;
19
+ updateByQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>, _patch: DBPatch<ROW>, _opt?: CommonDBOptions): Promise<number>;
20
+ runQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<RunQueryResult<ROW>>;
21
+ runQueryCount<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number>;
22
+ saveBatch<ROW extends ObjectWithId>(_table: string, _rows: ROW[], _opt?: CommonDBSaveOptions<ROW>): Promise<void>;
23
+ streamQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): ReadableTyped<ROW>;
24
+ deleteByIds(_table: string, _ids: string[], _opt?: CommonDBOptions): Promise<number>;
25
+ runInTransaction(fn: DBTransactionFn, _opt?: CommonDBTransactionOptions): Promise<void>;
26
26
  }
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseCommonDB = void 0;
4
4
  const common_db_1 = require("./common.db");
5
5
  const dbTransaction_util_1 = require("./transaction/dbTransaction.util");
6
- /* eslint-disable unused-imports/no-unused-vars */
7
6
  /**
8
7
  * No-op implementation of CommonDB interface.
9
8
  * To be extended by actual implementations.
@@ -19,37 +18,37 @@ class BaseCommonDB {
19
18
  async getTables() {
20
19
  throw new Error('getTables is not implemented');
21
20
  }
22
- async getTableSchema(table) {
21
+ async getTableSchema(_table) {
23
22
  throw new Error('getTableSchema is not implemented');
24
23
  }
25
- async createTable(table, schema) {
24
+ async createTable(_table, _schema) {
26
25
  // no-op
27
26
  }
28
- async getByIds(table, ids) {
27
+ async getByIds(_table, _ids) {
29
28
  throw new Error('getByIds is not implemented');
30
29
  }
31
- async deleteByQuery(q) {
30
+ async deleteByQuery(_q) {
32
31
  throw new Error('deleteByQuery is not implemented');
33
32
  }
34
- async updateByQuery(q, patch, opt) {
33
+ async updateByQuery(_q, _patch, _opt) {
35
34
  throw new Error('updateByQuery is not implemented');
36
35
  }
37
- async runQuery(q) {
36
+ async runQuery(_q) {
38
37
  throw new Error('runQuery is not implemented');
39
38
  }
40
- async runQueryCount(q) {
39
+ async runQueryCount(_q) {
41
40
  throw new Error('runQueryCount is not implemented');
42
41
  }
43
- async saveBatch(table, rows, opt) {
42
+ async saveBatch(_table, _rows, _opt) {
44
43
  throw new Error('saveBatch is not implemented');
45
44
  }
46
- streamQuery(q) {
45
+ streamQuery(_q) {
47
46
  throw new Error('streamQuery is not implemented');
48
47
  }
49
- async deleteByIds(table, ids, opt) {
48
+ async deleteByIds(_table, _ids, _opt) {
50
49
  throw new Error('deleteByIds is not implemented');
51
50
  }
52
- async runInTransaction(fn, opt) {
51
+ async runInTransaction(fn, _opt) {
53
52
  const tx = new dbTransaction_util_1.FakeDBTransaction(this);
54
53
  await fn(tx);
55
54
  // there's no try/catch and rollback, as there's nothing to rollback
@@ -157,6 +157,10 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
157
157
  */
158
158
  ping(): Promise<void>;
159
159
  runInTransaction<T = void>(fn: CommonDaoTransactionFn<T>, opt?: CommonDBTransactionOptions): Promise<T>;
160
+ /**
161
+ * Throws if query uses a property that is in `excludeFromIndexes` list.
162
+ */
163
+ private validateQueryIndexes;
160
164
  protected logResult(started: number, op: string, res: any, table: string): void;
161
165
  protected logSaveResult(started: number, op: string, table: string): void;
162
166
  protected logStarted(op: string, table: string, force?: boolean): UnixTimestampMillisNumber;
@@ -202,6 +202,7 @@ class CommonDao {
202
202
  return (0, js_lib_1._uniqBy)(results, r => r.id);
203
203
  }
204
204
  async runQueryExtended(q, opt = {}) {
205
+ this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
205
206
  q.table = opt.table || q.table;
206
207
  const op = `runQuery(${q.pretty()})`;
207
208
  const started = this.logStarted(op, q.table);
@@ -222,6 +223,7 @@ class CommonDao {
222
223
  return rows;
223
224
  }
224
225
  async runQueryExtendedAsDBM(q, opt = {}) {
226
+ this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
225
227
  q.table = opt.table || q.table;
226
228
  const op = `runQueryAsDBM(${q.pretty()})`;
227
229
  const started = this.logStarted(op, q.table);
@@ -235,6 +237,7 @@ class CommonDao {
235
237
  return { rows: dbms, ...queryResult };
236
238
  }
237
239
  async runQueryCount(q, opt = {}) {
240
+ this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
238
241
  q.table = opt.table || q.table;
239
242
  const op = `runQueryCount(${q.pretty()})`;
240
243
  const started = this.logStarted(op, q.table);
@@ -245,6 +248,7 @@ class CommonDao {
245
248
  return count;
246
249
  }
247
250
  async streamQueryForEach(q, mapper, opt = {}) {
251
+ this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
248
252
  q.table = opt.table || q.table;
249
253
  opt.skipValidation = opt.skipValidation !== false; // default true
250
254
  opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
@@ -283,6 +287,7 @@ class CommonDao {
283
287
  }
284
288
  }
285
289
  async streamQueryAsDBMForEach(q, mapper, opt = {}) {
290
+ this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
286
291
  q.table = opt.table || q.table;
287
292
  opt.skipValidation = opt.skipValidation !== false; // default true
288
293
  opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
@@ -324,6 +329,7 @@ class CommonDao {
324
329
  * Stream as Readable, to be able to .pipe() it further with support of backpressure.
325
330
  */
326
331
  streamQueryAsDBM(q, opt = {}) {
332
+ this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
327
333
  q.table = opt.table || q.table;
328
334
  opt.skipValidation = opt.skipValidation !== false; // default true
329
335
  opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
@@ -354,6 +360,7 @@ class CommonDao {
354
360
  * You can do `.pipe(transformNoOp)` to make it "valid again".
355
361
  */
356
362
  streamQuery(q, opt = {}) {
363
+ this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
357
364
  q.table = opt.table || q.table;
358
365
  opt.skipValidation = opt.skipValidation !== false; // default true
359
366
  opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
@@ -394,11 +401,13 @@ class CommonDao {
394
401
  .pipe((0, nodejs_lib_1.transformNoOp)()));
395
402
  }
396
403
  async queryIds(q, opt = {}) {
404
+ this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
397
405
  q.table = opt.table || q.table;
398
406
  const { rows } = await this.cfg.db.runQuery(q.select(['id']), opt);
399
407
  return rows.map(r => r.id);
400
408
  }
401
409
  streamQueryIds(q, opt = {}) {
410
+ this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
402
411
  q.table = opt.table || q.table;
403
412
  opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
404
413
  // Experimental: using `.map()`
@@ -417,6 +426,7 @@ class CommonDao {
417
426
  return stream;
418
427
  }
419
428
  async streamQueryIdsForEach(q, mapper, opt = {}) {
429
+ this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
420
430
  q.table = opt.table || q.table;
421
431
  opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
422
432
  const op = `streamQueryIdsForEach(${q.pretty()})`;
@@ -777,6 +787,7 @@ class CommonDao {
777
787
  * This is expected to be more memory-efficient way of deleting large number of rows.
778
788
  */
779
789
  async deleteByQuery(q, opt = {}) {
790
+ this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
780
791
  this.requireWriteAccess();
781
792
  this.requireObjectMutability(opt);
782
793
  q.table = opt.table || q.table;
@@ -820,6 +831,7 @@ class CommonDao {
820
831
  return await this.updateByQuery(this.query().filterIn('id', ids), patch, opt);
821
832
  }
822
833
  async updateByQuery(q, patch, opt = {}) {
834
+ this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
823
835
  this.requireWriteAccess();
824
836
  this.requireObjectMutability(opt);
825
837
  q.table = opt.table || q.table;
@@ -959,6 +971,19 @@ class CommonDao {
959
971
  }, opt);
960
972
  return r;
961
973
  }
974
+ /**
975
+ * Throws if query uses a property that is in `excludeFromIndexes` list.
976
+ */
977
+ validateQueryIndexes(q) {
978
+ const { excludeFromIndexes } = this.cfg;
979
+ if (!excludeFromIndexes)
980
+ return;
981
+ for (const f of q._filters) {
982
+ (0, js_lib_1._assert)(!excludeFromIndexes.includes(f.name), `cannot query on non-indexed property: ${this.cfg.table}.${f.name}`, {
983
+ query: q.pretty(),
984
+ });
985
+ }
986
+ }
962
987
  logResult(started, op, res, table) {
963
988
  if (!this.cfg.logLevel)
964
989
  return;
@@ -8,7 +8,6 @@ const __1 = require("..");
8
8
  const common_dao_1 = require("../commondao/common.dao");
9
9
  const dbTest_1 = require("./dbTest");
10
10
  const test_model_1 = require("./test.model");
11
- // eslint-disable-next-line jest/no-export
12
11
  function runCommonDaoTest(db, quirks = {}) {
13
12
  const { support } = db;
14
13
  const dao = new common_dao_1.CommonDao({
@@ -7,11 +7,10 @@ const common_db_1 = require("../common.db");
7
7
  const db_model_1 = require("../db.model");
8
8
  const dbQuery_1 = require("../query/dbQuery");
9
9
  const test_model_1 = require("./test.model");
10
- const test_util_1 = require("./test.util");
11
10
  function runCommonDBTest(db, quirks = {}) {
12
11
  const { support } = db;
13
12
  const items = (0, test_model_1.createTestItemsDBM)(3);
14
- (0, test_util_1.deepFreeze)(items);
13
+ (0, js_lib_1._deepFreeze)(items);
15
14
  const item1 = items[0];
16
15
  const queryAll = () => dbQuery_1.DBQuery.create(test_model_1.TEST_TABLE);
17
16
  test('ping', async () => {
@@ -54,7 +53,7 @@ function runCommonDBTest(db, quirks = {}) {
54
53
  ...(0, test_model_1.createTestItemDBM)(3),
55
54
  k2: null,
56
55
  };
57
- (0, test_util_1.deepFreeze)(item3);
56
+ (0, js_lib_1._deepFreeze)(item3);
58
57
  await db.saveBatch(test_model_1.TEST_TABLE, [item3]);
59
58
  const item3Loaded = (await db.getByIds(test_model_1.TEST_TABLE, [item3.id]))[0];
60
59
  expectMatch([item3], [item3Loaded], quirks);
@@ -67,7 +66,7 @@ function runCommonDBTest(db, quirks = {}) {
67
66
  ...(0, test_model_1.createTestItemDBM)(3),
68
67
  k2: undefined,
69
68
  };
70
- (0, test_util_1.deepFreeze)(item3);
69
+ (0, js_lib_1._deepFreeze)(item3);
71
70
  const expected = { ...item3 };
72
71
  delete expected.k2;
73
72
  await db.saveBatch(test_model_1.TEST_TABLE, [item3]);
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runCommonKeyValueDBTest = runCommonKeyValueDBTest;
4
4
  const js_lib_1 = require("@naturalcycles/js-lib");
5
5
  const test_model_1 = require("./test.model");
6
- /* eslint-disable jest/no-export */
7
6
  const testIds = (0, js_lib_1._range)(1, 4).map(n => `id${n}`);
8
7
  const testEntries = testIds.map(id => [id, Buffer.from(`${id}value`)]);
9
8
  function runCommonKeyValueDBTest(db) {
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runCommonKeyValueDaoTest = runCommonKeyValueDaoTest;
4
4
  const js_lib_1 = require("@naturalcycles/js-lib");
5
- /* eslint-disable jest/no-export */
6
5
  const testIds = (0, js_lib_1._range)(1, 4).map(n => `id${n}`);
7
6
  const testEntries = testIds.map(id => [id, Buffer.from(`${id}value`)]);
8
7
  function runCommonKeyValueDaoTest(dao) {
package/package.json CHANGED
@@ -13,6 +13,7 @@
13
13
  "@naturalcycles/nodejs-lib": "^13.1.1"
14
14
  },
15
15
  "devDependencies": {
16
+ "@biomejs/biome": "^1.8.3",
16
17
  "@naturalcycles/bench-lib": "^3.0.0",
17
18
  "@naturalcycles/dev-lib": "^15.4.1",
18
19
  "@types/node": "^22.1.0",
@@ -45,7 +46,7 @@
45
46
  "engines": {
46
47
  "node": ">=20.13"
47
48
  },
48
- "version": "9.15.1",
49
+ "version": "9.16.0",
49
50
  "description": "Lowest Common Denominator API to supported Databases",
50
51
  "keywords": [
51
52
  "db",
@@ -35,7 +35,8 @@ export function queryInMemory<ROW extends ObjectWithId>(q: DBQuery<ROW>, rows: R
35
35
  if (order) {
36
36
  const { name, descending } = order
37
37
  rows = rows.sort((a, b) => {
38
- if (a[name] == b[name]) return 0 // eslint-disable-line eqeqeq
38
+ // biome-ignore lint/suspicious/noDoubleEquals: ok
39
+ if (a[name] == b[name]) return 0
39
40
 
40
41
  if (descending) {
41
42
  return a[name] < b[name] ? 1 : -1
@@ -12,8 +12,6 @@ import {
12
12
  import { DBQuery } from './query/dbQuery'
13
13
  import { FakeDBTransaction } from './transaction/dbTransaction.util'
14
14
 
15
- /* eslint-disable unused-imports/no-unused-vars */
16
-
17
15
  /**
18
16
  * No-op implementation of CommonDB interface.
19
17
  * To be extended by actual implementations.
@@ -32,59 +30,59 @@ export class BaseCommonDB implements CommonDB {
32
30
  }
33
31
 
34
32
  async getTableSchema<ROW extends ObjectWithId>(
35
- table: string,
33
+ _table: string,
36
34
  ): Promise<JsonSchemaRootObject<ROW>> {
37
35
  throw new Error('getTableSchema is not implemented')
38
36
  }
39
37
 
40
38
  async createTable<ROW extends ObjectWithId>(
41
- table: string,
42
- schema: JsonSchemaObject<ROW>,
39
+ _table: string,
40
+ _schema: JsonSchemaObject<ROW>,
43
41
  ): Promise<void> {
44
42
  // no-op
45
43
  }
46
44
 
47
- async getByIds<ROW extends ObjectWithId>(table: string, ids: string[]): Promise<ROW[]> {
45
+ async getByIds<ROW extends ObjectWithId>(_table: string, _ids: string[]): Promise<ROW[]> {
48
46
  throw new Error('getByIds is not implemented')
49
47
  }
50
48
 
51
- async deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<number> {
49
+ async deleteByQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number> {
52
50
  throw new Error('deleteByQuery is not implemented')
53
51
  }
54
52
 
55
53
  async updateByQuery<ROW extends ObjectWithId>(
56
- q: DBQuery<ROW>,
57
- patch: DBPatch<ROW>,
58
- opt?: CommonDBOptions,
54
+ _q: DBQuery<ROW>,
55
+ _patch: DBPatch<ROW>,
56
+ _opt?: CommonDBOptions,
59
57
  ): Promise<number> {
60
58
  throw new Error('updateByQuery is not implemented')
61
59
  }
62
60
 
63
- async runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<RunQueryResult<ROW>> {
61
+ async runQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<RunQueryResult<ROW>> {
64
62
  throw new Error('runQuery is not implemented')
65
63
  }
66
64
 
67
- async runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<number> {
65
+ async runQueryCount<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number> {
68
66
  throw new Error('runQueryCount is not implemented')
69
67
  }
70
68
 
71
69
  async saveBatch<ROW extends ObjectWithId>(
72
- table: string,
73
- rows: ROW[],
74
- opt?: CommonDBSaveOptions<ROW>,
70
+ _table: string,
71
+ _rows: ROW[],
72
+ _opt?: CommonDBSaveOptions<ROW>,
75
73
  ): Promise<void> {
76
74
  throw new Error('saveBatch is not implemented')
77
75
  }
78
76
 
79
- streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): ReadableTyped<ROW> {
77
+ streamQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): ReadableTyped<ROW> {
80
78
  throw new Error('streamQuery is not implemented')
81
79
  }
82
80
 
83
- async deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number> {
81
+ async deleteByIds(_table: string, _ids: string[], _opt?: CommonDBOptions): Promise<number> {
84
82
  throw new Error('deleteByIds is not implemented')
85
83
  }
86
84
 
87
- async runInTransaction(fn: DBTransactionFn, opt?: CommonDBTransactionOptions): Promise<void> {
85
+ async runInTransaction(fn: DBTransactionFn, _opt?: CommonDBTransactionOptions): Promise<void> {
88
86
  const tx = new FakeDBTransaction(this)
89
87
  await fn(tx)
90
88
  // there's no try/catch and rollback, as there's nothing to rollback
@@ -295,6 +295,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
295
295
  }
296
296
 
297
297
  async runQueryExtended(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<RunQueryResult<BM>> {
298
+ this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
298
299
  q.table = opt.table || q.table
299
300
  const op = `runQuery(${q.pretty()})`
300
301
  const started = this.logStarted(op, q.table)
@@ -323,6 +324,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
323
324
  q: DBQuery<DBM>,
324
325
  opt: CommonDaoOptions = {},
325
326
  ): Promise<RunQueryResult<DBM>> {
327
+ this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
326
328
  q.table = opt.table || q.table
327
329
  const op = `runQueryAsDBM(${q.pretty()})`
328
330
  const started = this.logStarted(op, q.table)
@@ -340,6 +342,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
340
342
  }
341
343
 
342
344
  async runQueryCount(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
345
+ this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
343
346
  q.table = opt.table || q.table
344
347
  const op = `runQueryCount(${q.pretty()})`
345
348
  const started = this.logStarted(op, q.table)
@@ -355,6 +358,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
355
358
  mapper: AsyncMapper<BM, void>,
356
359
  opt: CommonDaoStreamForEachOptions<BM> = {},
357
360
  ): Promise<void> {
361
+ this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
358
362
  q.table = opt.table || q.table
359
363
  opt.skipValidation = opt.skipValidation !== false // default true
360
364
  opt.errorMode ||= ErrorMode.SUPPRESS
@@ -404,6 +408,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
404
408
  mapper: AsyncMapper<DBM, void>,
405
409
  opt: CommonDaoStreamForEachOptions<DBM> = {},
406
410
  ): Promise<void> {
411
+ this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
407
412
  q.table = opt.table || q.table
408
413
  opt.skipValidation = opt.skipValidation !== false // default true
409
414
  opt.errorMode ||= ErrorMode.SUPPRESS
@@ -452,6 +457,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
452
457
  * Stream as Readable, to be able to .pipe() it further with support of backpressure.
453
458
  */
454
459
  streamQueryAsDBM(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<DBM> = {}): ReadableTyped<DBM> {
460
+ this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
455
461
  q.table = opt.table || q.table
456
462
  opt.skipValidation = opt.skipValidation !== false // default true
457
463
  opt.errorMode ||= ErrorMode.SUPPRESS
@@ -490,6 +496,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
490
496
  * You can do `.pipe(transformNoOp)` to make it "valid again".
491
497
  */
492
498
  streamQuery(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<BM> = {}): ReadableTyped<BM> {
499
+ this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
493
500
  q.table = opt.table || q.table
494
501
  opt.skipValidation = opt.skipValidation !== false // default true
495
502
  opt.errorMode ||= ErrorMode.SUPPRESS
@@ -540,12 +547,14 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
540
547
  }
541
548
 
542
549
  async queryIds(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<string[]> {
550
+ this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
543
551
  q.table = opt.table || q.table
544
552
  const { rows } = await this.cfg.db.runQuery(q.select(['id']), opt)
545
553
  return rows.map(r => r.id)
546
554
  }
547
555
 
548
556
  streamQueryIds(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<string> = {}): ReadableTyped<string> {
557
+ this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
549
558
  q.table = opt.table || q.table
550
559
  opt.errorMode ||= ErrorMode.SUPPRESS
551
560
 
@@ -572,6 +581,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
572
581
  mapper: AsyncMapper<string, void>,
573
582
  opt: CommonDaoStreamForEachOptions<string> = {},
574
583
  ): Promise<void> {
584
+ this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
575
585
  q.table = opt.table || q.table
576
586
  opt.errorMode ||= ErrorMode.SUPPRESS
577
587
 
@@ -1015,6 +1025,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
1015
1025
  q: DBQuery<DBM>,
1016
1026
  opt: CommonDaoStreamDeleteOptions<DBM> = {},
1017
1027
  ): Promise<number> {
1028
+ this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
1018
1029
  this.requireWriteAccess()
1019
1030
  this.requireObjectMutability(opt)
1020
1031
  q.table = opt.table || q.table
@@ -1073,6 +1084,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
1073
1084
  patch: DBPatch<DBM>,
1074
1085
  opt: CommonDaoOptions = {},
1075
1086
  ): Promise<number> {
1087
+ this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
1076
1088
  this.requireWriteAccess()
1077
1089
  this.requireObjectMutability(opt)
1078
1090
  q.table = opt.table || q.table
@@ -1259,6 +1271,24 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
1259
1271
  return r!
1260
1272
  }
1261
1273
 
1274
+ /**
1275
+ * Throws if query uses a property that is in `excludeFromIndexes` list.
1276
+ */
1277
+ private validateQueryIndexes(q: DBQuery<DBM>): void {
1278
+ const { excludeFromIndexes } = this.cfg
1279
+ if (!excludeFromIndexes) return
1280
+
1281
+ for (const f of q._filters) {
1282
+ _assert(
1283
+ !excludeFromIndexes.includes(f.name),
1284
+ `cannot query on non-indexed property: ${this.cfg.table}.${f.name as string}`,
1285
+ {
1286
+ query: q.pretty(),
1287
+ },
1288
+ )
1289
+ }
1290
+ }
1291
+
1262
1292
  protected logResult(started: number, op: string, res: any, table: string): void {
1263
1293
  if (!this.cfg.logLevel) return
1264
1294
 
@@ -14,7 +14,6 @@ import {
14
14
  testItemBMSchema,
15
15
  } from './test.model'
16
16
 
17
- // eslint-disable-next-line jest/no-export
18
17
  export function runCommonDaoTest(db: CommonDB, quirks: CommonDBImplementationQuirks = {}): void {
19
18
  const { support } = db
20
19
  const dao = new CommonDao({
@@ -1,4 +1,4 @@
1
- import { _filterObject, _pick, _sortBy, pMap } from '@naturalcycles/js-lib'
1
+ import { _deepFreeze, _filterObject, _pick, _sortBy, pMap } from '@naturalcycles/js-lib'
2
2
  import { CommonDB, CommonDBType } from '../common.db'
3
3
  import { DBIncrement, DBPatch } from '../db.model'
4
4
  import { DBQuery } from '../query/dbQuery'
@@ -9,9 +9,6 @@ import {
9
9
  testItemBMJsonSchema,
10
10
  TestItemDBM,
11
11
  } from './test.model'
12
- import { deepFreeze } from './test.util'
13
-
14
- /* eslint-disable jest/no-export */
15
12
 
16
13
  /**
17
14
  * All options default to `false`.
@@ -31,7 +28,7 @@ export interface CommonDBImplementationQuirks {
31
28
  export function runCommonDBTest(db: CommonDB, quirks: CommonDBImplementationQuirks = {}): void {
32
29
  const { support } = db
33
30
  const items = createTestItemsDBM(3)
34
- deepFreeze(items)
31
+ _deepFreeze(items)
35
32
  const item1 = items[0]!
36
33
 
37
34
  const queryAll = (): DBQuery<TestItemDBM> => DBQuery.create<TestItemDBM>(TEST_TABLE)
@@ -88,7 +85,7 @@ export function runCommonDBTest(db: CommonDB, quirks: CommonDBImplementationQuir
88
85
  ...createTestItemDBM(3),
89
86
  k2: null,
90
87
  }
91
- deepFreeze(item3)
88
+ _deepFreeze(item3)
92
89
  await db.saveBatch(TEST_TABLE, [item3])
93
90
  const item3Loaded = (await db.getByIds<TestItemDBM>(TEST_TABLE, [item3.id]))[0]!
94
91
  expectMatch([item3], [item3Loaded], quirks)
@@ -102,7 +99,7 @@ export function runCommonDBTest(db: CommonDB, quirks: CommonDBImplementationQuir
102
99
  ...createTestItemDBM(3),
103
100
  k2: undefined,
104
101
  }
105
- deepFreeze(item3)
102
+ _deepFreeze(item3)
106
103
  const expected = { ...item3 }
107
104
  delete expected.k2
108
105
 
@@ -2,8 +2,6 @@ import { _range, _sortBy } from '@naturalcycles/js-lib'
2
2
  import { CommonKeyValueDB, KeyValueDBTuple } from '../kv/commonKeyValueDB'
3
3
  import { TEST_TABLE } from './test.model'
4
4
 
5
- /* eslint-disable jest/no-export */
6
-
7
5
  const testIds = _range(1, 4).map(n => `id${n}`)
8
6
 
9
7
  const testEntries: KeyValueDBTuple[] = testIds.map(id => [id, Buffer.from(`${id}value`)])
@@ -2,8 +2,6 @@ import { _range, _sortBy } from '@naturalcycles/js-lib'
2
2
  import { CommonKeyValueDao } from '../kv/commonKeyValueDao'
3
3
  import { KeyValueDBTuple } from '../kv/commonKeyValueDB'
4
4
 
5
- /* eslint-disable jest/no-export */
6
-
7
5
  const testIds = _range(1, 4).map(n => `id${n}`)
8
6
  const testEntries: KeyValueDBTuple[] = testIds.map(id => [id, Buffer.from(`${id}value`)])
9
7
 
@@ -1,6 +0,0 @@
1
- /**
2
- * Does Object.freeze recursively for given object.
3
- *
4
- * Based on: https://github.com/substack/deep-freeze/blob/master/index.js
5
- */
6
- export declare function deepFreeze(o: any): void;
@@ -1,23 +0,0 @@
1
- "use strict";
2
- // copy-pasted from test-lib to be able to not include devDependecies there
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.deepFreeze = deepFreeze;
5
- /**
6
- * Does Object.freeze recursively for given object.
7
- *
8
- * Based on: https://github.com/substack/deep-freeze/blob/master/index.js
9
- */
10
- function deepFreeze(o) {
11
- Object.freeze(o);
12
- Object.getOwnPropertyNames(o).forEach(prop => {
13
- if (
14
- // eslint-disable-next-line no-prototype-builtins
15
- o.hasOwnProperty(prop) &&
16
- o[prop] !== null &&
17
- (typeof o[prop] === 'object' || typeof o[prop] === 'function') &&
18
- !Object.isFrozen(o[prop])) {
19
- deepFreeze(o[prop]);
20
- }
21
- });
22
- return o;
23
- }
@@ -1,24 +0,0 @@
1
- // copy-pasted from test-lib to be able to not include devDependecies there
2
-
3
- /**
4
- * Does Object.freeze recursively for given object.
5
- *
6
- * Based on: https://github.com/substack/deep-freeze/blob/master/index.js
7
- */
8
- export function deepFreeze(o: any): void {
9
- Object.freeze(o)
10
-
11
- Object.getOwnPropertyNames(o).forEach(prop => {
12
- if (
13
- // eslint-disable-next-line no-prototype-builtins
14
- o.hasOwnProperty(prop) &&
15
- o[prop] !== null &&
16
- (typeof o[prop] === 'object' || typeof o[prop] === 'function') &&
17
- !Object.isFrozen(o[prop])
18
- ) {
19
- deepFreeze(o[prop])
20
- }
21
- })
22
-
23
- return o
24
- }