@theshelf/database 0.3.2 → 0.4.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/README.md +20 -31
- package/dist/Database.d.ts +3 -2
- package/dist/Database.js +108 -22
- package/dist/definitions/interfaces.d.ts +1 -0
- package/dist/drivers/Memory.d.ts +1 -0
- package/dist/drivers/Memory.js +1 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.js +1 -2
- package/package.json +9 -3
- package/dist/drivers/MongoDB.d.ts +0 -21
- package/dist/drivers/MongoDB.js +0 -187
package/README.md
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
# Database | The Shelf
|
|
2
|
+
# Database core | The Shelf
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
This integration is based on simple CRUD operations and purposely does NOT support relational querying.
|
|
4
|
+
This package contains the definition of the CRUD operations. It uses a interchangeable driver system for performing the actual operations. An in-memory driver is included.
|
|
7
5
|
|
|
8
6
|
## Installation
|
|
9
7
|
|
|
@@ -11,42 +9,20 @@ This integration is based on simple CRUD operations and purposely does NOT suppo
|
|
|
11
9
|
npm install @theshelf/database
|
|
12
10
|
```
|
|
13
11
|
|
|
14
|
-
## Drivers
|
|
15
|
-
|
|
16
|
-
Currently, there are two drivers available:
|
|
17
|
-
|
|
18
|
-
* **Memory** - non-persistent in memory storage (suited for testing).
|
|
19
|
-
* **MongoDB** - persistent document storage.
|
|
20
|
-
|
|
21
12
|
## How to use
|
|
22
13
|
|
|
23
14
|
The basic set up looks like this.
|
|
24
15
|
|
|
25
16
|
```ts
|
|
26
|
-
import Database, { MemoryDriver
|
|
17
|
+
import Database, { MemoryDriver } from '@theshelf/database';
|
|
27
18
|
|
|
28
|
-
const driver = new
|
|
19
|
+
const driver = new MemoryDriver();
|
|
29
20
|
const database = new Database(driver);
|
|
30
21
|
|
|
31
22
|
// Perform operations with the database instance
|
|
32
23
|
```
|
|
33
24
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
#### Memory driver
|
|
37
|
-
|
|
38
|
-
No configuration options.
|
|
39
|
-
|
|
40
|
-
#### MongoDB driver
|
|
41
|
-
|
|
42
|
-
```ts
|
|
43
|
-
type MongoDBConfiguration = {
|
|
44
|
-
connectionString: string; // e.g. "mongodb://development:development@localhost:27017"
|
|
45
|
-
databaseName: string; // e.g. "mydb"
|
|
46
|
-
};
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Operations
|
|
25
|
+
## Operations
|
|
50
26
|
|
|
51
27
|
```ts
|
|
52
28
|
import { RecordData, RecordQuery, RecordSort, SortDirections } from '@theshelf/database';
|
|
@@ -95,7 +71,7 @@ await database.updateRecord('items', item.id, { 'name': item.name });
|
|
|
95
71
|
await database.deleteRecord('items', item.id);
|
|
96
72
|
```
|
|
97
73
|
|
|
98
|
-
|
|
74
|
+
## Query options
|
|
99
75
|
|
|
100
76
|
A basic query has the following structure.
|
|
101
77
|
|
|
@@ -112,7 +88,7 @@ const andQuery: RecordQuery = { AND: [ query1, query2, ...] }
|
|
|
112
88
|
const orQuery: RecordQuery = { OR: [ query1, query2, ...] }
|
|
113
89
|
```
|
|
114
90
|
|
|
115
|
-
|
|
91
|
+
## Sort options
|
|
116
92
|
|
|
117
93
|
A basic query has the following structure.
|
|
118
94
|
|
|
@@ -127,3 +103,16 @@ const sort: RecordSort = { fieldName1: SortDirections.ASCENDING, fieldName2: Sor
|
|
|
127
103
|
```
|
|
128
104
|
|
|
129
105
|
The sort will be performed in the configured order.
|
|
106
|
+
|
|
107
|
+
## Drivers
|
|
108
|
+
|
|
109
|
+
There is one driver included in this package. Other drivers are available in separate packages.
|
|
110
|
+
|
|
111
|
+
### Memory
|
|
112
|
+
|
|
113
|
+
In memory database (suited for testing). It doesn't have any configuration options, but has an additional operation.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
// Clear the memory
|
|
117
|
+
driver.clear();
|
|
118
|
+
```
|
package/dist/Database.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type Logger from '@theshelf/logging';
|
|
1
2
|
import type { Driver } from './definitions/interfaces.js';
|
|
2
3
|
import type { RecordData, RecordField, RecordId, RecordQuery, RecordSort, RecordType } from './definitions/types.js';
|
|
3
|
-
export default class Database
|
|
4
|
+
export default class Database {
|
|
4
5
|
#private;
|
|
5
|
-
constructor(driver: Driver);
|
|
6
|
+
constructor(driver: Driver, logger?: Logger);
|
|
6
7
|
get connected(): boolean;
|
|
7
8
|
connect(): Promise<void>;
|
|
8
9
|
disconnect(): Promise<void>;
|
package/dist/Database.js
CHANGED
|
@@ -1,40 +1,126 @@
|
|
|
1
1
|
import sanitize from './utilities/sanitize.js';
|
|
2
|
+
import NotConnected from './errors/NotConnected.js';
|
|
2
3
|
export default class Database {
|
|
3
4
|
#driver;
|
|
4
|
-
|
|
5
|
+
#logger;
|
|
6
|
+
#logPrefix;
|
|
7
|
+
constructor(driver, logger) {
|
|
5
8
|
this.#driver = driver;
|
|
9
|
+
this.#logger = logger?.for(Database.name);
|
|
10
|
+
this.#logPrefix = `${this.#driver.name} ->`;
|
|
6
11
|
}
|
|
7
12
|
get connected() {
|
|
8
13
|
return this.#driver.connected;
|
|
9
14
|
}
|
|
10
|
-
connect() {
|
|
11
|
-
|
|
15
|
+
async connect() {
|
|
16
|
+
if (this.connected === true) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
this.#logger?.debug(this.#logPrefix, 'Connecting');
|
|
20
|
+
try {
|
|
21
|
+
await this.#driver.connect();
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
this.#logger?.error(this.#logPrefix, 'Connect failed with error', error);
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
12
27
|
}
|
|
13
|
-
disconnect() {
|
|
14
|
-
|
|
28
|
+
async disconnect() {
|
|
29
|
+
if (this.connected === false) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
this.#logger?.debug(this.#logPrefix, 'Disconnecting');
|
|
33
|
+
try {
|
|
34
|
+
return await this.#driver.disconnect();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
this.#logger?.error(this.#logPrefix, 'Disconnect failed with error', error);
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
15
40
|
}
|
|
16
|
-
createRecord(type, data) {
|
|
17
|
-
|
|
18
|
-
|
|
41
|
+
async createRecord(type, data) {
|
|
42
|
+
this.#logger?.debug(this.#logPrefix, 'Creating record for type', type);
|
|
43
|
+
try {
|
|
44
|
+
this.#validateConnection();
|
|
45
|
+
const cleanData = sanitize(data);
|
|
46
|
+
return await this.#driver.createRecord(type, cleanData);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
this.#logger?.error(this.#logPrefix, 'Create record for type', type, 'failed with error', error);
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
19
52
|
}
|
|
20
|
-
readRecord(type, query, fields, sort) {
|
|
21
|
-
|
|
53
|
+
async readRecord(type, query, fields, sort) {
|
|
54
|
+
this.#logger?.debug(this.#logPrefix, 'Reading record for type', type);
|
|
55
|
+
try {
|
|
56
|
+
this.#validateConnection();
|
|
57
|
+
return await this.#driver.readRecord(type, query, fields, sort);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
this.#logger?.error(this.#logPrefix, 'Read record for type', type, 'failed with error', error);
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
22
63
|
}
|
|
23
|
-
searchRecords(type, query, fields, sort, limit, offset) {
|
|
24
|
-
|
|
64
|
+
async searchRecords(type, query, fields, sort, limit, offset) {
|
|
65
|
+
this.#logger?.debug(this.#logPrefix, 'Searching record for type', type);
|
|
66
|
+
try {
|
|
67
|
+
this.#validateConnection();
|
|
68
|
+
return await this.#driver.searchRecords(type, query, fields, sort, limit, offset);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
this.#logger?.error(this.#logPrefix, 'Search record for type', type, 'failed with error', error);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
25
74
|
}
|
|
26
|
-
updateRecord(type, query, data) {
|
|
27
|
-
|
|
28
|
-
|
|
75
|
+
async updateRecord(type, query, data) {
|
|
76
|
+
this.#logger?.debug(this.#logPrefix, 'Updating record for type', type);
|
|
77
|
+
try {
|
|
78
|
+
this.#validateConnection();
|
|
79
|
+
const cleanData = sanitize(data);
|
|
80
|
+
return await this.#driver.updateRecord(type, query, cleanData);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
this.#logger?.error(this.#logPrefix, 'Update record for type', type, 'failed with error', error);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
29
86
|
}
|
|
30
|
-
updateRecords(type, query, data) {
|
|
31
|
-
|
|
32
|
-
|
|
87
|
+
async updateRecords(type, query, data) {
|
|
88
|
+
this.#logger?.debug(this.#logPrefix, 'Updating records for type', type);
|
|
89
|
+
try {
|
|
90
|
+
this.#validateConnection();
|
|
91
|
+
const cleanData = sanitize(data);
|
|
92
|
+
return await this.#driver.updateRecords(type, query, cleanData);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
this.#logger?.error(this.#logPrefix, 'Update records for type', type, 'failed with error', error);
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
33
98
|
}
|
|
34
|
-
deleteRecord(type, query) {
|
|
35
|
-
|
|
99
|
+
async deleteRecord(type, query) {
|
|
100
|
+
this.#logger?.debug(this.#logPrefix, 'Deleting record for type', type);
|
|
101
|
+
try {
|
|
102
|
+
this.#validateConnection();
|
|
103
|
+
return await this.#driver.deleteRecord(type, query);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
this.#logger?.error(this.#logPrefix, 'Delete record for type', type, 'failed with error', error);
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
36
109
|
}
|
|
37
|
-
deleteRecords(type, query) {
|
|
38
|
-
|
|
110
|
+
async deleteRecords(type, query) {
|
|
111
|
+
this.#logger?.debug(this.#logPrefix, 'Deleting records for type', type);
|
|
112
|
+
try {
|
|
113
|
+
this.#validateConnection();
|
|
114
|
+
return await this.#driver.deleteRecords(type, query);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
this.#logger?.error(this.#logPrefix, 'Delete records for type', type, 'failed with error', error);
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
#validateConnection() {
|
|
122
|
+
if (this.connected === false) {
|
|
123
|
+
throw new NotConnected();
|
|
124
|
+
}
|
|
39
125
|
}
|
|
40
126
|
}
|
package/dist/drivers/Memory.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Driver } from '../definitions/interfaces.js';
|
|
|
2
2
|
import type { RecordData, RecordQuery, RecordSort } from '../definitions/types.js';
|
|
3
3
|
export default class Memory implements Driver {
|
|
4
4
|
#private;
|
|
5
|
+
get name(): string;
|
|
5
6
|
get connected(): boolean;
|
|
6
7
|
get memory(): Map<string, RecordData[]>;
|
|
7
8
|
connect(): Promise<void>;
|
package/dist/drivers/Memory.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export
|
|
2
|
-
export type
|
|
3
|
-
export type
|
|
1
|
+
export { ID, LogicalOperators, SortDirections, QueryOperators } from './definitions/constants.js';
|
|
2
|
+
export type { Driver } from './definitions/interfaces.js';
|
|
3
|
+
export type { RecordType, RecordId, RecordField, RecordValue, RecordData, QueryOperator, QueryExpression, QuerySingleExpressionStatement, QueryMultiExpressionStatement, QuerySingleStatement, QueryMultiStatement, QueryStatement, RecordQuery, RecordDirection, RecordSort } from './definitions/types.js';
|
|
4
4
|
export { default as DatabaseError } from './errors/DatabaseError.js';
|
|
5
5
|
export { default as NotConnected } from './errors/NotConnected.js';
|
|
6
6
|
export { default as MemoryDriver } from './drivers/Memory.js';
|
|
7
|
-
export { default as MongoDBDriver } from './drivers/MongoDB.js';
|
|
8
7
|
export { default } from './Database.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { ID, LogicalOperators, SortDirections, QueryOperators } from './definitions/constants.js';
|
|
2
2
|
export { default as DatabaseError } from './errors/DatabaseError.js';
|
|
3
3
|
export { default as NotConnected } from './errors/NotConnected.js';
|
|
4
4
|
export { default as MemoryDriver } from './drivers/Memory.js';
|
|
5
|
-
export { default as MongoDBDriver } from './drivers/MongoDB.js';
|
|
6
5
|
export { default } from './Database.js';
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theshelf/database",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"url": "git+https://github.com/MaskingTechnology/theshelf.git"
|
|
8
8
|
},
|
|
9
|
+
"license": "MIT",
|
|
9
10
|
"scripts": {
|
|
10
11
|
"build": "tsc",
|
|
11
12
|
"clean": "rimraf dist",
|
|
@@ -19,10 +20,15 @@
|
|
|
19
20
|
"README.md",
|
|
20
21
|
"dist"
|
|
21
22
|
],
|
|
22
|
-
"types": "dist/index.d.ts",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
23
24
|
"exports": "./dist/index.js",
|
|
24
25
|
"dependencies": {
|
|
25
|
-
"mongodb": "7.0.0",
|
|
26
26
|
"sanitize-html": "2.17.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/sanitize-html": "2.16.0"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@theshelf/logging": "^0.4.0"
|
|
27
33
|
}
|
|
28
34
|
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { Driver } from '../definitions/interfaces.js';
|
|
2
|
-
import type { RecordData, RecordField, RecordId, RecordQuery, RecordSort, RecordType } from '../definitions/types.js';
|
|
3
|
-
type MongoDBConfiguration = {
|
|
4
|
-
connectionString: string;
|
|
5
|
-
databaseName: string;
|
|
6
|
-
};
|
|
7
|
-
export default class MongoDB implements Driver {
|
|
8
|
-
#private;
|
|
9
|
-
constructor(configuration: MongoDBConfiguration);
|
|
10
|
-
get connected(): boolean;
|
|
11
|
-
connect(): Promise<void>;
|
|
12
|
-
disconnect(): Promise<void>;
|
|
13
|
-
createRecord(type: RecordType, data: RecordData): Promise<RecordId>;
|
|
14
|
-
readRecord(type: RecordType, query: RecordQuery, fields?: RecordField[], sort?: RecordSort): Promise<RecordData | undefined>;
|
|
15
|
-
searchRecords(type: RecordType, query: RecordQuery, fields?: RecordField[], sort?: RecordSort, limit?: number, offset?: number): Promise<RecordData[]>;
|
|
16
|
-
updateRecord(type: RecordType, query: RecordQuery, data: RecordData): Promise<number>;
|
|
17
|
-
updateRecords(type: RecordType, query: RecordQuery, data: RecordData): Promise<number>;
|
|
18
|
-
deleteRecord(type: RecordType, query: RecordQuery): Promise<number>;
|
|
19
|
-
deleteRecords(type: RecordType, query: RecordQuery): Promise<number>;
|
|
20
|
-
}
|
|
21
|
-
export {};
|
package/dist/drivers/MongoDB.js
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
/* eslint @typescript-eslint/no-explicit-any: "off" */
|
|
2
|
-
import { MongoClient } from 'mongodb';
|
|
3
|
-
import { ID, LogicalOperators, QueryOperators, SortDirections } from '../definitions/constants.js';
|
|
4
|
-
import DatabaseError from '../errors/DatabaseError.js';
|
|
5
|
-
import NotConnected from '../errors/NotConnected.js';
|
|
6
|
-
const UNKNOWN_ERROR = 'Unknown error';
|
|
7
|
-
const OPERATORS = {
|
|
8
|
-
[QueryOperators.EQUALS]: '$eq',
|
|
9
|
-
[QueryOperators.GREATER_THAN]: '$gt',
|
|
10
|
-
[QueryOperators.GREATER_THAN_OR_EQUALS]: '$gte',
|
|
11
|
-
[QueryOperators.IN]: '$in',
|
|
12
|
-
[QueryOperators.LESS_THAN]: '$lt',
|
|
13
|
-
[QueryOperators.LESS_THAN_OR_EQUALS]: '$lte',
|
|
14
|
-
[QueryOperators.NOT_EQUALS]: '$ne',
|
|
15
|
-
[QueryOperators.NOT_IN]: '$nin',
|
|
16
|
-
[QueryOperators.CONTAINS]: '$regex',
|
|
17
|
-
[QueryOperators.STARTS_WITH]: '$regex',
|
|
18
|
-
[QueryOperators.ENDS_WITH]: '$regex'
|
|
19
|
-
};
|
|
20
|
-
const LOGICAL_OPERATORS = {
|
|
21
|
-
[LogicalOperators.AND]: '$and',
|
|
22
|
-
[LogicalOperators.OR]: '$or'
|
|
23
|
-
};
|
|
24
|
-
const MONGO_ID = '_id';
|
|
25
|
-
export default class MongoDB {
|
|
26
|
-
#connectionString;
|
|
27
|
-
#databaseName;
|
|
28
|
-
#client;
|
|
29
|
-
#database;
|
|
30
|
-
#connected = false;
|
|
31
|
-
constructor(configuration) {
|
|
32
|
-
this.#connectionString = configuration.connectionString;
|
|
33
|
-
this.#databaseName = configuration.databaseName;
|
|
34
|
-
}
|
|
35
|
-
get connected() { return this.#connected; }
|
|
36
|
-
async connect() {
|
|
37
|
-
try {
|
|
38
|
-
this.#client = await this.#createClient(this.#connectionString);
|
|
39
|
-
this.#client.on('close', () => { this.#connected = false; });
|
|
40
|
-
this.#client.on('serverHeartbeatSucceeded', () => { this.#connected = true; });
|
|
41
|
-
this.#client.on('serverHeartbeatFailed', () => { this.#connected = false; });
|
|
42
|
-
this.#database = this.#getDatabase(this.#databaseName);
|
|
43
|
-
this.#connected = true;
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
const message = error instanceof Error ? error.message : UNKNOWN_ERROR;
|
|
47
|
-
throw new DatabaseError('Database connection failed: ' + message);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
async disconnect() {
|
|
51
|
-
if (this.#client === undefined) {
|
|
52
|
-
throw new NotConnected();
|
|
53
|
-
}
|
|
54
|
-
try {
|
|
55
|
-
await this.#client.close();
|
|
56
|
-
this.#connected = false;
|
|
57
|
-
this.#client = undefined;
|
|
58
|
-
this.#database = undefined;
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
const message = error instanceof Error ? error.message : UNKNOWN_ERROR;
|
|
62
|
-
throw new DatabaseError('Database disconnection failed: ' + message);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
async createRecord(type, data) {
|
|
66
|
-
const collection = await this.#getCollection(type);
|
|
67
|
-
const dataCopy = { ...data };
|
|
68
|
-
const id = dataCopy.id;
|
|
69
|
-
delete dataCopy.id;
|
|
70
|
-
await collection.insertOne({ _id: id, ...dataCopy });
|
|
71
|
-
return id;
|
|
72
|
-
}
|
|
73
|
-
async readRecord(type, query, fields, sort) {
|
|
74
|
-
const result = await this.searchRecords(type, query, fields, sort, 1, 0);
|
|
75
|
-
return result[0];
|
|
76
|
-
}
|
|
77
|
-
async searchRecords(type, query, fields, sort, limit, offset) {
|
|
78
|
-
const mongoQuery = this.#buildMongoQuery(query);
|
|
79
|
-
const mongoSort = this.#buildMongoSort(sort);
|
|
80
|
-
const collection = await this.#getCollection(type);
|
|
81
|
-
const cursor = collection.find(mongoQuery, { sort: mongoSort, limit: limit, skip: offset });
|
|
82
|
-
const result = await cursor.toArray();
|
|
83
|
-
return result.map(data => this.#buildRecordData(data, fields));
|
|
84
|
-
}
|
|
85
|
-
async updateRecord(type, query, data) {
|
|
86
|
-
const mongoQuery = this.#buildMongoQuery(query);
|
|
87
|
-
const collection = await this.#getCollection(type);
|
|
88
|
-
const result = await collection.updateOne(mongoQuery, { $set: data });
|
|
89
|
-
return result.modifiedCount;
|
|
90
|
-
}
|
|
91
|
-
async updateRecords(type, query, data) {
|
|
92
|
-
const mongoQuery = this.#buildMongoQuery(query);
|
|
93
|
-
const collection = await this.#getCollection(type);
|
|
94
|
-
const result = await collection.updateMany(mongoQuery, { $set: data });
|
|
95
|
-
return result.modifiedCount;
|
|
96
|
-
}
|
|
97
|
-
async deleteRecord(type, query) {
|
|
98
|
-
const mongoQuery = this.#buildMongoQuery(query);
|
|
99
|
-
const collection = await this.#getCollection(type);
|
|
100
|
-
const result = await collection.deleteOne(mongoQuery);
|
|
101
|
-
return result.deletedCount;
|
|
102
|
-
}
|
|
103
|
-
async deleteRecords(type, query) {
|
|
104
|
-
const mongoQuery = this.#buildMongoQuery(query);
|
|
105
|
-
const collection = await this.#getCollection(type);
|
|
106
|
-
const result = await collection.deleteMany(mongoQuery);
|
|
107
|
-
return result.deletedCount;
|
|
108
|
-
}
|
|
109
|
-
#buildMongoQuery(query) {
|
|
110
|
-
const mongoQuery = {};
|
|
111
|
-
const multiStatements = query;
|
|
112
|
-
const singleStatements = query;
|
|
113
|
-
for (const key in multiStatements) {
|
|
114
|
-
if (key === 'AND' || key === 'OR') {
|
|
115
|
-
const singleMultiStatements = multiStatements[key] ?? [];
|
|
116
|
-
const multiMongoQuery = [];
|
|
117
|
-
for (const statement of singleMultiStatements) {
|
|
118
|
-
const mongoQuery = this.#buildMongoQuery(statement);
|
|
119
|
-
multiMongoQuery.push(mongoQuery);
|
|
120
|
-
}
|
|
121
|
-
const mongoKey = LOGICAL_OPERATORS[key];
|
|
122
|
-
mongoQuery[mongoKey] = multiMongoQuery;
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
const expression = singleStatements[key];
|
|
126
|
-
const mongoKey = key === ID ? MONGO_ID : key;
|
|
127
|
-
const mongoExpression = {};
|
|
128
|
-
for (const operator in expression) {
|
|
129
|
-
const value = this.#extractValue(expression, operator);
|
|
130
|
-
const mongoOperator = OPERATORS[operator];
|
|
131
|
-
mongoExpression[mongoOperator] = value;
|
|
132
|
-
}
|
|
133
|
-
mongoQuery[mongoKey] = mongoExpression;
|
|
134
|
-
}
|
|
135
|
-
return mongoQuery;
|
|
136
|
-
}
|
|
137
|
-
#buildMongoSort(sort) {
|
|
138
|
-
const mongoSort = {};
|
|
139
|
-
if (sort === undefined) {
|
|
140
|
-
return mongoSort;
|
|
141
|
-
}
|
|
142
|
-
for (const element in sort) {
|
|
143
|
-
const direction = sort[element];
|
|
144
|
-
mongoSort[element] = direction === SortDirections.DESCENDING ? -1 : 1;
|
|
145
|
-
}
|
|
146
|
-
return mongoSort;
|
|
147
|
-
}
|
|
148
|
-
async #getCollection(name) {
|
|
149
|
-
if (this.#database === undefined) {
|
|
150
|
-
throw new NotConnected();
|
|
151
|
-
}
|
|
152
|
-
return this.#database.collection(name);
|
|
153
|
-
}
|
|
154
|
-
#getDatabase(databaseName) {
|
|
155
|
-
if (this.#client === undefined) {
|
|
156
|
-
throw new NotConnected();
|
|
157
|
-
}
|
|
158
|
-
return this.#client.db(databaseName);
|
|
159
|
-
}
|
|
160
|
-
async #createClient(connectionString) {
|
|
161
|
-
return MongoClient.connect(connectionString);
|
|
162
|
-
}
|
|
163
|
-
#buildRecordData(data, fields) {
|
|
164
|
-
const result = {};
|
|
165
|
-
if (fields === undefined) {
|
|
166
|
-
const recordData = { ...data };
|
|
167
|
-
fields = Object.keys(recordData);
|
|
168
|
-
const idIndex = fields.indexOf(MONGO_ID);
|
|
169
|
-
fields[idIndex] = ID;
|
|
170
|
-
}
|
|
171
|
-
for (const field of fields) {
|
|
172
|
-
const value = field === ID
|
|
173
|
-
? data[MONGO_ID]
|
|
174
|
-
: data[field];
|
|
175
|
-
result[field] = value ?? undefined;
|
|
176
|
-
}
|
|
177
|
-
return result;
|
|
178
|
-
}
|
|
179
|
-
#extractValue(expression, operator) {
|
|
180
|
-
const value = expression[operator];
|
|
181
|
-
switch (operator) {
|
|
182
|
-
case QueryOperators.STARTS_WITH: return '^' + value;
|
|
183
|
-
case QueryOperators.ENDS_WITH: return value + '$';
|
|
184
|
-
}
|
|
185
|
-
return value;
|
|
186
|
-
}
|
|
187
|
-
}
|