@opra/mongodb 0.17.1 → 0.17.3

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.
@@ -3,10 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MongoCollectionResource = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const common_1 = require("@opra/common");
6
+ const core_1 = require("@opra/core");
6
7
  const mongo_adapter_js_1 = require("./mongo-adapter.js");
7
- class MongoCollectionResource {
8
- constructor() {
9
- this.defaultLimit = 100;
8
+ // noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected
9
+ class MongoCollectionResource extends core_1.CollectionResourceBase {
10
+ constructor(options) {
11
+ super(options);
10
12
  }
11
13
  async create(ctx) {
12
14
  const prepared = mongo_adapter_js_1.MongoAdapter.transformRequest(ctx.request);
@@ -26,18 +28,12 @@ class MongoCollectionResource {
26
28
  async get(ctx) {
27
29
  const prepared = mongo_adapter_js_1.MongoAdapter.transformRequest(ctx.request);
28
30
  const service = await this.getService(ctx);
29
- const out = await service.findOne(prepared.filter, prepared.options);
30
- if (!out)
31
- throw new common_1.ResourceNotFoundError(service.collectionName, prepared.filter._id);
32
- return out;
31
+ return service.findOne(prepared.filter, prepared.options);
33
32
  }
34
33
  async update(ctx) {
35
34
  const prepared = mongo_adapter_js_1.MongoAdapter.transformRequest(ctx.request);
36
35
  const service = await this.getService(ctx);
37
- const out = await service.updateOne(prepared.filter, prepared.update, prepared.options);
38
- if (!out)
39
- throw new common_1.ResourceNotFoundError(service.collectionName, prepared.filter._id);
40
- return out;
36
+ return service.updateOne(prepared.filter, prepared.update, prepared.options);
41
37
  }
42
38
  async updateMany(ctx) {
43
39
  const prepared = mongo_adapter_js_1.MongoAdapter.transformRequest(ctx.request);
@@ -2,13 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MongoEntityService = void 0;
4
4
  class MongoEntityService {
5
- constructor(collectionName, options) {
6
- this.collectionName = collectionName;
5
+ constructor(arg0, arg1) {
6
+ const options = typeof arg0 === 'object' ? arg0 : arg1;
7
+ if (typeof arg0 === 'string')
8
+ this._collectionName = arg0;
7
9
  this.db = options?.db;
8
10
  this.defaultLimit = options?.defaultLimit || 10;
9
11
  }
10
12
  async count(filter, options) {
11
- const collection = (await this.getDatabase()).collection(this.collectionName);
13
+ const db = await this.getDatabase();
14
+ const collection = await this.getCollection(db);
12
15
  options = {
13
16
  ...options,
14
17
  session: options?.session || this.session
@@ -22,7 +25,8 @@ class MongoEntityService {
22
25
  }
23
26
  }
24
27
  async deleteOne(filter, options) {
25
- const collection = (await this.getDatabase()).collection(this.collectionName);
28
+ const db = await this.getDatabase();
29
+ const collection = await this.getCollection(db);
26
30
  options = {
27
31
  ...options,
28
32
  session: options?.session || this.session
@@ -37,7 +41,8 @@ class MongoEntityService {
37
41
  }
38
42
  }
39
43
  async deleteMany(filter, options) {
40
- const collection = (await this.getDatabase()).collection(this.collectionName);
44
+ const db = await this.getDatabase();
45
+ const collection = await this.getCollection(db);
41
46
  options = {
42
47
  ...options,
43
48
  session: options?.session || this.session
@@ -52,7 +57,8 @@ class MongoEntityService {
52
57
  }
53
58
  }
54
59
  async findOne(filter, options) {
55
- const collection = (await this.getDatabase()).collection(this.collectionName);
60
+ const db = await this.getDatabase();
61
+ const collection = await this.getCollection(db);
56
62
  options = {
57
63
  ...options,
58
64
  session: options?.session || this.session
@@ -65,35 +71,40 @@ class MongoEntityService {
65
71
  await this._onError(e);
66
72
  throw e;
67
73
  }
68
- if (this.onTransformRow)
69
- out = this.onTransformRow(out);
70
74
  return out;
71
75
  }
72
76
  async find(filter, options) {
73
- const collection = (await this.getDatabase()).collection(this.collectionName);
77
+ const db = await this.getDatabase();
78
+ const collection = await this.getCollection(db);
74
79
  options = {
75
80
  ...options,
76
81
  limit: options?.limit || this.defaultLimit,
77
82
  session: options?.session || this.session
78
83
  };
79
84
  const out = [];
85
+ let cursor;
80
86
  try {
81
- const cursor = collection.find(filter, options);
82
- let row;
83
- while (row = await cursor.next()) {
84
- if (this.onTransformRow)
85
- row = this.onTransformRow(row);
86
- out.push(row);
87
+ cursor = collection.find(filter, options);
88
+ let obj;
89
+ while (out.length < this.defaultLimit && (obj = await cursor.next())) {
90
+ const v = this.transformData ? this.transformData(obj) : obj;
91
+ if (v)
92
+ out.push(obj);
87
93
  }
88
94
  }
89
95
  catch (e) {
90
96
  await this._onError(e);
91
97
  throw e;
92
98
  }
99
+ finally {
100
+ if (cursor)
101
+ await cursor.close();
102
+ }
93
103
  return out;
94
104
  }
95
105
  async insertOne(doc, options) {
96
- const collection = (await this.getDatabase()).collection(this.collectionName);
106
+ const db = await this.getDatabase();
107
+ const collection = await this.getCollection(db);
97
108
  let out;
98
109
  options = {
99
110
  ...options,
@@ -108,14 +119,15 @@ class MongoEntityService {
108
119
  await this._onError(e);
109
120
  throw e;
110
121
  }
111
- if (this.onTransformRow)
112
- out = this.onTransformRow(out);
122
+ if (this.transformData)
123
+ out = this.transformData(out);
113
124
  if (!out)
114
125
  throw new Error('"insertOne" operation returned no result!');
115
126
  return out;
116
127
  }
117
128
  async updateOne(filter, doc, options) {
118
- const collection = (await this.getDatabase()).collection(this.collectionName);
129
+ const db = await this.getDatabase();
130
+ const collection = await this.getCollection(db);
119
131
  let out;
120
132
  options = {
121
133
  ...options,
@@ -130,12 +142,13 @@ class MongoEntityService {
130
142
  await this._onError(e);
131
143
  throw e;
132
144
  }
133
- if (this.onTransformRow)
134
- out = this.onTransformRow(out);
145
+ if (this.transformData)
146
+ out = this.transformData(out);
135
147
  return out;
136
148
  }
137
149
  async updateMany(filter, doc, options) {
138
- const collection = (await this.getDatabase()).collection(this.collectionName);
150
+ const db = await this.getDatabase();
151
+ const collection = await this.getCollection(db);
139
152
  options = {
140
153
  ...options,
141
154
  session: options?.session || this.session,
@@ -175,5 +188,15 @@ class MongoEntityService {
175
188
  throw new Error(`Database not set!`);
176
189
  return this.db;
177
190
  }
191
+ async getCollection(db) {
192
+ if (!this._collectionName)
193
+ throw new Error('collectionName is not assigned');
194
+ return db.collection(this.getCollectionName());
195
+ }
196
+ getCollectionName() {
197
+ if (!this._collectionName)
198
+ throw new Error('collectionName is not defined');
199
+ return this._collectionName;
200
+ }
178
201
  }
179
202
  exports.MongoEntityService = MongoEntityService;
@@ -22,18 +22,12 @@ class MongoSingletonResource {
22
22
  async get(ctx) {
23
23
  const prepared = mongo_adapter_js_1.MongoAdapter.transformRequest(ctx.request);
24
24
  const service = await this.getService(ctx);
25
- const out = await service.findOne(prepared.filter, prepared.options);
26
- if (!out)
27
- throw new common_1.ResourceNotFoundError(service.collectionName);
28
- return out;
25
+ return service.findOne(prepared.filter, prepared.options);
29
26
  }
30
27
  async update(ctx) {
31
28
  const prepared = mongo_adapter_js_1.MongoAdapter.transformRequest(ctx.request);
32
29
  const service = await this.getService(ctx);
33
- const out = await service.updateOne(prepared.filter, prepared.update, prepared.options);
34
- if (!out)
35
- throw new common_1.ResourceNotFoundError(service.collectionName, prepared.filter._id);
36
- return out;
30
+ return service.updateOne(prepared.filter, prepared.update, prepared.options);
37
31
  }
38
32
  }
39
33
  tslib_1.__decorate([
@@ -1,96 +1,111 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("@opra/core");
3
4
  const common_1 = require("@opra/common");
4
- function transformFilter(str) {
5
- const ast = typeof str === 'string' ? (0, common_1.parseFilter)(str) : str;
5
+ const opMap = {
6
+ '=': '$eq',
7
+ '!=': '$ne',
8
+ '>': '$gt',
9
+ '>=': '$gte',
10
+ '<': '$lt',
11
+ '<=': '$lte',
12
+ 'in': '$in',
13
+ '!in': '$nin',
14
+ };
15
+ function transformFilter(ast, negative) {
6
16
  if (!ast)
7
17
  return;
8
- if (ast instanceof common_1.ComparisonExpression) {
9
- const left = ast.left instanceof common_1.QualifiedIdentifier
10
- ? ast.left.value
11
- : transformFilter(ast.left);
12
- const right = transformFilter(ast.right);
13
- switch (ast.op) {
14
- case '=':
18
+ if (ast instanceof common_1.OpraFilter.QualifiedIdentifier) {
19
+ return ast.value;
20
+ }
21
+ if (ast instanceof common_1.OpraFilter.NumberLiteral ||
22
+ ast instanceof common_1.OpraFilter.StringLiteral ||
23
+ ast instanceof common_1.OpraFilter.BooleanLiteral ||
24
+ ast instanceof common_1.OpraFilter.NullLiteral ||
25
+ ast instanceof common_1.OpraFilter.DateLiteral ||
26
+ ast instanceof common_1.OpraFilter.TimeLiteral) {
27
+ return ast.value;
28
+ }
29
+ if (ast instanceof common_1.OpraFilter.ArrayExpression) {
30
+ return ast.items.map(x => transformFilter(x, negative));
31
+ }
32
+ if (ast instanceof common_1.OpraFilter.NegativeExpression) {
33
+ return transformFilter(ast.expression, !negative);
34
+ }
35
+ if (ast instanceof common_1.OpraFilter.LogicalExpression) {
36
+ if (ast.op === 'or')
37
+ return { $or: ast.items.map(x => transformFilter(x, negative)) };
38
+ return { $and: ast.items.map(x => transformFilter(x, negative)) };
39
+ }
40
+ if (ast instanceof common_1.OpraFilter.ParenthesizedExpression) {
41
+ return transformFilter(ast.expression, negative);
42
+ }
43
+ if (ast instanceof common_1.OpraFilter.ComparisonExpression) {
44
+ const left = transformFilter(ast.left, negative);
45
+ if (ast.right instanceof common_1.OpraFilter.QualifiedIdentifier) {
46
+ const op = opMap[ast.op];
47
+ if (op)
48
+ return { $expr: { [op]: ["$" + left, "$" + ast.right.value] } };
49
+ /* istanbul ignore next */
50
+ throw new Error(`Invalid filter query.`);
51
+ }
52
+ let right = transformFilter(ast.right);
53
+ if (right == null) {
54
+ const op = ast.op === '='
55
+ ? (negative ? '!=' : '=')
56
+ : (negative ? '=' : '!=');
57
+ if (op === '=')
58
+ return { $or: [{ [left]: null }, { [left]: { $exists: false } }] };
59
+ if (op === '!=')
60
+ return { $and: [{ $ne: { [left]: null } }, { [left]: { $exists: true } }] };
61
+ }
62
+ const mngOp = opMap[ast.op];
63
+ if (mngOp) {
64
+ if (ast.op === 'in' || ast.op === '!in')
65
+ right = Array.isArray(right) ? right : [right];
66
+ if (ast.op === '=' && !negative)
15
67
  return { [left]: right };
16
- case '!=':
17
- return { [left]: { $ne: right } };
18
- case '>':
19
- return { [left]: { $gt: right } };
20
- case '>=':
21
- return { [left]: { $gte: right } };
22
- case '<':
23
- return { [left]: { $lt: right } };
24
- case '<=':
25
- return { [left]: { $lte: right } };
26
- case 'in':
27
- return { [left]: { $in: Array.isArray(right) ? right : [right] } };
28
- case '!in':
29
- return { [left]: { $nin: Array.isArray(right) ? right : [right] } };
68
+ return { [left]: wrapNot({ [mngOp]: right }, negative) };
69
+ }
70
+ switch (ast.op) {
30
71
  case 'like':
31
72
  return {
32
- [left]: {
73
+ [left]: wrapNot({
33
74
  $text: {
34
75
  $search: '\\"' + right.replace(/\\"/, '"') + '\\"',
35
76
  $caseSensitive: true
36
77
  }
37
- }
78
+ }, negative)
38
79
  };
39
80
  case 'ilike':
40
81
  return {
41
- [left]: {
82
+ [left]: wrapNot({
42
83
  $text: {
43
84
  $search: '\\"' + right.replace(/\\"/, '"') + '\\"'
44
85
  }
45
- }
86
+ }, negative)
46
87
  };
47
88
  case '!like':
48
89
  return {
49
- [left]: {
50
- $not: {
51
- $text: {
52
- $search: '\\"' + right.replace(/\\"/, '"') + '\\"',
53
- $caseSensitive: true
54
- }
90
+ [left]: wrapNot({
91
+ $text: {
92
+ $search: '\\"' + right.replace(/\\"/, '"') + '\\"',
93
+ $caseSensitive: true
55
94
  }
56
- }
95
+ }, !negative)
57
96
  };
58
97
  case '!ilike':
59
98
  return {
60
- [left]: {
61
- $not: {
62
- $text: {
63
- $search: '\\"' + right.replace(/\\"/, '"') + '\\"'
64
- }
99
+ [left]: wrapNot({
100
+ $text: {
101
+ $search: '\\"' + right.replace(/\\"/, '"') + '\\"'
65
102
  }
66
- }
103
+ }, !negative)
67
104
  };
68
- default:
69
- throw new Error(`ComparisonExpression operator (${ast.op}) not implemented yet`);
70
105
  }
106
+ throw new Error(`Unimplemented ComparisonExpression operation (right side is ${ast.right.kind})`);
71
107
  }
72
- if (ast instanceof common_1.QualifiedIdentifier) {
73
- return ast.value;
74
- }
75
- if (ast instanceof common_1.NumberLiteral ||
76
- ast instanceof common_1.StringLiteral ||
77
- ast instanceof common_1.BooleanLiteral ||
78
- ast instanceof common_1.NullLiteral ||
79
- ast instanceof common_1.DateLiteral ||
80
- ast instanceof common_1.TimeLiteral) {
81
- return ast.value;
82
- }
83
- if (ast instanceof common_1.ArrayExpression) {
84
- return ast.items.map(transformFilter);
85
- }
86
- if (ast instanceof common_1.LogicalExpression) {
87
- if (ast.op === 'or')
88
- return { $or: ast.items.map(transformFilter) };
89
- return { $and: ast.items.map(transformFilter) };
90
- }
91
- if (ast instanceof common_1.ParenthesesExpression) {
92
- return transformFilter(ast.expression);
93
- }
94
- throw new Error(`${ast.type} is not implemented yet`);
108
+ throw new Error(`${ast.kind} is not implemented yet`);
95
109
  }
96
110
  exports.default = transformFilter;
111
+ const wrapNot = (o, negative) => negative ? { $not: o } : o;
@@ -1,9 +1,11 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
- import { Collection, ResourceNotFoundError } from '@opra/common';
2
+ import { Collection } from '@opra/common';
3
+ import { CollectionResourceBase } from '@opra/core';
3
4
  import { MongoAdapter } from './mongo-adapter.js';
4
- export class MongoCollectionResource {
5
- constructor() {
6
- this.defaultLimit = 100;
5
+ // noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected
6
+ export class MongoCollectionResource extends CollectionResourceBase {
7
+ constructor(options) {
8
+ super(options);
7
9
  }
8
10
  async create(ctx) {
9
11
  const prepared = MongoAdapter.transformRequest(ctx.request);
@@ -23,18 +25,12 @@ export class MongoCollectionResource {
23
25
  async get(ctx) {
24
26
  const prepared = MongoAdapter.transformRequest(ctx.request);
25
27
  const service = await this.getService(ctx);
26
- const out = await service.findOne(prepared.filter, prepared.options);
27
- if (!out)
28
- throw new ResourceNotFoundError(service.collectionName, prepared.filter._id);
29
- return out;
28
+ return service.findOne(prepared.filter, prepared.options);
30
29
  }
31
30
  async update(ctx) {
32
31
  const prepared = MongoAdapter.transformRequest(ctx.request);
33
32
  const service = await this.getService(ctx);
34
- const out = await service.updateOne(prepared.filter, prepared.update, prepared.options);
35
- if (!out)
36
- throw new ResourceNotFoundError(service.collectionName, prepared.filter._id);
37
- return out;
33
+ return service.updateOne(prepared.filter, prepared.update, prepared.options);
38
34
  }
39
35
  async updateMany(ctx) {
40
36
  const prepared = MongoAdapter.transformRequest(ctx.request);
@@ -1,11 +1,14 @@
1
1
  export class MongoEntityService {
2
- constructor(collectionName, options) {
3
- this.collectionName = collectionName;
2
+ constructor(arg0, arg1) {
3
+ const options = typeof arg0 === 'object' ? arg0 : arg1;
4
+ if (typeof arg0 === 'string')
5
+ this._collectionName = arg0;
4
6
  this.db = options?.db;
5
7
  this.defaultLimit = options?.defaultLimit || 10;
6
8
  }
7
9
  async count(filter, options) {
8
- const collection = (await this.getDatabase()).collection(this.collectionName);
10
+ const db = await this.getDatabase();
11
+ const collection = await this.getCollection(db);
9
12
  options = {
10
13
  ...options,
11
14
  session: options?.session || this.session
@@ -19,7 +22,8 @@ export class MongoEntityService {
19
22
  }
20
23
  }
21
24
  async deleteOne(filter, options) {
22
- const collection = (await this.getDatabase()).collection(this.collectionName);
25
+ const db = await this.getDatabase();
26
+ const collection = await this.getCollection(db);
23
27
  options = {
24
28
  ...options,
25
29
  session: options?.session || this.session
@@ -34,7 +38,8 @@ export class MongoEntityService {
34
38
  }
35
39
  }
36
40
  async deleteMany(filter, options) {
37
- const collection = (await this.getDatabase()).collection(this.collectionName);
41
+ const db = await this.getDatabase();
42
+ const collection = await this.getCollection(db);
38
43
  options = {
39
44
  ...options,
40
45
  session: options?.session || this.session
@@ -49,7 +54,8 @@ export class MongoEntityService {
49
54
  }
50
55
  }
51
56
  async findOne(filter, options) {
52
- const collection = (await this.getDatabase()).collection(this.collectionName);
57
+ const db = await this.getDatabase();
58
+ const collection = await this.getCollection(db);
53
59
  options = {
54
60
  ...options,
55
61
  session: options?.session || this.session
@@ -62,35 +68,40 @@ export class MongoEntityService {
62
68
  await this._onError(e);
63
69
  throw e;
64
70
  }
65
- if (this.onTransformRow)
66
- out = this.onTransformRow(out);
67
71
  return out;
68
72
  }
69
73
  async find(filter, options) {
70
- const collection = (await this.getDatabase()).collection(this.collectionName);
74
+ const db = await this.getDatabase();
75
+ const collection = await this.getCollection(db);
71
76
  options = {
72
77
  ...options,
73
78
  limit: options?.limit || this.defaultLimit,
74
79
  session: options?.session || this.session
75
80
  };
76
81
  const out = [];
82
+ let cursor;
77
83
  try {
78
- const cursor = collection.find(filter, options);
79
- let row;
80
- while (row = await cursor.next()) {
81
- if (this.onTransformRow)
82
- row = this.onTransformRow(row);
83
- out.push(row);
84
+ cursor = collection.find(filter, options);
85
+ let obj;
86
+ while (out.length < this.defaultLimit && (obj = await cursor.next())) {
87
+ const v = this.transformData ? this.transformData(obj) : obj;
88
+ if (v)
89
+ out.push(obj);
84
90
  }
85
91
  }
86
92
  catch (e) {
87
93
  await this._onError(e);
88
94
  throw e;
89
95
  }
96
+ finally {
97
+ if (cursor)
98
+ await cursor.close();
99
+ }
90
100
  return out;
91
101
  }
92
102
  async insertOne(doc, options) {
93
- const collection = (await this.getDatabase()).collection(this.collectionName);
103
+ const db = await this.getDatabase();
104
+ const collection = await this.getCollection(db);
94
105
  let out;
95
106
  options = {
96
107
  ...options,
@@ -105,14 +116,15 @@ export class MongoEntityService {
105
116
  await this._onError(e);
106
117
  throw e;
107
118
  }
108
- if (this.onTransformRow)
109
- out = this.onTransformRow(out);
119
+ if (this.transformData)
120
+ out = this.transformData(out);
110
121
  if (!out)
111
122
  throw new Error('"insertOne" operation returned no result!');
112
123
  return out;
113
124
  }
114
125
  async updateOne(filter, doc, options) {
115
- const collection = (await this.getDatabase()).collection(this.collectionName);
126
+ const db = await this.getDatabase();
127
+ const collection = await this.getCollection(db);
116
128
  let out;
117
129
  options = {
118
130
  ...options,
@@ -127,12 +139,13 @@ export class MongoEntityService {
127
139
  await this._onError(e);
128
140
  throw e;
129
141
  }
130
- if (this.onTransformRow)
131
- out = this.onTransformRow(out);
142
+ if (this.transformData)
143
+ out = this.transformData(out);
132
144
  return out;
133
145
  }
134
146
  async updateMany(filter, doc, options) {
135
- const collection = (await this.getDatabase()).collection(this.collectionName);
147
+ const db = await this.getDatabase();
148
+ const collection = await this.getCollection(db);
136
149
  options = {
137
150
  ...options,
138
151
  session: options?.session || this.session,
@@ -172,4 +185,14 @@ export class MongoEntityService {
172
185
  throw new Error(`Database not set!`);
173
186
  return this.db;
174
187
  }
188
+ async getCollection(db) {
189
+ if (!this._collectionName)
190
+ throw new Error('collectionName is not assigned');
191
+ return db.collection(this.getCollectionName());
192
+ }
193
+ getCollectionName() {
194
+ if (!this._collectionName)
195
+ throw new Error('collectionName is not defined');
196
+ return this._collectionName;
197
+ }
175
198
  }
@@ -1,5 +1,5 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
- import { ResourceNotFoundError, Singleton } from '@opra/common';
2
+ import { Singleton } from '@opra/common';
3
3
  import { MongoAdapter } from './mongo-adapter.js';
4
4
  export class MongoSingletonResource {
5
5
  constructor() {
@@ -19,18 +19,12 @@ export class MongoSingletonResource {
19
19
  async get(ctx) {
20
20
  const prepared = MongoAdapter.transformRequest(ctx.request);
21
21
  const service = await this.getService(ctx);
22
- const out = await service.findOne(prepared.filter, prepared.options);
23
- if (!out)
24
- throw new ResourceNotFoundError(service.collectionName);
25
- return out;
22
+ return service.findOne(prepared.filter, prepared.options);
26
23
  }
27
24
  async update(ctx) {
28
25
  const prepared = MongoAdapter.transformRequest(ctx.request);
29
26
  const service = await this.getService(ctx);
30
- const out = await service.updateOne(prepared.filter, prepared.update, prepared.options);
31
- if (!out)
32
- throw new ResourceNotFoundError(service.collectionName, prepared.filter._id);
33
- return out;
27
+ return service.updateOne(prepared.filter, prepared.update, prepared.options);
34
28
  }
35
29
  }
36
30
  __decorate([
@@ -1,93 +1,108 @@
1
- import { ArrayExpression, BooleanLiteral, ComparisonExpression, DateLiteral, LogicalExpression, NullLiteral, NumberLiteral, ParenthesesExpression, parseFilter, QualifiedIdentifier, StringLiteral, TimeLiteral } from '@opra/common';
2
- export default function transformFilter(str) {
3
- const ast = typeof str === 'string' ? parseFilter(str) : str;
1
+ import '@opra/core';
2
+ import { OpraFilter } from '@opra/common';
3
+ const opMap = {
4
+ '=': '$eq',
5
+ '!=': '$ne',
6
+ '>': '$gt',
7
+ '>=': '$gte',
8
+ '<': '$lt',
9
+ '<=': '$lte',
10
+ 'in': '$in',
11
+ '!in': '$nin',
12
+ };
13
+ export default function transformFilter(ast, negative) {
4
14
  if (!ast)
5
15
  return;
6
- if (ast instanceof ComparisonExpression) {
7
- const left = ast.left instanceof QualifiedIdentifier
8
- ? ast.left.value
9
- : transformFilter(ast.left);
10
- const right = transformFilter(ast.right);
11
- switch (ast.op) {
12
- case '=':
16
+ if (ast instanceof OpraFilter.QualifiedIdentifier) {
17
+ return ast.value;
18
+ }
19
+ if (ast instanceof OpraFilter.NumberLiteral ||
20
+ ast instanceof OpraFilter.StringLiteral ||
21
+ ast instanceof OpraFilter.BooleanLiteral ||
22
+ ast instanceof OpraFilter.NullLiteral ||
23
+ ast instanceof OpraFilter.DateLiteral ||
24
+ ast instanceof OpraFilter.TimeLiteral) {
25
+ return ast.value;
26
+ }
27
+ if (ast instanceof OpraFilter.ArrayExpression) {
28
+ return ast.items.map(x => transformFilter(x, negative));
29
+ }
30
+ if (ast instanceof OpraFilter.NegativeExpression) {
31
+ return transformFilter(ast.expression, !negative);
32
+ }
33
+ if (ast instanceof OpraFilter.LogicalExpression) {
34
+ if (ast.op === 'or')
35
+ return { $or: ast.items.map(x => transformFilter(x, negative)) };
36
+ return { $and: ast.items.map(x => transformFilter(x, negative)) };
37
+ }
38
+ if (ast instanceof OpraFilter.ParenthesizedExpression) {
39
+ return transformFilter(ast.expression, negative);
40
+ }
41
+ if (ast instanceof OpraFilter.ComparisonExpression) {
42
+ const left = transformFilter(ast.left, negative);
43
+ if (ast.right instanceof OpraFilter.QualifiedIdentifier) {
44
+ const op = opMap[ast.op];
45
+ if (op)
46
+ return { $expr: { [op]: ["$" + left, "$" + ast.right.value] } };
47
+ /* istanbul ignore next */
48
+ throw new Error(`Invalid filter query.`);
49
+ }
50
+ let right = transformFilter(ast.right);
51
+ if (right == null) {
52
+ const op = ast.op === '='
53
+ ? (negative ? '!=' : '=')
54
+ : (negative ? '=' : '!=');
55
+ if (op === '=')
56
+ return { $or: [{ [left]: null }, { [left]: { $exists: false } }] };
57
+ if (op === '!=')
58
+ return { $and: [{ $ne: { [left]: null } }, { [left]: { $exists: true } }] };
59
+ }
60
+ const mngOp = opMap[ast.op];
61
+ if (mngOp) {
62
+ if (ast.op === 'in' || ast.op === '!in')
63
+ right = Array.isArray(right) ? right : [right];
64
+ if (ast.op === '=' && !negative)
13
65
  return { [left]: right };
14
- case '!=':
15
- return { [left]: { $ne: right } };
16
- case '>':
17
- return { [left]: { $gt: right } };
18
- case '>=':
19
- return { [left]: { $gte: right } };
20
- case '<':
21
- return { [left]: { $lt: right } };
22
- case '<=':
23
- return { [left]: { $lte: right } };
24
- case 'in':
25
- return { [left]: { $in: Array.isArray(right) ? right : [right] } };
26
- case '!in':
27
- return { [left]: { $nin: Array.isArray(right) ? right : [right] } };
66
+ return { [left]: wrapNot({ [mngOp]: right }, negative) };
67
+ }
68
+ switch (ast.op) {
28
69
  case 'like':
29
70
  return {
30
- [left]: {
71
+ [left]: wrapNot({
31
72
  $text: {
32
73
  $search: '\\"' + right.replace(/\\"/, '"') + '\\"',
33
74
  $caseSensitive: true
34
75
  }
35
- }
76
+ }, negative)
36
77
  };
37
78
  case 'ilike':
38
79
  return {
39
- [left]: {
80
+ [left]: wrapNot({
40
81
  $text: {
41
82
  $search: '\\"' + right.replace(/\\"/, '"') + '\\"'
42
83
  }
43
- }
84
+ }, negative)
44
85
  };
45
86
  case '!like':
46
87
  return {
47
- [left]: {
48
- $not: {
49
- $text: {
50
- $search: '\\"' + right.replace(/\\"/, '"') + '\\"',
51
- $caseSensitive: true
52
- }
88
+ [left]: wrapNot({
89
+ $text: {
90
+ $search: '\\"' + right.replace(/\\"/, '"') + '\\"',
91
+ $caseSensitive: true
53
92
  }
54
- }
93
+ }, !negative)
55
94
  };
56
95
  case '!ilike':
57
96
  return {
58
- [left]: {
59
- $not: {
60
- $text: {
61
- $search: '\\"' + right.replace(/\\"/, '"') + '\\"'
62
- }
97
+ [left]: wrapNot({
98
+ $text: {
99
+ $search: '\\"' + right.replace(/\\"/, '"') + '\\"'
63
100
  }
64
- }
101
+ }, !negative)
65
102
  };
66
- default:
67
- throw new Error(`ComparisonExpression operator (${ast.op}) not implemented yet`);
68
103
  }
104
+ throw new Error(`Unimplemented ComparisonExpression operation (right side is ${ast.right.kind})`);
69
105
  }
70
- if (ast instanceof QualifiedIdentifier) {
71
- return ast.value;
72
- }
73
- if (ast instanceof NumberLiteral ||
74
- ast instanceof StringLiteral ||
75
- ast instanceof BooleanLiteral ||
76
- ast instanceof NullLiteral ||
77
- ast instanceof DateLiteral ||
78
- ast instanceof TimeLiteral) {
79
- return ast.value;
80
- }
81
- if (ast instanceof ArrayExpression) {
82
- return ast.items.map(transformFilter);
83
- }
84
- if (ast instanceof LogicalExpression) {
85
- if (ast.op === 'or')
86
- return { $or: ast.items.map(transformFilter) };
87
- return { $and: ast.items.map(transformFilter) };
88
- }
89
- if (ast instanceof ParenthesesExpression) {
90
- return transformFilter(ast.expression);
91
- }
92
- throw new Error(`${ast.type} is not implemented yet`);
106
+ throw new Error(`${ast.kind} is not implemented yet`);
93
107
  }
108
+ const wrapNot = (o, negative) => negative ? { $not: o } : o;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/mongodb",
3
- "version": "0.17.1",
3
+ "version": "0.17.3",
4
4
  "description": "Opra MongoDB adapter package",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -28,13 +28,13 @@
28
28
  "lodash": "^4.17.21"
29
29
  },
30
30
  "devDependencies": {
31
- "@faker-js/faker": "^7.6.0",
32
- "mongodb": "^5.3.0",
33
- "ts-gems": "^2.3.0"
31
+ "@faker-js/faker": "^8.0.0",
32
+ "mongodb": "^5.5.0",
33
+ "ts-gems": "^2.4.0"
34
34
  },
35
35
  "peerDependencies": {
36
- "@opra/common": "^0.17.1",
37
- "@opra/core": "^0.17.1",
36
+ "@opra/common": "^0.17.3",
37
+ "@opra/core": "^0.17.3",
38
38
  "mongodb": ">=4.x.x"
39
39
  },
40
40
  "type": "module",
@@ -1,14 +1,19 @@
1
1
  import mongodb from 'mongodb';
2
+ import { Maybe } from 'ts-gems';
2
3
  import { PartialOutput } from '@opra/common';
3
- import { RequestContext } from '@opra/core';
4
+ import { CollectionResourceBase, RequestContext } from '@opra/core';
4
5
  import { MongoEntityService } from './mongo-entity-service.js';
5
- export declare abstract class MongoCollectionResource<T extends mongodb.Document, TOutput = PartialOutput<T>> {
6
- defaultLimit: number;
6
+ export declare namespace MongoCollectionResource {
7
+ interface Options extends CollectionResourceBase.Options {
8
+ }
9
+ }
10
+ export declare abstract class MongoCollectionResource<T extends mongodb.Document, TOutput = PartialOutput<T>> extends CollectionResourceBase {
11
+ constructor(options?: MongoCollectionResource.Options);
7
12
  create(ctx: RequestContext): Promise<TOutput>;
8
13
  delete(ctx: RequestContext): Promise<number>;
9
14
  deleteMany(ctx: RequestContext): Promise<number>;
10
- get(ctx: RequestContext): Promise<TOutput>;
11
- update(ctx: RequestContext): Promise<TOutput>;
15
+ get(ctx: RequestContext): Promise<Maybe<TOutput>>;
16
+ update(ctx: RequestContext): Promise<Maybe<TOutput>>;
12
17
  updateMany(ctx: RequestContext): Promise<number>;
13
18
  findMany(ctx: RequestContext): Promise<TOutput[]>;
14
19
  abstract getService(ctx: RequestContext): MongoEntityService<T, TOutput> | Promise<MongoEntityService<T, TOutput>>;
@@ -8,11 +8,12 @@ export declare namespace MongoEntityService {
8
8
  }
9
9
  }
10
10
  export declare class MongoEntityService<T extends mongodb.Document, TOutput = PartialOutput<T>> {
11
- readonly collectionName: string;
11
+ protected _collectionName: string;
12
12
  context: RequestContext;
13
13
  defaultLimit: number;
14
14
  db?: mongodb.Db;
15
15
  session?: mongodb.ClientSession;
16
+ constructor(options?: MongoEntityService.Options);
16
17
  constructor(collectionName: string, options?: MongoEntityService.Options);
17
18
  count(filter?: mongodb.Filter<T>, options?: mongodb.CountOptions): Promise<number>;
18
19
  deleteOne(filter?: mongodb.Filter<T>, options?: mongodb.DeleteOptions): Promise<number>;
@@ -25,6 +26,8 @@ export declare class MongoEntityService<T extends mongodb.Document, TOutput = Pa
25
26
  with(context: RequestContext, db?: mongodb.Db, session?: mongodb.ClientSession): MongoEntityService<T, TOutput>;
26
27
  protected _onError(error: unknown): Promise<void>;
27
28
  protected getDatabase(): mongodb.Db | Promise<mongodb.Db>;
29
+ protected getCollection(db: mongodb.Db): Promise<mongodb.Collection<T>>;
30
+ protected getCollectionName(): string;
28
31
  protected onError?(error: unknown): void | Promise<void>;
29
- protected onTransformRow?(row: mongodb.WithId<T>): mongodb.WithId<T>;
32
+ protected transformData?(row: TOutput): TOutput;
30
33
  }
@@ -1,4 +1,5 @@
1
1
  import mongodb from 'mongodb';
2
+ import { Maybe } from 'ts-gems';
2
3
  import { PartialOutput } from '@opra/common';
3
4
  import { RequestContext } from '@opra/core';
4
5
  import { MongoEntityService } from './mongo-entity-service.js';
@@ -6,7 +7,7 @@ export declare abstract class MongoSingletonResource<T extends mongodb.Document,
6
7
  defaultLimit: number;
7
8
  create(ctx: RequestContext): Promise<TOutput>;
8
9
  delete(ctx: RequestContext): Promise<number>;
9
- get(ctx: RequestContext): Promise<TOutput>;
10
- update(ctx: RequestContext): Promise<TOutput>;
10
+ get(ctx: RequestContext): Promise<Maybe<TOutput>>;
11
+ update(ctx: RequestContext): Promise<Maybe<TOutput>>;
11
12
  abstract getService(ctx: RequestContext): MongoEntityService<T, TOutput> | Promise<MongoEntityService<T, TOutput>>;
12
13
  }
@@ -1,2 +1,3 @@
1
- import { Expression } from '@opra/common';
2
- export default function transformFilter(str: string | Expression | undefined): any;
1
+ import '@opra/core';
2
+ import { OpraFilter } from '@opra/common';
3
+ export default function transformFilter(ast: OpraFilter.Expression | undefined, negative?: boolean): any;