@sprucelabs/data-stores 28.0.26 → 28.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/databases/MongoDatabase.d.ts +4 -3
- package/build/databases/MongoDatabase.js +28 -8
- package/build/databases/NeDbDatabase.d.ts +6 -6
- package/build/databases/NeDbDatabase.js +18 -9
- package/build/databases/normalizeIndex.d.ts +5 -0
- package/build/databases/normalizeIndex.js +8 -0
- package/build/esm/databases/MongoDatabase.d.ts +4 -3
- package/build/esm/databases/MongoDatabase.js +29 -9
- package/build/esm/databases/NeDbDatabase.d.ts +6 -6
- package/build/esm/databases/NeDbDatabase.js +18 -9
- package/build/esm/databases/normalizeIndex.d.ts +5 -0
- package/build/esm/databases/normalizeIndex.js +5 -0
- package/build/esm/tests/databaseAssertUtil.d.ts +2 -1
- package/build/esm/tests/databaseAssertUtil.js +88 -5
- package/build/esm/tests/pluckAssertionMethods.d.ts +1 -1
- package/build/esm/types/database.types.d.ts +5 -1
- package/build/tests/databaseAssertUtil.d.ts +2 -1
- package/build/tests/databaseAssertUtil.js +86 -5
- package/build/tests/pluckAssertionMethods.d.ts +1 -1
- package/build/types/database.types.d.ts +5 -1
- package/package.json +10 -10
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MongoClientOptions, MongoClient } from 'mongodb';
|
|
2
|
-
import { Database, DatabaseOptions } from '../types/database.types';
|
|
2
|
+
import { Database, DatabaseOptions, IndexWithFilter, UniqueIndex } from '../types/database.types';
|
|
3
3
|
import { QueryOptions } from '../types/query.types';
|
|
4
4
|
export declare const MONGO_TEST_URI = "mongodb://localhost:27017";
|
|
5
5
|
export default class MongoDatabase implements Database {
|
|
@@ -33,8 +33,9 @@ export default class MongoDatabase implements Database {
|
|
|
33
33
|
private assertIndexDoesNotExist;
|
|
34
34
|
private doesIndexExist;
|
|
35
35
|
syncIndexes(collectionName: string, indexes: string[][]): Promise<void>;
|
|
36
|
-
createUniqueIndex(collection: string,
|
|
37
|
-
|
|
36
|
+
createUniqueIndex(collection: string, index: string[] | IndexWithFilter): Promise<void>;
|
|
37
|
+
private normalizeIndex;
|
|
38
|
+
syncUniqueIndexes(collectionName: string, indexes: UniqueIndex[]): Promise<void>;
|
|
38
39
|
update(collection: string, query: Record<string, any>, updates: Record<string, any>): Promise<number>;
|
|
39
40
|
updateOne(collection: string, query: Record<string, any>, updates: Record<string, any>): Promise<Record<string, any>>;
|
|
40
41
|
upsertOne(collection: string, query: Record<string, any>, updates: Record<string, any>): Promise<Record<string, any>>;
|
|
@@ -22,6 +22,7 @@ const mongodb_1 = require("mongodb");
|
|
|
22
22
|
const SpruceError_1 = __importDefault(require("../errors/SpruceError"));
|
|
23
23
|
const generateId_1 = __importDefault(require("../utilities/generateId"));
|
|
24
24
|
const mongo_utility_1 = __importDefault(require("../utilities/mongo.utility"));
|
|
25
|
+
const normalizeIndex_1 = __importDefault(require("./normalizeIndex"));
|
|
25
26
|
exports.MONGO_TEST_URI = 'mongodb://localhost:27017';
|
|
26
27
|
class MongoDatabase {
|
|
27
28
|
constructor(url, options) {
|
|
@@ -321,7 +322,8 @@ class MongoDatabase {
|
|
|
321
322
|
}
|
|
322
323
|
doesIndexExist(currentIndexes, fields) {
|
|
323
324
|
for (const index of currentIndexes !== null && currentIndexes !== void 0 ? currentIndexes : []) {
|
|
324
|
-
|
|
325
|
+
const { fields: normalizedFields } = this.normalizeIndex(index);
|
|
326
|
+
if ((0, isEqual_1.default)(normalizedFields, fields)) {
|
|
325
327
|
return true;
|
|
326
328
|
}
|
|
327
329
|
}
|
|
@@ -339,17 +341,22 @@ class MongoDatabase {
|
|
|
339
341
|
await this.dropIndex(collectionName, extra);
|
|
340
342
|
}
|
|
341
343
|
}
|
|
342
|
-
async createUniqueIndex(collection,
|
|
344
|
+
async createUniqueIndex(collection, index) {
|
|
343
345
|
const currentIndexes = await this.getUniqueIndexes(collection);
|
|
344
|
-
|
|
345
|
-
|
|
346
|
+
const { fields, filter } = this.normalizeIndex(index);
|
|
347
|
+
this.assertIndexDoesNotExist(currentIndexes, fields, collection);
|
|
348
|
+
const created = {};
|
|
346
349
|
fields.forEach((name) => {
|
|
347
|
-
|
|
350
|
+
created[name] = 1;
|
|
348
351
|
});
|
|
349
352
|
try {
|
|
353
|
+
const options = { unique: true };
|
|
354
|
+
if (filter) {
|
|
355
|
+
options.partialFilterExpression = filter;
|
|
356
|
+
}
|
|
350
357
|
await this.assertDbWhileAttempingTo('create a unique index.', collection)
|
|
351
358
|
.collection(collection)
|
|
352
|
-
.createIndex(
|
|
359
|
+
.createIndex(created, options);
|
|
353
360
|
}
|
|
354
361
|
catch (err) {
|
|
355
362
|
if ((err === null || err === void 0 ? void 0 : err.code) === 11000) {
|
|
@@ -363,12 +370,25 @@ class MongoDatabase {
|
|
|
363
370
|
}
|
|
364
371
|
}
|
|
365
372
|
}
|
|
373
|
+
normalizeIndex(index) {
|
|
374
|
+
const { fields, filter } = (0, normalizeIndex_1.default)(index);
|
|
375
|
+
return { fields, filter };
|
|
376
|
+
}
|
|
366
377
|
async syncUniqueIndexes(collectionName, indexes) {
|
|
378
|
+
var _a;
|
|
367
379
|
const currentIndexes = await this.getUniqueIndexes(collectionName);
|
|
368
380
|
const extraIndexes = (0, differenceWith_1.default)(currentIndexes, indexes, isEqual_1.default);
|
|
369
381
|
for (const index of indexes) {
|
|
370
|
-
|
|
371
|
-
|
|
382
|
+
const { fields } = this.normalizeIndex(index);
|
|
383
|
+
if (!this.doesIndexExist(currentIndexes, fields)) {
|
|
384
|
+
try {
|
|
385
|
+
await this.createUniqueIndex(collectionName, index);
|
|
386
|
+
}
|
|
387
|
+
catch (err) {
|
|
388
|
+
if (((_a = err.options) === null || _a === void 0 ? void 0 : _a.code) !== 'INDEX_EXISTS') {
|
|
389
|
+
throw err;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
372
392
|
}
|
|
373
393
|
}
|
|
374
394
|
for (const extra of extraIndexes) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import AbstractMutexer from '../mutexers/AbstractMutexer';
|
|
2
|
-
import { CreateOptions, Database, DatabaseInternalOptions } from '../types/database.types';
|
|
2
|
+
import { CreateOptions, Database, DatabaseInternalOptions, UniqueIndex } from '../types/database.types';
|
|
3
3
|
import { QueryOptions } from '../types/query.types';
|
|
4
4
|
export default class NeDbDatabase extends AbstractMutexer implements Database {
|
|
5
5
|
private collections;
|
|
@@ -31,14 +31,14 @@ export default class NeDbDatabase extends AbstractMutexer implements Database {
|
|
|
31
31
|
delete(collection: string, query: Record<string, any>): Promise<number>;
|
|
32
32
|
deleteOne(collection: string, query: Record<string, any>): Promise<number>;
|
|
33
33
|
private assertPassesUniqueIndexes;
|
|
34
|
-
getUniqueIndexes(collection: string): Promise<
|
|
35
|
-
getIndexes(collection: string, shouldIncludeUnique?: boolean): Promise<
|
|
36
|
-
dropIndex(collection: string,
|
|
34
|
+
getUniqueIndexes(collection: string): Promise<UniqueIndex[]>;
|
|
35
|
+
getIndexes(collection: string, shouldIncludeUnique?: boolean): Promise<UniqueIndex[]>;
|
|
36
|
+
dropIndex(collection: string, index: UniqueIndex): Promise<void>;
|
|
37
37
|
private assertIndexDoesNotExist;
|
|
38
38
|
private doesIndexExist;
|
|
39
|
-
createUniqueIndex(collection: string,
|
|
39
|
+
createUniqueIndex(collection: string, index: UniqueIndex): Promise<void>;
|
|
40
40
|
createIndex(collection: string, fields: string[]): Promise<void>;
|
|
41
|
-
syncUniqueIndexes(collectionName: string, indexes:
|
|
41
|
+
syncUniqueIndexes(collectionName: string, indexes: UniqueIndex[]): Promise<void>;
|
|
42
42
|
syncIndexes(collectionName: string, indexes: string[][]): Promise<void>;
|
|
43
43
|
query<T>(query: string, params?: any[]): Promise<T[]>;
|
|
44
44
|
private queryToKey;
|
|
@@ -26,6 +26,7 @@ const SpruceError_1 = __importDefault(require("../errors/SpruceError"));
|
|
|
26
26
|
const AbstractMutexer_1 = __importDefault(require("../mutexers/AbstractMutexer"));
|
|
27
27
|
const generateId_1 = __importDefault(require("../utilities/generateId"));
|
|
28
28
|
const mongo_utility_1 = __importDefault(require("../utilities/mongo.utility"));
|
|
29
|
+
const normalizeIndex_1 = __importDefault(require("./normalizeIndex"));
|
|
29
30
|
dotenv_1.default.config();
|
|
30
31
|
const NULL_PLACEHOLDER = '_____NULL_____';
|
|
31
32
|
const UNDEFINED_PLACEHOLDER = '_____UNDEFINED_____';
|
|
@@ -292,7 +293,11 @@ class NeDbDatabase extends AbstractMutexer_1.default {
|
|
|
292
293
|
const col = this.loadCollection(collection);
|
|
293
294
|
await this.randomDelay();
|
|
294
295
|
if (col._uniqueIndexes) {
|
|
295
|
-
for (const
|
|
296
|
+
for (const index of col._uniqueIndexes) {
|
|
297
|
+
const { fields, filter } = (0, normalizeIndex_1.default)(index);
|
|
298
|
+
if (filter) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
296
301
|
const existing = query
|
|
297
302
|
? await this.findOne(collection, query)
|
|
298
303
|
: null;
|
|
@@ -333,9 +338,10 @@ class NeDbDatabase extends AbstractMutexer_1.default {
|
|
|
333
338
|
}
|
|
334
339
|
return (_c = col._indexes) !== null && _c !== void 0 ? _c : [];
|
|
335
340
|
}
|
|
336
|
-
async dropIndex(collection,
|
|
341
|
+
async dropIndex(collection, index) {
|
|
337
342
|
var _a, _b;
|
|
338
343
|
const col = this.loadCollection(collection);
|
|
344
|
+
const { fields } = (0, normalizeIndex_1.default)(index);
|
|
339
345
|
await this.randomDelay();
|
|
340
346
|
let found = false;
|
|
341
347
|
let newIndexes = [];
|
|
@@ -389,18 +395,20 @@ class NeDbDatabase extends AbstractMutexer_1.default {
|
|
|
389
395
|
}
|
|
390
396
|
return false;
|
|
391
397
|
}
|
|
392
|
-
async createUniqueIndex(collection,
|
|
398
|
+
async createUniqueIndex(collection, index) {
|
|
393
399
|
const col = this.loadCollection(collection);
|
|
394
400
|
if (!col._uniqueIndexes) {
|
|
395
401
|
col._uniqueIndexes = [];
|
|
396
402
|
}
|
|
403
|
+
const { fields, filter } = (0, normalizeIndex_1.default)(index);
|
|
397
404
|
await this.randomDelay();
|
|
398
405
|
this.assertIndexDoesNotExist(col._uniqueIndexes, fields, collection);
|
|
399
|
-
if (col._uniqueIndexes) {
|
|
406
|
+
if (col._uniqueIndexes && !filter) {
|
|
400
407
|
const tempUniqueIndexes = [...col._uniqueIndexes];
|
|
401
408
|
tempUniqueIndexes.push(fields);
|
|
402
|
-
const documents = (await this.find(collection
|
|
403
|
-
for (const
|
|
409
|
+
const documents = (await this.find(collection)) || [];
|
|
410
|
+
for (const index of tempUniqueIndexes) {
|
|
411
|
+
const { fields: uniqueFields } = (0, normalizeIndex_1.default)(index);
|
|
404
412
|
let parsedExisting = [];
|
|
405
413
|
for (const doc of documents) {
|
|
406
414
|
const tempDoc = {};
|
|
@@ -413,12 +421,12 @@ class NeDbDatabase extends AbstractMutexer_1.default {
|
|
|
413
421
|
if (parsedExisting.length != uniqued.length) {
|
|
414
422
|
throw new SpruceError_1.default({
|
|
415
423
|
code: 'DUPLICATE_KEY',
|
|
416
|
-
friendlyMessage: `Could not create index! Unique index on '${collection}' has duplicate key for "${
|
|
424
|
+
friendlyMessage: `Could not create index! Unique index on '${collection}' has duplicate key for "${uniqueFields.join(',')}"`,
|
|
417
425
|
});
|
|
418
426
|
}
|
|
419
427
|
}
|
|
420
428
|
}
|
|
421
|
-
col._uniqueIndexes.push(
|
|
429
|
+
col._uniqueIndexes.push(index);
|
|
422
430
|
}
|
|
423
431
|
async createIndex(collection, fields) {
|
|
424
432
|
const col = this.loadCollection(collection);
|
|
@@ -434,7 +442,8 @@ class NeDbDatabase extends AbstractMutexer_1.default {
|
|
|
434
442
|
const currentIndexes = await this.getUniqueIndexes(collectionName);
|
|
435
443
|
const extraIndexes = (0, differenceWith_1.default)(currentIndexes, indexes, isEqual_1.default);
|
|
436
444
|
for (const index of indexes) {
|
|
437
|
-
|
|
445
|
+
const { fields } = (0, normalizeIndex_1.default)(index);
|
|
446
|
+
if (!this.doesIndexExist(currentIndexes, fields)) {
|
|
438
447
|
try {
|
|
439
448
|
await this.createUniqueIndex(collectionName, index);
|
|
440
449
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
function normalizeIndex(index) {
|
|
4
|
+
const fields = Array.isArray(index) ? index : index.fields;
|
|
5
|
+
const filter = Array.isArray(index) ? undefined : index.filter;
|
|
6
|
+
return { fields, filter };
|
|
7
|
+
}
|
|
8
|
+
exports.default = normalizeIndex;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MongoClientOptions, MongoClient } from 'mongodb';
|
|
2
|
-
import { Database, DatabaseOptions } from '../types/database.types';
|
|
2
|
+
import { Database, DatabaseOptions, IndexWithFilter, UniqueIndex } from '../types/database.types';
|
|
3
3
|
import { QueryOptions } from '../types/query.types';
|
|
4
4
|
export declare const MONGO_TEST_URI = "mongodb://localhost:27017";
|
|
5
5
|
export default class MongoDatabase implements Database {
|
|
@@ -33,8 +33,9 @@ export default class MongoDatabase implements Database {
|
|
|
33
33
|
private assertIndexDoesNotExist;
|
|
34
34
|
private doesIndexExist;
|
|
35
35
|
syncIndexes(collectionName: string, indexes: string[][]): Promise<void>;
|
|
36
|
-
createUniqueIndex(collection: string,
|
|
37
|
-
|
|
36
|
+
createUniqueIndex(collection: string, index: string[] | IndexWithFilter): Promise<void>;
|
|
37
|
+
private normalizeIndex;
|
|
38
|
+
syncUniqueIndexes(collectionName: string, indexes: UniqueIndex[]): Promise<void>;
|
|
38
39
|
update(collection: string, query: Record<string, any>, updates: Record<string, any>): Promise<number>;
|
|
39
40
|
updateOne(collection: string, query: Record<string, any>, updates: Record<string, any>): Promise<Record<string, any>>;
|
|
40
41
|
upsertOne(collection: string, query: Record<string, any>, updates: Record<string, any>): Promise<Record<string, any>>;
|
|
@@ -21,10 +21,11 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
21
21
|
import { buildLog } from '@sprucelabs/spruce-skill-utils';
|
|
22
22
|
import differenceWith from 'lodash/differenceWith.js';
|
|
23
23
|
import isEqual from 'lodash/isEqual.js';
|
|
24
|
-
import { MongoClient, MongoError } from 'mongodb';
|
|
24
|
+
import { MongoClient, MongoError, } from 'mongodb';
|
|
25
25
|
import SpruceError from '../errors/SpruceError.js';
|
|
26
26
|
import generateId from '../utilities/generateId.js';
|
|
27
27
|
import mongoUtil from '../utilities/mongo.utility.js';
|
|
28
|
+
import normalizeIndex from './normalizeIndex.js';
|
|
28
29
|
export const MONGO_TEST_URI = 'mongodb://localhost:27017';
|
|
29
30
|
export default class MongoDatabase {
|
|
30
31
|
constructor(url, options) {
|
|
@@ -354,7 +355,8 @@ export default class MongoDatabase {
|
|
|
354
355
|
}
|
|
355
356
|
doesIndexExist(currentIndexes, fields) {
|
|
356
357
|
for (const index of currentIndexes !== null && currentIndexes !== void 0 ? currentIndexes : []) {
|
|
357
|
-
|
|
358
|
+
const { fields: normalizedFields } = this.normalizeIndex(index);
|
|
359
|
+
if (isEqual(normalizedFields, fields)) {
|
|
358
360
|
return true;
|
|
359
361
|
}
|
|
360
362
|
}
|
|
@@ -374,18 +376,23 @@ export default class MongoDatabase {
|
|
|
374
376
|
}
|
|
375
377
|
});
|
|
376
378
|
}
|
|
377
|
-
createUniqueIndex(collection,
|
|
379
|
+
createUniqueIndex(collection, index) {
|
|
378
380
|
return __awaiter(this, void 0, void 0, function* () {
|
|
379
381
|
const currentIndexes = yield this.getUniqueIndexes(collection);
|
|
380
|
-
|
|
381
|
-
|
|
382
|
+
const { fields, filter } = this.normalizeIndex(index);
|
|
383
|
+
this.assertIndexDoesNotExist(currentIndexes, fields, collection);
|
|
384
|
+
const created = {};
|
|
382
385
|
fields.forEach((name) => {
|
|
383
|
-
|
|
386
|
+
created[name] = 1;
|
|
384
387
|
});
|
|
385
388
|
try {
|
|
389
|
+
const options = { unique: true };
|
|
390
|
+
if (filter) {
|
|
391
|
+
options.partialFilterExpression = filter;
|
|
392
|
+
}
|
|
386
393
|
yield this.assertDbWhileAttempingTo('create a unique index.', collection)
|
|
387
394
|
.collection(collection)
|
|
388
|
-
.createIndex(
|
|
395
|
+
.createIndex(created, options);
|
|
389
396
|
}
|
|
390
397
|
catch (err) {
|
|
391
398
|
if ((err === null || err === void 0 ? void 0 : err.code) === 11000) {
|
|
@@ -400,13 +407,26 @@ export default class MongoDatabase {
|
|
|
400
407
|
}
|
|
401
408
|
});
|
|
402
409
|
}
|
|
410
|
+
normalizeIndex(index) {
|
|
411
|
+
const { fields, filter } = normalizeIndex(index);
|
|
412
|
+
return { fields, filter };
|
|
413
|
+
}
|
|
403
414
|
syncUniqueIndexes(collectionName, indexes) {
|
|
404
415
|
return __awaiter(this, void 0, void 0, function* () {
|
|
416
|
+
var _a;
|
|
405
417
|
const currentIndexes = yield this.getUniqueIndexes(collectionName);
|
|
406
418
|
const extraIndexes = differenceWith(currentIndexes, indexes, isEqual);
|
|
407
419
|
for (const index of indexes) {
|
|
408
|
-
|
|
409
|
-
|
|
420
|
+
const { fields } = this.normalizeIndex(index);
|
|
421
|
+
if (!this.doesIndexExist(currentIndexes, fields)) {
|
|
422
|
+
try {
|
|
423
|
+
yield this.createUniqueIndex(collectionName, index);
|
|
424
|
+
}
|
|
425
|
+
catch (err) {
|
|
426
|
+
if (((_a = err.options) === null || _a === void 0 ? void 0 : _a.code) !== 'INDEX_EXISTS') {
|
|
427
|
+
throw err;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
410
430
|
}
|
|
411
431
|
}
|
|
412
432
|
for (const extra of extraIndexes) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import AbstractMutexer from '../mutexers/AbstractMutexer';
|
|
2
|
-
import { CreateOptions, Database, DatabaseInternalOptions } from '../types/database.types';
|
|
2
|
+
import { CreateOptions, Database, DatabaseInternalOptions, UniqueIndex } from '../types/database.types';
|
|
3
3
|
import { QueryOptions } from '../types/query.types';
|
|
4
4
|
export default class NeDbDatabase extends AbstractMutexer implements Database {
|
|
5
5
|
private collections;
|
|
@@ -31,14 +31,14 @@ export default class NeDbDatabase extends AbstractMutexer implements Database {
|
|
|
31
31
|
delete(collection: string, query: Record<string, any>): Promise<number>;
|
|
32
32
|
deleteOne(collection: string, query: Record<string, any>): Promise<number>;
|
|
33
33
|
private assertPassesUniqueIndexes;
|
|
34
|
-
getUniqueIndexes(collection: string): Promise<
|
|
35
|
-
getIndexes(collection: string, shouldIncludeUnique?: boolean): Promise<
|
|
36
|
-
dropIndex(collection: string,
|
|
34
|
+
getUniqueIndexes(collection: string): Promise<UniqueIndex[]>;
|
|
35
|
+
getIndexes(collection: string, shouldIncludeUnique?: boolean): Promise<UniqueIndex[]>;
|
|
36
|
+
dropIndex(collection: string, index: UniqueIndex): Promise<void>;
|
|
37
37
|
private assertIndexDoesNotExist;
|
|
38
38
|
private doesIndexExist;
|
|
39
|
-
createUniqueIndex(collection: string,
|
|
39
|
+
createUniqueIndex(collection: string, index: UniqueIndex): Promise<void>;
|
|
40
40
|
createIndex(collection: string, fields: string[]): Promise<void>;
|
|
41
|
-
syncUniqueIndexes(collectionName: string, indexes:
|
|
41
|
+
syncUniqueIndexes(collectionName: string, indexes: UniqueIndex[]): Promise<void>;
|
|
42
42
|
syncIndexes(collectionName: string, indexes: string[][]): Promise<void>;
|
|
43
43
|
query<T>(query: string, params?: any[]): Promise<T[]>;
|
|
44
44
|
private queryToKey;
|
|
@@ -30,6 +30,7 @@ import SpruceError from '../errors/SpruceError.js';
|
|
|
30
30
|
import AbstractMutexer from '../mutexers/AbstractMutexer.js';
|
|
31
31
|
import generateId from '../utilities/generateId.js';
|
|
32
32
|
import mongoUtil from '../utilities/mongo.utility.js';
|
|
33
|
+
import normalizeIndex from './normalizeIndex.js';
|
|
33
34
|
dotenv.config();
|
|
34
35
|
const NULL_PLACEHOLDER = '_____NULL_____';
|
|
35
36
|
const UNDEFINED_PLACEHOLDER = '_____UNDEFINED_____';
|
|
@@ -327,7 +328,11 @@ export default class NeDbDatabase extends AbstractMutexer {
|
|
|
327
328
|
const col = this.loadCollection(collection);
|
|
328
329
|
yield this.randomDelay();
|
|
329
330
|
if (col._uniqueIndexes) {
|
|
330
|
-
for (const
|
|
331
|
+
for (const index of col._uniqueIndexes) {
|
|
332
|
+
const { fields, filter } = normalizeIndex(index);
|
|
333
|
+
if (filter) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
331
336
|
const existing = query
|
|
332
337
|
? yield this.findOne(collection, query)
|
|
333
338
|
: null;
|
|
@@ -373,10 +378,11 @@ export default class NeDbDatabase extends AbstractMutexer {
|
|
|
373
378
|
return (_c = col._indexes) !== null && _c !== void 0 ? _c : [];
|
|
374
379
|
});
|
|
375
380
|
}
|
|
376
|
-
dropIndex(collection,
|
|
381
|
+
dropIndex(collection, index) {
|
|
377
382
|
return __awaiter(this, void 0, void 0, function* () {
|
|
378
383
|
var _a, _b;
|
|
379
384
|
const col = this.loadCollection(collection);
|
|
385
|
+
const { fields } = normalizeIndex(index);
|
|
380
386
|
yield this.randomDelay();
|
|
381
387
|
let found = false;
|
|
382
388
|
let newIndexes = [];
|
|
@@ -431,19 +437,21 @@ export default class NeDbDatabase extends AbstractMutexer {
|
|
|
431
437
|
}
|
|
432
438
|
return false;
|
|
433
439
|
}
|
|
434
|
-
createUniqueIndex(collection,
|
|
440
|
+
createUniqueIndex(collection, index) {
|
|
435
441
|
return __awaiter(this, void 0, void 0, function* () {
|
|
436
442
|
const col = this.loadCollection(collection);
|
|
437
443
|
if (!col._uniqueIndexes) {
|
|
438
444
|
col._uniqueIndexes = [];
|
|
439
445
|
}
|
|
446
|
+
const { fields, filter } = normalizeIndex(index);
|
|
440
447
|
yield this.randomDelay();
|
|
441
448
|
this.assertIndexDoesNotExist(col._uniqueIndexes, fields, collection);
|
|
442
|
-
if (col._uniqueIndexes) {
|
|
449
|
+
if (col._uniqueIndexes && !filter) {
|
|
443
450
|
const tempUniqueIndexes = [...col._uniqueIndexes];
|
|
444
451
|
tempUniqueIndexes.push(fields);
|
|
445
|
-
const documents = (yield this.find(collection
|
|
446
|
-
for (const
|
|
452
|
+
const documents = (yield this.find(collection)) || [];
|
|
453
|
+
for (const index of tempUniqueIndexes) {
|
|
454
|
+
const { fields: uniqueFields } = normalizeIndex(index);
|
|
447
455
|
let parsedExisting = [];
|
|
448
456
|
for (const doc of documents) {
|
|
449
457
|
const tempDoc = {};
|
|
@@ -456,12 +464,12 @@ export default class NeDbDatabase extends AbstractMutexer {
|
|
|
456
464
|
if (parsedExisting.length != uniqued.length) {
|
|
457
465
|
throw new SpruceError({
|
|
458
466
|
code: 'DUPLICATE_KEY',
|
|
459
|
-
friendlyMessage: `Could not create index! Unique index on '${collection}' has duplicate key for "${
|
|
467
|
+
friendlyMessage: `Could not create index! Unique index on '${collection}' has duplicate key for "${uniqueFields.join(',')}"`,
|
|
460
468
|
});
|
|
461
469
|
}
|
|
462
470
|
}
|
|
463
471
|
}
|
|
464
|
-
col._uniqueIndexes.push(
|
|
472
|
+
col._uniqueIndexes.push(index);
|
|
465
473
|
});
|
|
466
474
|
}
|
|
467
475
|
createIndex(collection, fields) {
|
|
@@ -481,7 +489,8 @@ export default class NeDbDatabase extends AbstractMutexer {
|
|
|
481
489
|
const currentIndexes = yield this.getUniqueIndexes(collectionName);
|
|
482
490
|
const extraIndexes = differenceWith(currentIndexes, indexes, isEqual);
|
|
483
491
|
for (const index of indexes) {
|
|
484
|
-
|
|
492
|
+
const { fields } = normalizeIndex(index);
|
|
493
|
+
if (!this.doesIndexExist(currentIndexes, fields)) {
|
|
485
494
|
try {
|
|
486
495
|
yield this.createUniqueIndex(collectionName, index);
|
|
487
496
|
}
|
|
@@ -3,7 +3,7 @@ import { DataStore } from '../types/stores.types';
|
|
|
3
3
|
declare const databaseAssertUtil: {
|
|
4
4
|
collectionName: string;
|
|
5
5
|
runSuite(connect: TestConnect, tests?: string[]): Promise<void>;
|
|
6
|
-
_getFilteredIndexes(db: Database): Promise<
|
|
6
|
+
_getFilteredIndexes(db: Database): Promise<string[][]>;
|
|
7
7
|
_filterIdIndex(allIndexes: UniqueIndex[] | Index[]): UniqueIndex[] | Index[];
|
|
8
8
|
_assertUpdateUpdatedRightNumberOfRecords(db: Database, search: Record<string, any>, updates: Record<string, any>, expectedUpdateCount: number): Promise<void>;
|
|
9
9
|
generateIdDifferentEachTime(connect: TestConnect): Promise<void>;
|
|
@@ -63,6 +63,7 @@ declare const databaseAssertUtil: {
|
|
|
63
63
|
assertCanUpsertNull(connect: TestConnect): Promise<void>;
|
|
64
64
|
assertCanSaveAndGetNullAndUndefined(connect: TestConnect): Promise<void>;
|
|
65
65
|
assertSyncIndexesDoesNotRemoveExisting(connect: TestConnect): Promise<void>;
|
|
66
|
+
assertCanSyncUniqueIndexesWithFilterExpression(connect: TestConnect): Promise<void>;
|
|
66
67
|
assertSyncIndexesRemovesExtraIndexes(connect: TestConnect): Promise<void>;
|
|
67
68
|
assertSyncIndexesSkipsExisting(connect: TestConnect): Promise<void>;
|
|
68
69
|
assertCantDropCompoundIndexThatDoesNotExist(connect: TestConnect): Promise<void>;
|
|
@@ -49,6 +49,7 @@ const databaseAssertUtil = {
|
|
|
49
49
|
'assertCanUpsertOne',
|
|
50
50
|
'assertCanUpsertNull',
|
|
51
51
|
'assertCanPushToArrayOnUpsert',
|
|
52
|
+
'assertCanSyncUniqueIndexesWithFilterExpression',
|
|
52
53
|
//finding
|
|
53
54
|
'assertEmptyDatabaseReturnsEmptyArray',
|
|
54
55
|
'assertFindOneOnEmptyDatabaseReturnsNull',
|
|
@@ -122,6 +123,7 @@ const databaseAssertUtil = {
|
|
|
122
123
|
});
|
|
123
124
|
},
|
|
124
125
|
_filterIdIndex(allIndexes) {
|
|
126
|
+
//@ts-ignore
|
|
125
127
|
return allIndexes.filter((i) => i[0] !== '_id');
|
|
126
128
|
},
|
|
127
129
|
_assertUpdateUpdatedRightNumberOfRecords(db, search, updates, expectedUpdateCount) {
|
|
@@ -243,19 +245,19 @@ const databaseAssertUtil = {
|
|
|
243
245
|
return __awaiter(this, void 0, void 0, function* () {
|
|
244
246
|
const db = yield connectToDabatase(connect);
|
|
245
247
|
yield db.createUniqueIndex(this.collectionName, ['uniqueField']);
|
|
246
|
-
let indexes = yield db.getUniqueIndexes(this.collectionName);
|
|
248
|
+
let indexes = (yield db.getUniqueIndexes(this.collectionName));
|
|
247
249
|
assert.isLength(indexes, 1, 'getUniqueIndexes() did not return the unique index I tried to create!');
|
|
248
250
|
assert.isLength(indexes[0], 1, 'getUniqueIndexes() needs to return an array of arrays. Each item in the array should be an array of fields that make up the unique index.');
|
|
249
251
|
assert.isEqual(indexes[0][0].toLowerCase(), 'uniqueField'.toLowerCase(), 'getUniqueIndexes() did not add the expected field to the first unique index.');
|
|
250
252
|
yield db.createUniqueIndex(this.collectionName, ['uniqueField2']);
|
|
251
|
-
indexes = yield db.getUniqueIndexes(this.collectionName);
|
|
253
|
+
indexes = (yield db.getUniqueIndexes(this.collectionName));
|
|
252
254
|
assert.isLength(indexes, 2);
|
|
253
255
|
assert.isEqual(indexes[1][0].toLowerCase(), 'uniqueField2'.toLowerCase());
|
|
254
256
|
yield db.createUniqueIndex(this.collectionName, [
|
|
255
257
|
'uniqueField3',
|
|
256
258
|
'uniqueField4',
|
|
257
259
|
]);
|
|
258
|
-
indexes = yield db.getUniqueIndexes(this.collectionName);
|
|
260
|
+
indexes = (yield db.getUniqueIndexes(this.collectionName));
|
|
259
261
|
assert.isLength(indexes, 3);
|
|
260
262
|
assert.isEqual(indexes[2][0].toLowerCase(), 'uniqueField3'.toLowerCase());
|
|
261
263
|
assert.isEqual(indexes[2][1].toLowerCase(), 'uniqueField4'.toLowerCase());
|
|
@@ -745,10 +747,10 @@ const databaseAssertUtil = {
|
|
|
745
747
|
['someField'],
|
|
746
748
|
['otherField', 'otherField2'],
|
|
747
749
|
]);
|
|
748
|
-
let indexes = yield db.getUniqueIndexes(this.collectionName);
|
|
750
|
+
let indexes = (yield db.getUniqueIndexes(this.collectionName));
|
|
749
751
|
assert.isLength(indexes, 3, 'syncUniqueIndexes() should have created 3 indexes.');
|
|
750
752
|
yield db.syncUniqueIndexes(this.collectionName, [['uniqueField']]);
|
|
751
|
-
indexes = yield db.getUniqueIndexes(this.collectionName);
|
|
753
|
+
indexes = (yield db.getUniqueIndexes(this.collectionName));
|
|
752
754
|
assert.isLength(indexes, 1);
|
|
753
755
|
assert.isEqual(indexes[0][0].toLowerCase(), 'uniquefield');
|
|
754
756
|
yield this.shutdown(db);
|
|
@@ -1446,6 +1448,87 @@ const databaseAssertUtil = {
|
|
|
1446
1448
|
yield this.shutdown(db);
|
|
1447
1449
|
});
|
|
1448
1450
|
},
|
|
1451
|
+
assertCanSyncUniqueIndexesWithFilterExpression(connect) {
|
|
1452
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1453
|
+
var _a, _b, _c, _d;
|
|
1454
|
+
const db = yield connectToDabatase(connect);
|
|
1455
|
+
try {
|
|
1456
|
+
yield db.syncUniqueIndexes(this.collectionName, [
|
|
1457
|
+
{
|
|
1458
|
+
fields: ['username', 'dateScrambled'],
|
|
1459
|
+
filter: {
|
|
1460
|
+
username: { $exists: true },
|
|
1461
|
+
},
|
|
1462
|
+
},
|
|
1463
|
+
]);
|
|
1464
|
+
}
|
|
1465
|
+
catch (err) {
|
|
1466
|
+
assert.fail((_a = `syncUniqueIndexes() should not have thrown an error when syncing a unique index with a filter expression.\n\n` +
|
|
1467
|
+
err.stack) !== null && _a !== void 0 ? _a : err.message);
|
|
1468
|
+
}
|
|
1469
|
+
try {
|
|
1470
|
+
yield db.createOne(this.collectionName, {
|
|
1471
|
+
username: 'test',
|
|
1472
|
+
phone: null,
|
|
1473
|
+
dateScrambled: 'test',
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
catch (err) {
|
|
1477
|
+
assert.fail((_b = `Creating a record should not have thrown an error after syncing a unique index with a filter expression.\n\n` +
|
|
1478
|
+
err.stack) !== null && _b !== void 0 ? _b : err.message);
|
|
1479
|
+
}
|
|
1480
|
+
yield assert.doesThrowAsync(() => db.createOne(this.collectionName, {
|
|
1481
|
+
username: 'test',
|
|
1482
|
+
phone: null,
|
|
1483
|
+
dateScrambled: 'test',
|
|
1484
|
+
}));
|
|
1485
|
+
yield db.createOne(this.collectionName, {
|
|
1486
|
+
phone: '555-000-0000',
|
|
1487
|
+
dateScrambled: 'test',
|
|
1488
|
+
});
|
|
1489
|
+
yield db.createOne(this.collectionName, {
|
|
1490
|
+
phone: '555-000-0001',
|
|
1491
|
+
dateScrambled: 'test',
|
|
1492
|
+
});
|
|
1493
|
+
try {
|
|
1494
|
+
yield db.syncUniqueIndexes(this.collectionName, [
|
|
1495
|
+
{
|
|
1496
|
+
fields: ['username', 'dateScrambled'],
|
|
1497
|
+
filter: {
|
|
1498
|
+
username: { $exists: true },
|
|
1499
|
+
},
|
|
1500
|
+
},
|
|
1501
|
+
{
|
|
1502
|
+
fields: ['phone', 'dateScrambled'],
|
|
1503
|
+
filter: {
|
|
1504
|
+
phone: { $exists: true, $type: 'string' },
|
|
1505
|
+
},
|
|
1506
|
+
},
|
|
1507
|
+
]);
|
|
1508
|
+
}
|
|
1509
|
+
catch (err) {
|
|
1510
|
+
assert.fail((_c = `syncUniqueIndexes() should not have thrown an error when syncing multiple unique indexes with filter expressions.\n\n` +
|
|
1511
|
+
err.stack) !== null && _c !== void 0 ? _c : err.message);
|
|
1512
|
+
}
|
|
1513
|
+
yield db.createOne(this.collectionName, {
|
|
1514
|
+
username: 'test',
|
|
1515
|
+
phone: null,
|
|
1516
|
+
dateScrambled: 'next',
|
|
1517
|
+
});
|
|
1518
|
+
try {
|
|
1519
|
+
yield db.createOne(this.collectionName, {
|
|
1520
|
+
username: 'test2',
|
|
1521
|
+
phone: null,
|
|
1522
|
+
dateScrambled: 'next',
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
catch (err) {
|
|
1526
|
+
assert.fail((_d = `createOne() should not throw if index has filter { \$exists: true, \$type: 'string' }.\n\n` +
|
|
1527
|
+
err.stack) !== null && _d !== void 0 ? _d : err.message);
|
|
1528
|
+
}
|
|
1529
|
+
yield this.shutdown(db);
|
|
1530
|
+
});
|
|
1531
|
+
},
|
|
1449
1532
|
assertSyncIndexesRemovesExtraIndexes(connect) {
|
|
1450
1533
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1451
1534
|
const db = yield connectToDabatase(connect);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import databaseAssertUtil from './databaseAssertUtil';
|
|
2
|
-
export default function pluckAssertionMethods(util: typeof databaseAssertUtil): ("collectionName" | "assertThrowsWithInvalidConnectionString" | "assertThrowsWhenCantConnect" | "assertThrowsWithBadDatabaseName" | "assertEmptyDatabaseReturnsEmptyArray" | "assertKnowsIfConnectionClosed" | "assertFindOneOnEmptyDatabaseReturnsNull" | "assertCanSortDesc" | "assertCanSortAsc" | "assertCanSortById" | "assertCanQueryWithOr" | "generateIdDifferentEachTime" | "assertInsertingGeneratesId" | "assertCanCreateMany" | "assertCanLimitResults" | "assertCanCreateWithObjectField" | "assertCanCountOnId" | "assertCanCount" | "assertThrowsWhenUpdatingRecordNotFound" | "assertCanUpdate" | "assertCanUpdateMany" | "assertCanPushOntoArrayValue" | "assertCanUpdateWithObjectField" | "assertCanUpdateFieldInObjectFieldWithTargettedWhere" | "assertCanSaveAndGetNullAndUndefined" | "assertCanUpsertOne" | "assertCanUpsertNull" | "assertCanPushToArrayOnUpsert" | "assertCanLimitResultsToZero" | "assertCanFindWithBooleanField" | "assertCanQueryByGtLtGteLteNe" | "assertCanQueryPathWithDotSyntax" | "assertCanReturnOnlySelectFields" | "assertCanSearchByRegex" | "assertCanFindWithNe" | "assertCanFindWithIn" | "assertCanDeleteRecord" | "assertCanDeleteOne" | "assertHasNoUniqueIndexToStart" | "assertCanCreateUniqueIndex" | "assertCanCreateMultiFieldUniqueIndex" | "assertCantCreateUniqueIndexTwice" | "assertCanDropUniqueIndex" | "assertCanDropCompoundUniqueIndex" | "assertCantDropUniqueIndexThatDoesntExist" | "assertCantDropIndexWhenNoIndexExists" | "assertCantDropCompoundUniqueIndexThatDoesntExist" | "assertSyncingUniqueIndexsAddsMissingIndexes" | "assertSyncingUniqueIndexsSkipsExistingIndexs" | "assertSyncingUniqueIndexesRemovesExtraIndexes" | "assertSyncingUniqueIndexesIsRaceProof" | "assertSyncingIndexesDoesNotAddAndRemove" | "assertUniqueIndexBlocksDuplicates" | "assertDuplicateKeyThrowsOnInsert" | "assertSettingUniqueIndexViolationThrowsSpruceError" | "assertCanCreateUniqueIndexOnNestedField" | "assertUpsertWithUniqueIndex" | "assertNestedFieldIndexUpdates" | "assertHasNoIndexToStart" | "assertCanCreateIndex" | "assertCantCreateSameIndexTwice" | "assertCanCreateMultiFieldIndex" | "assertCanDropIndex" | "assertCanDropCompoundIndex" | "assertCantDropCompoundIndexThatDoesNotExist" | "assertSyncIndexesSkipsExisting" | "assertSyncIndexesRemovesExtraIndexes" | "assertSyncIndexesHandlesRaceConditions" | "assertSyncIndexesDoesNotRemoveExisting" | "assertDuplicateFieldsWithMultipleUniqueIndexesWorkAsExpected" | "runSuite" | "_getFilteredIndexes" | "_filterIdIndex" | "_assertUpdateUpdatedRightNumberOfRecords" | "shutdown" | "_assertThrowsExpectedNotFoundOnUpdateOne" | "_assert$orReturnsExpectedTotalRecords" | "_assertCanCreateMultiFieldIndex" | "assertHasLowerCaseToCamelCaseMappingEnabled")[];
|
|
2
|
+
export default function pluckAssertionMethods(util: typeof databaseAssertUtil): ("collectionName" | "assertThrowsWithInvalidConnectionString" | "assertThrowsWhenCantConnect" | "assertThrowsWithBadDatabaseName" | "assertEmptyDatabaseReturnsEmptyArray" | "assertKnowsIfConnectionClosed" | "assertFindOneOnEmptyDatabaseReturnsNull" | "assertCanSortDesc" | "assertCanSortAsc" | "assertCanSortById" | "assertCanQueryWithOr" | "generateIdDifferentEachTime" | "assertInsertingGeneratesId" | "assertCanCreateMany" | "assertCanLimitResults" | "assertCanCreateWithObjectField" | "assertCanCountOnId" | "assertCanCount" | "assertThrowsWhenUpdatingRecordNotFound" | "assertCanUpdate" | "assertCanUpdateMany" | "assertCanPushOntoArrayValue" | "assertCanUpdateWithObjectField" | "assertCanUpdateFieldInObjectFieldWithTargettedWhere" | "assertCanSaveAndGetNullAndUndefined" | "assertCanUpsertOne" | "assertCanUpsertNull" | "assertCanPushToArrayOnUpsert" | "assertCanSyncUniqueIndexesWithFilterExpression" | "assertCanLimitResultsToZero" | "assertCanFindWithBooleanField" | "assertCanQueryByGtLtGteLteNe" | "assertCanQueryPathWithDotSyntax" | "assertCanReturnOnlySelectFields" | "assertCanSearchByRegex" | "assertCanFindWithNe" | "assertCanFindWithIn" | "assertCanDeleteRecord" | "assertCanDeleteOne" | "assertHasNoUniqueIndexToStart" | "assertCanCreateUniqueIndex" | "assertCanCreateMultiFieldUniqueIndex" | "assertCantCreateUniqueIndexTwice" | "assertCanDropUniqueIndex" | "assertCanDropCompoundUniqueIndex" | "assertCantDropUniqueIndexThatDoesntExist" | "assertCantDropIndexWhenNoIndexExists" | "assertCantDropCompoundUniqueIndexThatDoesntExist" | "assertSyncingUniqueIndexsAddsMissingIndexes" | "assertSyncingUniqueIndexsSkipsExistingIndexs" | "assertSyncingUniqueIndexesRemovesExtraIndexes" | "assertSyncingUniqueIndexesIsRaceProof" | "assertSyncingIndexesDoesNotAddAndRemove" | "assertUniqueIndexBlocksDuplicates" | "assertDuplicateKeyThrowsOnInsert" | "assertSettingUniqueIndexViolationThrowsSpruceError" | "assertCanCreateUniqueIndexOnNestedField" | "assertUpsertWithUniqueIndex" | "assertNestedFieldIndexUpdates" | "assertHasNoIndexToStart" | "assertCanCreateIndex" | "assertCantCreateSameIndexTwice" | "assertCanCreateMultiFieldIndex" | "assertCanDropIndex" | "assertCanDropCompoundIndex" | "assertCantDropCompoundIndexThatDoesNotExist" | "assertSyncIndexesSkipsExisting" | "assertSyncIndexesRemovesExtraIndexes" | "assertSyncIndexesHandlesRaceConditions" | "assertSyncIndexesDoesNotRemoveExisting" | "assertDuplicateFieldsWithMultipleUniqueIndexesWorkAsExpected" | "runSuite" | "_getFilteredIndexes" | "_filterIdIndex" | "_assertUpdateUpdatedRightNumberOfRecords" | "shutdown" | "_assertThrowsExpectedNotFoundOnUpdateOne" | "_assert$orReturnsExpectedTotalRecords" | "_assertCanCreateMultiFieldIndex" | "assertHasLowerCaseToCamelCaseMappingEnabled")[];
|
|
@@ -38,7 +38,11 @@ export type TestConnect = (connectionString?: string, dbName?: string) => Promis
|
|
|
38
38
|
connectionStringWithRandomBadDatabaseName: string;
|
|
39
39
|
badDatabaseName: string;
|
|
40
40
|
}>;
|
|
41
|
-
export
|
|
41
|
+
export interface IndexWithFilter {
|
|
42
|
+
fields: string[];
|
|
43
|
+
filter: Record<string, any>;
|
|
44
|
+
}
|
|
45
|
+
export type UniqueIndex = string[] | IndexWithFilter;
|
|
42
46
|
export type Index = string[];
|
|
43
47
|
export interface CreateOptions extends DatabaseInternalOptions {
|
|
44
48
|
}
|
|
@@ -3,7 +3,7 @@ import { DataStore } from '../types/stores.types';
|
|
|
3
3
|
declare const databaseAssertUtil: {
|
|
4
4
|
collectionName: string;
|
|
5
5
|
runSuite(connect: TestConnect, tests?: string[]): Promise<void>;
|
|
6
|
-
_getFilteredIndexes(db: Database): Promise<
|
|
6
|
+
_getFilteredIndexes(db: Database): Promise<string[][]>;
|
|
7
7
|
_filterIdIndex(allIndexes: UniqueIndex[] | Index[]): UniqueIndex[] | Index[];
|
|
8
8
|
_assertUpdateUpdatedRightNumberOfRecords(db: Database, search: Record<string, any>, updates: Record<string, any>, expectedUpdateCount: number): Promise<void>;
|
|
9
9
|
generateIdDifferentEachTime(connect: TestConnect): Promise<void>;
|
|
@@ -63,6 +63,7 @@ declare const databaseAssertUtil: {
|
|
|
63
63
|
assertCanUpsertNull(connect: TestConnect): Promise<void>;
|
|
64
64
|
assertCanSaveAndGetNullAndUndefined(connect: TestConnect): Promise<void>;
|
|
65
65
|
assertSyncIndexesDoesNotRemoveExisting(connect: TestConnect): Promise<void>;
|
|
66
|
+
assertCanSyncUniqueIndexesWithFilterExpression(connect: TestConnect): Promise<void>;
|
|
66
67
|
assertSyncIndexesRemovesExtraIndexes(connect: TestConnect): Promise<void>;
|
|
67
68
|
assertSyncIndexesSkipsExisting(connect: TestConnect): Promise<void>;
|
|
68
69
|
assertCantDropCompoundIndexThatDoesNotExist(connect: TestConnect): Promise<void>;
|
|
@@ -44,6 +44,7 @@ const databaseAssertUtil = {
|
|
|
44
44
|
'assertCanUpsertOne',
|
|
45
45
|
'assertCanUpsertNull',
|
|
46
46
|
'assertCanPushToArrayOnUpsert',
|
|
47
|
+
'assertCanSyncUniqueIndexesWithFilterExpression',
|
|
47
48
|
//finding
|
|
48
49
|
'assertEmptyDatabaseReturnsEmptyArray',
|
|
49
50
|
'assertFindOneOnEmptyDatabaseReturnsNull',
|
|
@@ -114,6 +115,7 @@ const databaseAssertUtil = {
|
|
|
114
115
|
return this._filterIdIndex(await db.getIndexes(this.collectionName));
|
|
115
116
|
},
|
|
116
117
|
_filterIdIndex(allIndexes) {
|
|
118
|
+
//@ts-ignore
|
|
117
119
|
return allIndexes.filter((i) => i[0] !== '_id');
|
|
118
120
|
},
|
|
119
121
|
async _assertUpdateUpdatedRightNumberOfRecords(db, search, updates, expectedUpdateCount) {
|
|
@@ -220,19 +222,19 @@ const databaseAssertUtil = {
|
|
|
220
222
|
async assertCanCreateUniqueIndex(connect) {
|
|
221
223
|
const db = await connectToDabatase(connect);
|
|
222
224
|
await db.createUniqueIndex(this.collectionName, ['uniqueField']);
|
|
223
|
-
let indexes = await db.getUniqueIndexes(this.collectionName);
|
|
225
|
+
let indexes = (await db.getUniqueIndexes(this.collectionName));
|
|
224
226
|
test_utils_1.assert.isLength(indexes, 1, 'getUniqueIndexes() did not return the unique index I tried to create!');
|
|
225
227
|
test_utils_1.assert.isLength(indexes[0], 1, 'getUniqueIndexes() needs to return an array of arrays. Each item in the array should be an array of fields that make up the unique index.');
|
|
226
228
|
test_utils_1.assert.isEqual(indexes[0][0].toLowerCase(), 'uniqueField'.toLowerCase(), 'getUniqueIndexes() did not add the expected field to the first unique index.');
|
|
227
229
|
await db.createUniqueIndex(this.collectionName, ['uniqueField2']);
|
|
228
|
-
indexes = await db.getUniqueIndexes(this.collectionName);
|
|
230
|
+
indexes = (await db.getUniqueIndexes(this.collectionName));
|
|
229
231
|
test_utils_1.assert.isLength(indexes, 2);
|
|
230
232
|
test_utils_1.assert.isEqual(indexes[1][0].toLowerCase(), 'uniqueField2'.toLowerCase());
|
|
231
233
|
await db.createUniqueIndex(this.collectionName, [
|
|
232
234
|
'uniqueField3',
|
|
233
235
|
'uniqueField4',
|
|
234
236
|
]);
|
|
235
|
-
indexes = await db.getUniqueIndexes(this.collectionName);
|
|
237
|
+
indexes = (await db.getUniqueIndexes(this.collectionName));
|
|
236
238
|
test_utils_1.assert.isLength(indexes, 3);
|
|
237
239
|
test_utils_1.assert.isEqual(indexes[2][0].toLowerCase(), 'uniqueField3'.toLowerCase());
|
|
238
240
|
test_utils_1.assert.isEqual(indexes[2][1].toLowerCase(), 'uniqueField4'.toLowerCase());
|
|
@@ -678,10 +680,10 @@ const databaseAssertUtil = {
|
|
|
678
680
|
['someField'],
|
|
679
681
|
['otherField', 'otherField2'],
|
|
680
682
|
]);
|
|
681
|
-
let indexes = await db.getUniqueIndexes(this.collectionName);
|
|
683
|
+
let indexes = (await db.getUniqueIndexes(this.collectionName));
|
|
682
684
|
test_utils_1.assert.isLength(indexes, 3, 'syncUniqueIndexes() should have created 3 indexes.');
|
|
683
685
|
await db.syncUniqueIndexes(this.collectionName, [['uniqueField']]);
|
|
684
|
-
indexes = await db.getUniqueIndexes(this.collectionName);
|
|
686
|
+
indexes = (await db.getUniqueIndexes(this.collectionName));
|
|
685
687
|
test_utils_1.assert.isLength(indexes, 1);
|
|
686
688
|
test_utils_1.assert.isEqual(indexes[0][0].toLowerCase(), 'uniquefield');
|
|
687
689
|
await this.shutdown(db);
|
|
@@ -1322,6 +1324,85 @@ const databaseAssertUtil = {
|
|
|
1322
1324
|
]);
|
|
1323
1325
|
await this.shutdown(db);
|
|
1324
1326
|
},
|
|
1327
|
+
async assertCanSyncUniqueIndexesWithFilterExpression(connect) {
|
|
1328
|
+
var _a, _b, _c, _d;
|
|
1329
|
+
const db = await connectToDabatase(connect);
|
|
1330
|
+
try {
|
|
1331
|
+
await db.syncUniqueIndexes(this.collectionName, [
|
|
1332
|
+
{
|
|
1333
|
+
fields: ['username', 'dateScrambled'],
|
|
1334
|
+
filter: {
|
|
1335
|
+
username: { $exists: true },
|
|
1336
|
+
},
|
|
1337
|
+
},
|
|
1338
|
+
]);
|
|
1339
|
+
}
|
|
1340
|
+
catch (err) {
|
|
1341
|
+
test_utils_1.assert.fail((_a = `syncUniqueIndexes() should not have thrown an error when syncing a unique index with a filter expression.\n\n` +
|
|
1342
|
+
err.stack) !== null && _a !== void 0 ? _a : err.message);
|
|
1343
|
+
}
|
|
1344
|
+
try {
|
|
1345
|
+
await db.createOne(this.collectionName, {
|
|
1346
|
+
username: 'test',
|
|
1347
|
+
phone: null,
|
|
1348
|
+
dateScrambled: 'test',
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
catch (err) {
|
|
1352
|
+
test_utils_1.assert.fail((_b = `Creating a record should not have thrown an error after syncing a unique index with a filter expression.\n\n` +
|
|
1353
|
+
err.stack) !== null && _b !== void 0 ? _b : err.message);
|
|
1354
|
+
}
|
|
1355
|
+
await test_utils_1.assert.doesThrowAsync(() => db.createOne(this.collectionName, {
|
|
1356
|
+
username: 'test',
|
|
1357
|
+
phone: null,
|
|
1358
|
+
dateScrambled: 'test',
|
|
1359
|
+
}));
|
|
1360
|
+
await db.createOne(this.collectionName, {
|
|
1361
|
+
phone: '555-000-0000',
|
|
1362
|
+
dateScrambled: 'test',
|
|
1363
|
+
});
|
|
1364
|
+
await db.createOne(this.collectionName, {
|
|
1365
|
+
phone: '555-000-0001',
|
|
1366
|
+
dateScrambled: 'test',
|
|
1367
|
+
});
|
|
1368
|
+
try {
|
|
1369
|
+
await db.syncUniqueIndexes(this.collectionName, [
|
|
1370
|
+
{
|
|
1371
|
+
fields: ['username', 'dateScrambled'],
|
|
1372
|
+
filter: {
|
|
1373
|
+
username: { $exists: true },
|
|
1374
|
+
},
|
|
1375
|
+
},
|
|
1376
|
+
{
|
|
1377
|
+
fields: ['phone', 'dateScrambled'],
|
|
1378
|
+
filter: {
|
|
1379
|
+
phone: { $exists: true, $type: 'string' },
|
|
1380
|
+
},
|
|
1381
|
+
},
|
|
1382
|
+
]);
|
|
1383
|
+
}
|
|
1384
|
+
catch (err) {
|
|
1385
|
+
test_utils_1.assert.fail((_c = `syncUniqueIndexes() should not have thrown an error when syncing multiple unique indexes with filter expressions.\n\n` +
|
|
1386
|
+
err.stack) !== null && _c !== void 0 ? _c : err.message);
|
|
1387
|
+
}
|
|
1388
|
+
await db.createOne(this.collectionName, {
|
|
1389
|
+
username: 'test',
|
|
1390
|
+
phone: null,
|
|
1391
|
+
dateScrambled: 'next',
|
|
1392
|
+
});
|
|
1393
|
+
try {
|
|
1394
|
+
await db.createOne(this.collectionName, {
|
|
1395
|
+
username: 'test2',
|
|
1396
|
+
phone: null,
|
|
1397
|
+
dateScrambled: 'next',
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
catch (err) {
|
|
1401
|
+
test_utils_1.assert.fail((_d = `createOne() should not throw if index has filter { \$exists: true, \$type: 'string' }.\n\n` +
|
|
1402
|
+
err.stack) !== null && _d !== void 0 ? _d : err.message);
|
|
1403
|
+
}
|
|
1404
|
+
await this.shutdown(db);
|
|
1405
|
+
},
|
|
1325
1406
|
async assertSyncIndexesRemovesExtraIndexes(connect) {
|
|
1326
1407
|
const db = await connectToDabatase(connect);
|
|
1327
1408
|
await db.syncIndexes(this.collectionName, [
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import databaseAssertUtil from './databaseAssertUtil';
|
|
2
|
-
export default function pluckAssertionMethods(util: typeof databaseAssertUtil): ("collectionName" | "assertThrowsWithInvalidConnectionString" | "assertThrowsWhenCantConnect" | "assertThrowsWithBadDatabaseName" | "assertEmptyDatabaseReturnsEmptyArray" | "assertKnowsIfConnectionClosed" | "assertFindOneOnEmptyDatabaseReturnsNull" | "assertCanSortDesc" | "assertCanSortAsc" | "assertCanSortById" | "assertCanQueryWithOr" | "generateIdDifferentEachTime" | "assertInsertingGeneratesId" | "assertCanCreateMany" | "assertCanLimitResults" | "assertCanCreateWithObjectField" | "assertCanCountOnId" | "assertCanCount" | "assertThrowsWhenUpdatingRecordNotFound" | "assertCanUpdate" | "assertCanUpdateMany" | "assertCanPushOntoArrayValue" | "assertCanUpdateWithObjectField" | "assertCanUpdateFieldInObjectFieldWithTargettedWhere" | "assertCanSaveAndGetNullAndUndefined" | "assertCanUpsertOne" | "assertCanUpsertNull" | "assertCanPushToArrayOnUpsert" | "assertCanLimitResultsToZero" | "assertCanFindWithBooleanField" | "assertCanQueryByGtLtGteLteNe" | "assertCanQueryPathWithDotSyntax" | "assertCanReturnOnlySelectFields" | "assertCanSearchByRegex" | "assertCanFindWithNe" | "assertCanFindWithIn" | "assertCanDeleteRecord" | "assertCanDeleteOne" | "assertHasNoUniqueIndexToStart" | "assertCanCreateUniqueIndex" | "assertCanCreateMultiFieldUniqueIndex" | "assertCantCreateUniqueIndexTwice" | "assertCanDropUniqueIndex" | "assertCanDropCompoundUniqueIndex" | "assertCantDropUniqueIndexThatDoesntExist" | "assertCantDropIndexWhenNoIndexExists" | "assertCantDropCompoundUniqueIndexThatDoesntExist" | "assertSyncingUniqueIndexsAddsMissingIndexes" | "assertSyncingUniqueIndexsSkipsExistingIndexs" | "assertSyncingUniqueIndexesRemovesExtraIndexes" | "assertSyncingUniqueIndexesIsRaceProof" | "assertSyncingIndexesDoesNotAddAndRemove" | "assertUniqueIndexBlocksDuplicates" | "assertDuplicateKeyThrowsOnInsert" | "assertSettingUniqueIndexViolationThrowsSpruceError" | "assertCanCreateUniqueIndexOnNestedField" | "assertUpsertWithUniqueIndex" | "assertNestedFieldIndexUpdates" | "assertHasNoIndexToStart" | "assertCanCreateIndex" | "assertCantCreateSameIndexTwice" | "assertCanCreateMultiFieldIndex" | "assertCanDropIndex" | "assertCanDropCompoundIndex" | "assertCantDropCompoundIndexThatDoesNotExist" | "assertSyncIndexesSkipsExisting" | "assertSyncIndexesRemovesExtraIndexes" | "assertSyncIndexesHandlesRaceConditions" | "assertSyncIndexesDoesNotRemoveExisting" | "assertDuplicateFieldsWithMultipleUniqueIndexesWorkAsExpected" | "runSuite" | "_getFilteredIndexes" | "_filterIdIndex" | "_assertUpdateUpdatedRightNumberOfRecords" | "shutdown" | "_assertThrowsExpectedNotFoundOnUpdateOne" | "_assert$orReturnsExpectedTotalRecords" | "_assertCanCreateMultiFieldIndex" | "assertHasLowerCaseToCamelCaseMappingEnabled")[];
|
|
2
|
+
export default function pluckAssertionMethods(util: typeof databaseAssertUtil): ("collectionName" | "assertThrowsWithInvalidConnectionString" | "assertThrowsWhenCantConnect" | "assertThrowsWithBadDatabaseName" | "assertEmptyDatabaseReturnsEmptyArray" | "assertKnowsIfConnectionClosed" | "assertFindOneOnEmptyDatabaseReturnsNull" | "assertCanSortDesc" | "assertCanSortAsc" | "assertCanSortById" | "assertCanQueryWithOr" | "generateIdDifferentEachTime" | "assertInsertingGeneratesId" | "assertCanCreateMany" | "assertCanLimitResults" | "assertCanCreateWithObjectField" | "assertCanCountOnId" | "assertCanCount" | "assertThrowsWhenUpdatingRecordNotFound" | "assertCanUpdate" | "assertCanUpdateMany" | "assertCanPushOntoArrayValue" | "assertCanUpdateWithObjectField" | "assertCanUpdateFieldInObjectFieldWithTargettedWhere" | "assertCanSaveAndGetNullAndUndefined" | "assertCanUpsertOne" | "assertCanUpsertNull" | "assertCanPushToArrayOnUpsert" | "assertCanSyncUniqueIndexesWithFilterExpression" | "assertCanLimitResultsToZero" | "assertCanFindWithBooleanField" | "assertCanQueryByGtLtGteLteNe" | "assertCanQueryPathWithDotSyntax" | "assertCanReturnOnlySelectFields" | "assertCanSearchByRegex" | "assertCanFindWithNe" | "assertCanFindWithIn" | "assertCanDeleteRecord" | "assertCanDeleteOne" | "assertHasNoUniqueIndexToStart" | "assertCanCreateUniqueIndex" | "assertCanCreateMultiFieldUniqueIndex" | "assertCantCreateUniqueIndexTwice" | "assertCanDropUniqueIndex" | "assertCanDropCompoundUniqueIndex" | "assertCantDropUniqueIndexThatDoesntExist" | "assertCantDropIndexWhenNoIndexExists" | "assertCantDropCompoundUniqueIndexThatDoesntExist" | "assertSyncingUniqueIndexsAddsMissingIndexes" | "assertSyncingUniqueIndexsSkipsExistingIndexs" | "assertSyncingUniqueIndexesRemovesExtraIndexes" | "assertSyncingUniqueIndexesIsRaceProof" | "assertSyncingIndexesDoesNotAddAndRemove" | "assertUniqueIndexBlocksDuplicates" | "assertDuplicateKeyThrowsOnInsert" | "assertSettingUniqueIndexViolationThrowsSpruceError" | "assertCanCreateUniqueIndexOnNestedField" | "assertUpsertWithUniqueIndex" | "assertNestedFieldIndexUpdates" | "assertHasNoIndexToStart" | "assertCanCreateIndex" | "assertCantCreateSameIndexTwice" | "assertCanCreateMultiFieldIndex" | "assertCanDropIndex" | "assertCanDropCompoundIndex" | "assertCantDropCompoundIndexThatDoesNotExist" | "assertSyncIndexesSkipsExisting" | "assertSyncIndexesRemovesExtraIndexes" | "assertSyncIndexesHandlesRaceConditions" | "assertSyncIndexesDoesNotRemoveExisting" | "assertDuplicateFieldsWithMultipleUniqueIndexesWorkAsExpected" | "runSuite" | "_getFilteredIndexes" | "_filterIdIndex" | "_assertUpdateUpdatedRightNumberOfRecords" | "shutdown" | "_assertThrowsExpectedNotFoundOnUpdateOne" | "_assert$orReturnsExpectedTotalRecords" | "_assertCanCreateMultiFieldIndex" | "assertHasLowerCaseToCamelCaseMappingEnabled")[];
|
|
@@ -38,7 +38,11 @@ export type TestConnect = (connectionString?: string, dbName?: string) => Promis
|
|
|
38
38
|
connectionStringWithRandomBadDatabaseName: string;
|
|
39
39
|
badDatabaseName: string;
|
|
40
40
|
}>;
|
|
41
|
-
export
|
|
41
|
+
export interface IndexWithFilter {
|
|
42
|
+
fields: string[];
|
|
43
|
+
filter: Record<string, any>;
|
|
44
|
+
}
|
|
45
|
+
export type UniqueIndex = string[] | IndexWithFilter;
|
|
42
46
|
export type Index = string[];
|
|
43
47
|
export interface CreateOptions extends DatabaseInternalOptions {
|
|
44
48
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "28.0
|
|
6
|
+
"version": "28.1.0",
|
|
7
7
|
"files": [
|
|
8
8
|
"build/**/*",
|
|
9
9
|
"!build/__tests__",
|
|
@@ -68,24 +68,24 @@
|
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
70
|
"@sprucelabs/error": "^6.0.34",
|
|
71
|
-
"@sprucelabs/globby": "^2.0.
|
|
72
|
-
"@sprucelabs/schema": "^30.0.
|
|
73
|
-
"@sprucelabs/spruce-skill-utils": "^31.0.
|
|
71
|
+
"@sprucelabs/globby": "^2.0.16",
|
|
72
|
+
"@sprucelabs/schema": "^30.0.62",
|
|
73
|
+
"@sprucelabs/spruce-skill-utils": "^31.0.67",
|
|
74
74
|
"just-clone": "^6.2.0",
|
|
75
75
|
"lodash": "^4.17.21",
|
|
76
|
-
"mongodb": "^6.
|
|
76
|
+
"mongodb": "^6.7.0",
|
|
77
77
|
"nedb": "^1.8.0"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"@sprucelabs/esm-postbuild": "^6.0.
|
|
81
|
-
"@sprucelabs/jest-json-reporter": "^8.0.
|
|
82
|
-
"@sprucelabs/resolve-path-aliases": "^2.0.
|
|
80
|
+
"@sprucelabs/esm-postbuild": "^6.0.27",
|
|
81
|
+
"@sprucelabs/jest-json-reporter": "^8.0.38",
|
|
82
|
+
"@sprucelabs/resolve-path-aliases": "^2.0.28",
|
|
83
83
|
"@sprucelabs/semantic-release": "^5.0.1",
|
|
84
84
|
"@sprucelabs/test": "^9.0.18",
|
|
85
|
-
"@sprucelabs/test-utils": "^5.0.
|
|
85
|
+
"@sprucelabs/test-utils": "^5.0.51",
|
|
86
86
|
"@types/lodash": "^4.17.4",
|
|
87
87
|
"@types/nedb": "^1.8.16",
|
|
88
|
-
"@types/node": "^20.12.
|
|
88
|
+
"@types/node": "^20.12.13",
|
|
89
89
|
"chokidar-cli": "^3.0.0",
|
|
90
90
|
"concurrently": "^8.2.2",
|
|
91
91
|
"dotenv": "^16.4.5",
|