@steedos/odata-v4-mongodb 2.5.3-beta.11 → 2.5.3-beta.12

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 CHANGED
@@ -1,85 +1,85 @@
1
- <!--
2
- * @Author: baozhoutao@steedos.com
3
- * @Date: 2022-07-10 15:44:34
4
- * @LastEditors: baozhoutao@steedos.com
5
- * @LastEditTime: 2022-07-10 15:57:25
6
- * @Description:
7
- -->
8
- # OData V4 Service modules - MongoDB Connector
9
-
10
- Service OData v4 requests from a MongoDB data store.
11
-
12
- ## Synopsis
13
- The OData V4 MongoDB Connector provides functionality to convert the various types of OData segments
14
- into MongoDB query objects, that you can execute over a MongoDB database.
15
-
16
- ## Potential usage scenarios
17
-
18
- - Create high speed, standard compliant data sharing APIs
19
-
20
- ## Usage as server - TypeScript
21
- ```javascript
22
- import { createFilter } from '@steedos/odata-v4-mongodb'
23
-
24
- //example request: GET /api/products?$filter=category/id eq 5 or color eq 'Red'
25
- app.get("/api/products", (req: Request, res: Response) => {
26
- const filter = createFilter(req.query.$filter);
27
- // collection instance from MongoDB Node.JS Driver
28
- collection.find(filter, function(err, data){
29
- res.json({
30
- '@odata.context': req.protocol + '://' + req.get('host') + '/api/$metadata#products',
31
- value: data
32
- });
33
- });
34
- });
35
- ```
36
-
37
- ## Usage ES5
38
- ```javascript
39
- var createFilter = require('@steedos/odata-v4-mongodb').createFilter;
40
-
41
- app.get("/api/products", function(req, res) {
42
- var filter = createFilter(req.query.$filter);
43
- // collection instance from MongoDB Node.JS Driver
44
- collection.find(filter, function(err, data){
45
- res.json({
46
- '@odata.context': req.protocol + '://' + req.get('host') + '/api/$metadata#products',
47
- value: data
48
- });
49
- });
50
- })
51
- ```
52
-
53
- ## Supported OData segments
54
-
55
- For now **$filter**, **$select**, **$skip** and **$top**
56
-
57
- Support for **$expand** is next.
58
-
59
- ### Supported $filter expressions
60
-
61
- The [OData v4 Parser](https://www.npmjs.com/package/odata-v4-parser) layer supports 100% of the specification.
62
- The Connector is supporting basic MongoDB queries.
63
-
64
- *We are into creating a comprehensive feature availability chart for V1 release*
65
-
66
- √ expression 5.1.1.6.1: NullValue eq null
67
- √ expression 5.1.1.6.1: TrueValue eq true
68
- √ expression 5.1.1.6.1: FalseValue eq false
69
- √ expression 5.1.1.6.1: IntegerValue lt -128
70
- √ expression 5.1.1.6.1: DecimalValue eq 34.95
71
- √ expression 5.1.1.6.1: StringValue eq 'Say Hello,then go'
72
- √ expression 5.1.1.6.1: DurationValue eq duration'P12DT23H59M59.999999999999S'
73
- √ expression 5.1.1.6.1: DateValue eq 2012-12-03
74
- √ expression 5.1.1.6.1: DateTimeOffsetValue eq 2012-12-03T07:16:23Z
75
- √ expression 5.1.1.6.1: GuidValue eq 01234567-89ab-cdef-0123-456789abcdef
76
- √ expression 5.1.1.6.1: Int64Value eq 0
77
- √ expression 5.1.1.6.1: A eq INF
78
- √ expression 5.1.1.6.1: A eq 0.31415926535897931e1
79
- √ expression 5.1.1.1.2: A ne 1
80
- √ expression 5.1.1.1.3: A gt 2
81
- √ expression 5.1.1.1.4: A ge 3
82
- √ expression 5.1.1.1.5: A lt 2
83
- √ expression 5.1.1.1.6: A le 2
84
- √ expression: A/b eq 1
85
- √ expression 5.1.1.3: (A/b eq 2) or (B/c lt 4) and ((E gt 5) or (E lt -1))
1
+ <!--
2
+ * @Author: baozhoutao@steedos.com
3
+ * @Date: 2022-07-10 15:44:34
4
+ * @LastEditors: baozhoutao@steedos.com
5
+ * @LastEditTime: 2022-07-10 15:57:25
6
+ * @Description:
7
+ -->
8
+ # OData V4 Service modules - MongoDB Connector
9
+
10
+ Service OData v4 requests from a MongoDB data store.
11
+
12
+ ## Synopsis
13
+ The OData V4 MongoDB Connector provides functionality to convert the various types of OData segments
14
+ into MongoDB query objects, that you can execute over a MongoDB database.
15
+
16
+ ## Potential usage scenarios
17
+
18
+ - Create high speed, standard compliant data sharing APIs
19
+
20
+ ## Usage as server - TypeScript
21
+ ```javascript
22
+ import { createFilter } from '@steedos/odata-v4-mongodb'
23
+
24
+ //example request: GET /api/products?$filter=category/id eq 5 or color eq 'Red'
25
+ app.get("/api/products", (req: Request, res: Response) => {
26
+ const filter = createFilter(req.query.$filter);
27
+ // collection instance from MongoDB Node.JS Driver
28
+ collection.find(filter, function(err, data){
29
+ res.json({
30
+ '@odata.context': req.protocol + '://' + req.get('host') + '/api/$metadata#products',
31
+ value: data
32
+ });
33
+ });
34
+ });
35
+ ```
36
+
37
+ ## Usage ES5
38
+ ```javascript
39
+ var createFilter = require('@steedos/odata-v4-mongodb').createFilter;
40
+
41
+ app.get("/api/products", function(req, res) {
42
+ var filter = createFilter(req.query.$filter);
43
+ // collection instance from MongoDB Node.JS Driver
44
+ collection.find(filter, function(err, data){
45
+ res.json({
46
+ '@odata.context': req.protocol + '://' + req.get('host') + '/api/$metadata#products',
47
+ value: data
48
+ });
49
+ });
50
+ })
51
+ ```
52
+
53
+ ## Supported OData segments
54
+
55
+ For now **$filter**, **$select**, **$skip** and **$top**
56
+
57
+ Support for **$expand** is next.
58
+
59
+ ### Supported $filter expressions
60
+
61
+ The [OData v4 Parser](https://www.npmjs.com/package/odata-v4-parser) layer supports 100% of the specification.
62
+ The Connector is supporting basic MongoDB queries.
63
+
64
+ *We are into creating a comprehensive feature availability chart for V1 release*
65
+
66
+ √ expression 5.1.1.6.1: NullValue eq null
67
+ √ expression 5.1.1.6.1: TrueValue eq true
68
+ √ expression 5.1.1.6.1: FalseValue eq false
69
+ √ expression 5.1.1.6.1: IntegerValue lt -128
70
+ √ expression 5.1.1.6.1: DecimalValue eq 34.95
71
+ √ expression 5.1.1.6.1: StringValue eq 'Say Hello,then go'
72
+ √ expression 5.1.1.6.1: DurationValue eq duration'P12DT23H59M59.999999999999S'
73
+ √ expression 5.1.1.6.1: DateValue eq 2012-12-03
74
+ √ expression 5.1.1.6.1: DateTimeOffsetValue eq 2012-12-03T07:16:23Z
75
+ √ expression 5.1.1.6.1: GuidValue eq 01234567-89ab-cdef-0123-456789abcdef
76
+ √ expression 5.1.1.6.1: Int64Value eq 0
77
+ √ expression 5.1.1.6.1: A eq INF
78
+ √ expression 5.1.1.6.1: A eq 0.31415926535897931e1
79
+ √ expression 5.1.1.1.2: A ne 1
80
+ √ expression 5.1.1.1.3: A gt 2
81
+ √ expression 5.1.1.1.4: A ge 3
82
+ √ expression 5.1.1.1.5: A lt 2
83
+ √ expression 5.1.1.1.6: A le 2
84
+ √ expression: A/b eq 1
85
+ √ expression 5.1.1.3: (A/b eq 2) or (B/c lt 4) and ((E gt 5) or (E lt -1))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steedos/odata-v4-mongodb",
3
- "version": "2.5.3-beta.11",
3
+ "version": "2.5.3-beta.12",
4
4
  "description": "Service OData requests from a MongoDB data store",
5
5
  "main": "lib/index.js",
6
6
  "directories": {
@@ -26,12 +26,12 @@
26
26
  },
27
27
  "homepage": "https://github.com/jaystack/odata-v4-mongodb#readme",
28
28
  "dependencies": {
29
- "@steedos/odata-v4-parser": "2.5.3-beta.11",
29
+ "@steedos/odata-v4-parser": "2.5.3-beta.12",
30
30
  "odata-v4-literal": "^0.1.0"
31
31
  },
32
32
  "private": false,
33
33
  "publishConfig": {
34
34
  "access": "public"
35
35
  },
36
- "gitHead": "90a5790974f858323e534c321f96643da576d260"
36
+ "gitHead": "79e9dfbd0bf19a0979f29d2a055cf6e2ea847997"
37
37
  }
package/src/index.ts CHANGED
@@ -1,41 +1,41 @@
1
- /*
2
- * @Author: baozhoutao@steedos.com
3
- * @Date: 2022-07-10 15:44:34
4
- * @LastEditors: baozhoutao@steedos.com
5
- * @LastEditTime: 2022-07-10 17:09:22
6
- * @Description:
7
- */
8
- import { Visitor } from "./visitor"
9
- import { filter, query } from "@steedos/odata-v4-parser"
10
-
11
- /**
12
- * Creates MongoDB collection, query, projection, sort, skip and limit from an OData URI string
13
- * @param {string} queryString - An OData query string
14
- * @return {Visitor} Visitor instance object with collection, query, projection, sort, skip and limit
15
- * @example
16
- * const query = createQuery("$filter=Size eq 4&$orderby=Orders&$skip=10&$top=5");
17
- * collections[query.collection].find(query.query).project(query.projection).sort(query.sort).skip(query.skip).limit(query.limit).toArray(function(err, data){ ... });
18
- */
19
- export function createQuery(odataQuery:string);
20
- export function createQuery(odataQuery:any);
21
- export function createQuery(odataQuery:string | any){
22
- let ast:any = <any>(typeof odataQuery == "string" ? query(<string>odataQuery) : odataQuery);
23
- return new Visitor().Visit(ast);
24
- }
25
-
26
- /**
27
- * Creates a MongoDB query object from an OData filter expression string
28
- * @param {string} odataFilter - A filter expression in OData $filter format
29
- * @return {Object} MongoDB query object
30
- * @example
31
- * const filter = createFilter("Size eq 4 and Age gt 18");
32
- * collection.find(filter, function(err, data){ ... });
33
- */
34
- export function createFilter(odataFilter:string);
35
- export function createFilter(odataFilter:any);
36
- export function createFilter(odataFilter:string | any):Object{
37
- let context = { query: {} };
38
- let ast:any = <any>(typeof odataFilter == "string" ? filter(<string>odataFilter) : odataFilter);
39
- new Visitor().Visit(ast, context);
40
- return context.query;
1
+ /*
2
+ * @Author: baozhoutao@steedos.com
3
+ * @Date: 2022-07-10 15:44:34
4
+ * @LastEditors: baozhoutao@steedos.com
5
+ * @LastEditTime: 2022-07-10 17:09:22
6
+ * @Description:
7
+ */
8
+ import { Visitor } from "./visitor"
9
+ import { filter, query } from "@steedos/odata-v4-parser"
10
+
11
+ /**
12
+ * Creates MongoDB collection, query, projection, sort, skip and limit from an OData URI string
13
+ * @param {string} queryString - An OData query string
14
+ * @return {Visitor} Visitor instance object with collection, query, projection, sort, skip and limit
15
+ * @example
16
+ * const query = createQuery("$filter=Size eq 4&$orderby=Orders&$skip=10&$top=5");
17
+ * collections[query.collection].find(query.query).project(query.projection).sort(query.sort).skip(query.skip).limit(query.limit).toArray(function(err, data){ ... });
18
+ */
19
+ export function createQuery(odataQuery:string);
20
+ export function createQuery(odataQuery:any);
21
+ export function createQuery(odataQuery:string | any){
22
+ let ast:any = <any>(typeof odataQuery == "string" ? query(<string>odataQuery) : odataQuery);
23
+ return new Visitor().Visit(ast);
24
+ }
25
+
26
+ /**
27
+ * Creates a MongoDB query object from an OData filter expression string
28
+ * @param {string} odataFilter - A filter expression in OData $filter format
29
+ * @return {Object} MongoDB query object
30
+ * @example
31
+ * const filter = createFilter("Size eq 4 and Age gt 18");
32
+ * collection.find(filter, function(err, data){ ... });
33
+ */
34
+ export function createFilter(odataFilter:string);
35
+ export function createFilter(odataFilter:any);
36
+ export function createFilter(odataFilter:string | any):Object{
37
+ let context = { query: {} };
38
+ let ast:any = <any>(typeof odataFilter == "string" ? filter(<string>odataFilter) : odataFilter);
39
+ new Visitor().Visit(ast, context);
40
+ return context.query;
41
41
  }
package/src/visitor.ts CHANGED
@@ -1,312 +1,312 @@
1
- import { Literal } from "odata-v4-literal";
2
-
3
- export class Visitor{
4
- query: any
5
- sort: any
6
- skip: number
7
- limit: number
8
- projection: any
9
- collection: string
10
- navigationProperty: string
11
- includes:Visitor[]
12
- inlinecount: boolean
13
- ast:any
14
-
15
- constructor(){
16
- this.query = {};
17
- this.sort = {};
18
- this.projection = {};
19
- this.includes = [];
20
-
21
- let _ast;
22
- Object.defineProperty(this, "ast", {
23
- get: () => { return _ast; },
24
- set: (v) => { _ast = v; },
25
- enumerable: false
26
- });
27
- }
28
-
29
- Visit(node:any, context?:any){
30
- this.ast = this.ast || node;
31
- context = context || {};
32
-
33
- if (node){
34
- var visitor = this[`Visit${node.type}`];
35
- if (visitor) visitor.call(this, node, context);
36
- }
37
-
38
- return this;
39
- }
40
-
41
- protected VisitODataUri(node:any, context:any){
42
- this.Visit(node.value.resource, context);
43
- this.Visit(node.value.query, context);
44
- }
45
-
46
- protected VisitEntitySetName(node:any, context:any){
47
- this.collection = node.value.name;
48
- }
49
-
50
- protected VisitExpand(node: any, context: any) {
51
- var innerContexts:any = {};
52
- node.value.items.forEach((item) => {
53
- var expandPath = item.value.path.raw;
54
- var innerVisitor = this.includes.filter(v => v.navigationProperty === expandPath)[0];
55
- if (!innerVisitor){
56
- innerVisitor = new Visitor();
57
-
58
- innerContexts[expandPath] = {
59
- query: {},
60
- sort: {},
61
- projection: {},
62
- options: {}
63
- };
64
-
65
- this.includes.push(innerVisitor);
66
- }
67
-
68
- let innerContext:any = innerContexts[expandPath] || {};
69
- innerVisitor.Visit(item, innerContext);
70
-
71
- innerVisitor.query = innerContext.query || innerVisitor.query || {};
72
- innerVisitor.sort = innerContext.sort || innerVisitor.sort;
73
- innerVisitor.projection = innerContext.projection || innerVisitor.projection;
74
- });
75
- }
76
-
77
- protected VisitExpandItem(node: any, context: any) {
78
- this.Visit(node.value.path, context);
79
- node.value.options && node.value.options.forEach((item) => this.Visit(item, context));
80
- }
81
-
82
- protected VisitExpandPath(node: any, context: any) {
83
- this.navigationProperty = node.raw;
84
- }
85
-
86
- protected VisitQueryOptions(node:any, context:any){
87
- context.options = {};
88
- node.value.options.forEach((option) => this.Visit(option, context));
89
-
90
- this.query = context.query || {};
91
- delete context.query;
92
-
93
- this.sort = context.sort;
94
- delete context.sort;
95
- }
96
-
97
- protected VisitInlineCount(node:any, context:any){
98
- this.inlinecount = Literal.convert(node.value.value, node.value.raw);
99
- }
100
-
101
- protected VisitFilter(node:any, context:any){
102
- context.query = {};
103
- this.Visit(node.value, context);
104
- delete context.identifier;
105
- delete context.literal;
106
- }
107
-
108
- protected VisitOrderBy(node:any, context:any){
109
- context.sort = {};
110
- node.value.items.forEach((item) => this.Visit(item, context));
111
- }
112
-
113
- protected VisitSkip(node:any, context:any){
114
- this.skip = +node.value.raw;
115
- }
116
-
117
- protected VisitTop(node:any, context:any){
118
- this.limit = +node.value.raw;
119
- }
120
-
121
- protected VisitOrderByItem(node:any, context:any){
122
- this.Visit(node.value.expr, context);
123
- if (context.identifier) context.sort[context.identifier] = node.value.direction;
124
- delete context.identifier;
125
- delete context.literal;
126
- }
127
-
128
- protected VisitSelect(node:any, context:any){
129
- context.projection = {};
130
- node.value.items.forEach((item) => this.Visit(item, context));
131
-
132
- this.projection = context.projection;
133
- delete context.projection;
134
- }
135
-
136
- protected VisitSelectItem(node:any, context:any){
137
- context.projection[node.raw.replace(/\//g, '.')] = 1;
138
- }
139
-
140
- protected VisitAndExpression(node:any, context:any){
141
- var query = context.query;
142
- var leftQuery = {};
143
- context.query = leftQuery;
144
- this.Visit(node.value.left, context);
145
-
146
- var rightQuery = {};
147
- context.query = rightQuery;
148
- this.Visit(node.value.right, context);
149
-
150
- if (Object.keys(leftQuery).length > 0 && Object.keys(rightQuery).length > 0){
151
- query.$and = [leftQuery, rightQuery];
152
- }
153
- context.query = query;
154
- }
155
-
156
- protected VisitOrExpression(node:any, context:any){
157
- var query = context.query;
158
- var leftQuery = {};
159
- context.query = leftQuery;
160
- this.Visit(node.value.left, context);
161
-
162
- var rightQuery = {};
163
- context.query = rightQuery;
164
- this.Visit(node.value.right, context);
165
-
166
- if (Object.keys(leftQuery).length > 0 && Object.keys(rightQuery).length > 0){
167
- query.$or = [leftQuery, rightQuery];
168
- }
169
- context.query = query;
170
- }
171
-
172
- protected VisitBoolParenExpression(node:any, context:any){
173
- this.Visit(node.value, context);
174
- }
175
-
176
- protected VisitCommonExpression(node:any, context:any){
177
- this.Visit(node.value, context);
178
- }
179
-
180
- protected VisitFirstMemberExpression(node:any, context:any){
181
- this.Visit(node.value, context);
182
- }
183
-
184
- protected VisitMemberExpression(node:any, context:any){
185
- this.Visit(node.value, context);
186
- }
187
-
188
- protected VisitPropertyPathExpression(node:any, context:any){
189
- if (node.value.current && node.value.next){
190
- this.Visit(node.value.current, context);
191
- if (context.identifier) context.identifier += ".";
192
- this.Visit(node.value.next, context);
193
- }else this.Visit(node.value, context);
194
- }
195
-
196
- protected VisitSingleNavigationExpression(node:any, context:any){
197
- if (node.value.current && node.value.next){
198
- this.Visit(node.value.current, context);
199
- this.Visit(node.value.next, context);
200
- }else this.Visit(node.value, context);
201
- }
202
-
203
- protected VisitODataIdentifier(node:any, context:any){
204
- context.identifier = (context.identifier || "") + node.value.name;
205
- }
206
-
207
- protected VisitNotExpression(node:any, context:any){
208
- this.Visit(node.value, context);
209
- if (context.query){
210
- for (var prop in context.query){
211
- context.query[prop] = { $not: context.query[prop] };
212
- }
213
- }
214
- }
215
-
216
- protected VisitEqualsExpression(node:any, context:any){
217
- this.Visit(node.value.left, context);
218
- this.Visit(node.value.right, context);
219
-
220
- if (context.identifier) context.query[context.identifier] = context.literal;
221
- delete context.identifier;
222
- delete context.literal;
223
- }
224
-
225
- protected VisitNotEqualsExpression(node:any, context:any){
226
- this.Visit(node.value.left, context);
227
- this.Visit(node.value.right, context);
228
- if (context.identifier) context.query[context.identifier] = { $ne: context.literal };
229
- delete context.identifier;
230
- delete context.literal;
231
- }
232
-
233
- protected VisitInExpression(node:any, context:any){
234
- this.Visit(node.value.left, context);
235
- this.Visit(node.value.right, context);
236
- if (context.identifier)
237
- context.query[context.identifier] = {
238
- $in: JSON.parse(`[${node.value.right.raw.replace(/\'/g, "\"").slice(1).slice(0, -1)}]`)
239
- };
240
- delete context.identifier;
241
- delete context.literal;
242
- };
243
-
244
- protected VisitNotInExpression(node:any, context:any){
245
- this.Visit(node.value.left, context);
246
- this.Visit(node.value.right, context);
247
- if (context.identifier)
248
- context.query[context.identifier] = {
249
- $nin: JSON.parse(`[${node.value.right.raw.replace(/\'/g, "\"").slice(1).slice(0, -1)}]`)
250
- };
251
- delete context.identifier;
252
- delete context.literal;
253
- };
254
-
255
- protected VisitLesserThanExpression(node:any, context:any){
256
- this.Visit(node.value.left, context);
257
- this.Visit(node.value.right, context);
258
- if (context.identifier) context.query[context.identifier] = { $lt: context.literal };
259
- delete context.identifier;
260
- delete context.literal;
261
- }
262
-
263
- protected VisitLesserOrEqualsExpression(node:any, context:any){
264
- this.Visit(node.value.left, context);
265
- this.Visit(node.value.right, context);
266
- if (context.identifier) context.query[context.identifier] = { $lte: context.literal };
267
- delete context.identifier;
268
- delete context.literal;
269
- }
270
-
271
- protected VisitGreaterThanExpression(node:any, context:any){
272
- this.Visit(node.value.left, context);
273
- this.Visit(node.value.right, context);
274
- if (context.identifier) context.query[context.identifier] = { $gt: context.literal };
275
- delete context.identifier;
276
- delete context.literal;
277
- }
278
-
279
- protected VisitGreaterOrEqualsExpression(node:any, context:any){
280
- this.Visit(node.value.left, context);
281
- this.Visit(node.value.right, context);
282
- if (context.identifier) context.query[context.identifier] = { $gte: context.literal };
283
- delete context.identifier;
284
- delete context.literal;
285
- }
286
-
287
- protected VisitLiteral(node:any, context:any){
288
- context.literal = Literal.convert(node.value, node.raw);
289
- }
290
-
291
- protected VisitMethodCallExpression(node: any, context: any) {
292
- var method = node.value.method;
293
- (node.value.parameters || []).forEach(p => this.Visit(p, context));
294
- if (context.identifier) {
295
- switch (method) {
296
- case "contains":
297
- context.query[context.identifier] = new RegExp(context.literal, "gi");
298
- break;
299
- case "endswith":
300
- context.query[context.identifier] = new RegExp(context.literal + "$", "gi");
301
- break;
302
- case "startswith":
303
- context.query[context.identifier] = new RegExp("^" + context.literal, "gi");
304
- break;
305
- default:
306
- throw new Error("Method call not implemented.")
307
- }
308
- delete context.identifier;
309
- }
310
- }
311
-
312
- }
1
+ import { Literal } from "odata-v4-literal";
2
+
3
+ export class Visitor{
4
+ query: any
5
+ sort: any
6
+ skip: number
7
+ limit: number
8
+ projection: any
9
+ collection: string
10
+ navigationProperty: string
11
+ includes:Visitor[]
12
+ inlinecount: boolean
13
+ ast:any
14
+
15
+ constructor(){
16
+ this.query = {};
17
+ this.sort = {};
18
+ this.projection = {};
19
+ this.includes = [];
20
+
21
+ let _ast;
22
+ Object.defineProperty(this, "ast", {
23
+ get: () => { return _ast; },
24
+ set: (v) => { _ast = v; },
25
+ enumerable: false
26
+ });
27
+ }
28
+
29
+ Visit(node:any, context?:any){
30
+ this.ast = this.ast || node;
31
+ context = context || {};
32
+
33
+ if (node){
34
+ var visitor = this[`Visit${node.type}`];
35
+ if (visitor) visitor.call(this, node, context);
36
+ }
37
+
38
+ return this;
39
+ }
40
+
41
+ protected VisitODataUri(node:any, context:any){
42
+ this.Visit(node.value.resource, context);
43
+ this.Visit(node.value.query, context);
44
+ }
45
+
46
+ protected VisitEntitySetName(node:any, context:any){
47
+ this.collection = node.value.name;
48
+ }
49
+
50
+ protected VisitExpand(node: any, context: any) {
51
+ var innerContexts:any = {};
52
+ node.value.items.forEach((item) => {
53
+ var expandPath = item.value.path.raw;
54
+ var innerVisitor = this.includes.filter(v => v.navigationProperty === expandPath)[0];
55
+ if (!innerVisitor){
56
+ innerVisitor = new Visitor();
57
+
58
+ innerContexts[expandPath] = {
59
+ query: {},
60
+ sort: {},
61
+ projection: {},
62
+ options: {}
63
+ };
64
+
65
+ this.includes.push(innerVisitor);
66
+ }
67
+
68
+ let innerContext:any = innerContexts[expandPath] || {};
69
+ innerVisitor.Visit(item, innerContext);
70
+
71
+ innerVisitor.query = innerContext.query || innerVisitor.query || {};
72
+ innerVisitor.sort = innerContext.sort || innerVisitor.sort;
73
+ innerVisitor.projection = innerContext.projection || innerVisitor.projection;
74
+ });
75
+ }
76
+
77
+ protected VisitExpandItem(node: any, context: any) {
78
+ this.Visit(node.value.path, context);
79
+ node.value.options && node.value.options.forEach((item) => this.Visit(item, context));
80
+ }
81
+
82
+ protected VisitExpandPath(node: any, context: any) {
83
+ this.navigationProperty = node.raw;
84
+ }
85
+
86
+ protected VisitQueryOptions(node:any, context:any){
87
+ context.options = {};
88
+ node.value.options.forEach((option) => this.Visit(option, context));
89
+
90
+ this.query = context.query || {};
91
+ delete context.query;
92
+
93
+ this.sort = context.sort;
94
+ delete context.sort;
95
+ }
96
+
97
+ protected VisitInlineCount(node:any, context:any){
98
+ this.inlinecount = Literal.convert(node.value.value, node.value.raw);
99
+ }
100
+
101
+ protected VisitFilter(node:any, context:any){
102
+ context.query = {};
103
+ this.Visit(node.value, context);
104
+ delete context.identifier;
105
+ delete context.literal;
106
+ }
107
+
108
+ protected VisitOrderBy(node:any, context:any){
109
+ context.sort = {};
110
+ node.value.items.forEach((item) => this.Visit(item, context));
111
+ }
112
+
113
+ protected VisitSkip(node:any, context:any){
114
+ this.skip = +node.value.raw;
115
+ }
116
+
117
+ protected VisitTop(node:any, context:any){
118
+ this.limit = +node.value.raw;
119
+ }
120
+
121
+ protected VisitOrderByItem(node:any, context:any){
122
+ this.Visit(node.value.expr, context);
123
+ if (context.identifier) context.sort[context.identifier] = node.value.direction;
124
+ delete context.identifier;
125
+ delete context.literal;
126
+ }
127
+
128
+ protected VisitSelect(node:any, context:any){
129
+ context.projection = {};
130
+ node.value.items.forEach((item) => this.Visit(item, context));
131
+
132
+ this.projection = context.projection;
133
+ delete context.projection;
134
+ }
135
+
136
+ protected VisitSelectItem(node:any, context:any){
137
+ context.projection[node.raw.replace(/\//g, '.')] = 1;
138
+ }
139
+
140
+ protected VisitAndExpression(node:any, context:any){
141
+ var query = context.query;
142
+ var leftQuery = {};
143
+ context.query = leftQuery;
144
+ this.Visit(node.value.left, context);
145
+
146
+ var rightQuery = {};
147
+ context.query = rightQuery;
148
+ this.Visit(node.value.right, context);
149
+
150
+ if (Object.keys(leftQuery).length > 0 && Object.keys(rightQuery).length > 0){
151
+ query.$and = [leftQuery, rightQuery];
152
+ }
153
+ context.query = query;
154
+ }
155
+
156
+ protected VisitOrExpression(node:any, context:any){
157
+ var query = context.query;
158
+ var leftQuery = {};
159
+ context.query = leftQuery;
160
+ this.Visit(node.value.left, context);
161
+
162
+ var rightQuery = {};
163
+ context.query = rightQuery;
164
+ this.Visit(node.value.right, context);
165
+
166
+ if (Object.keys(leftQuery).length > 0 && Object.keys(rightQuery).length > 0){
167
+ query.$or = [leftQuery, rightQuery];
168
+ }
169
+ context.query = query;
170
+ }
171
+
172
+ protected VisitBoolParenExpression(node:any, context:any){
173
+ this.Visit(node.value, context);
174
+ }
175
+
176
+ protected VisitCommonExpression(node:any, context:any){
177
+ this.Visit(node.value, context);
178
+ }
179
+
180
+ protected VisitFirstMemberExpression(node:any, context:any){
181
+ this.Visit(node.value, context);
182
+ }
183
+
184
+ protected VisitMemberExpression(node:any, context:any){
185
+ this.Visit(node.value, context);
186
+ }
187
+
188
+ protected VisitPropertyPathExpression(node:any, context:any){
189
+ if (node.value.current && node.value.next){
190
+ this.Visit(node.value.current, context);
191
+ if (context.identifier) context.identifier += ".";
192
+ this.Visit(node.value.next, context);
193
+ }else this.Visit(node.value, context);
194
+ }
195
+
196
+ protected VisitSingleNavigationExpression(node:any, context:any){
197
+ if (node.value.current && node.value.next){
198
+ this.Visit(node.value.current, context);
199
+ this.Visit(node.value.next, context);
200
+ }else this.Visit(node.value, context);
201
+ }
202
+
203
+ protected VisitODataIdentifier(node:any, context:any){
204
+ context.identifier = (context.identifier || "") + node.value.name;
205
+ }
206
+
207
+ protected VisitNotExpression(node:any, context:any){
208
+ this.Visit(node.value, context);
209
+ if (context.query){
210
+ for (var prop in context.query){
211
+ context.query[prop] = { $not: context.query[prop] };
212
+ }
213
+ }
214
+ }
215
+
216
+ protected VisitEqualsExpression(node:any, context:any){
217
+ this.Visit(node.value.left, context);
218
+ this.Visit(node.value.right, context);
219
+
220
+ if (context.identifier) context.query[context.identifier] = context.literal;
221
+ delete context.identifier;
222
+ delete context.literal;
223
+ }
224
+
225
+ protected VisitNotEqualsExpression(node:any, context:any){
226
+ this.Visit(node.value.left, context);
227
+ this.Visit(node.value.right, context);
228
+ if (context.identifier) context.query[context.identifier] = { $ne: context.literal };
229
+ delete context.identifier;
230
+ delete context.literal;
231
+ }
232
+
233
+ protected VisitInExpression(node:any, context:any){
234
+ this.Visit(node.value.left, context);
235
+ this.Visit(node.value.right, context);
236
+ if (context.identifier)
237
+ context.query[context.identifier] = {
238
+ $in: JSON.parse(`[${node.value.right.raw.replace(/\'/g, "\"").slice(1).slice(0, -1)}]`)
239
+ };
240
+ delete context.identifier;
241
+ delete context.literal;
242
+ };
243
+
244
+ protected VisitNotInExpression(node:any, context:any){
245
+ this.Visit(node.value.left, context);
246
+ this.Visit(node.value.right, context);
247
+ if (context.identifier)
248
+ context.query[context.identifier] = {
249
+ $nin: JSON.parse(`[${node.value.right.raw.replace(/\'/g, "\"").slice(1).slice(0, -1)}]`)
250
+ };
251
+ delete context.identifier;
252
+ delete context.literal;
253
+ };
254
+
255
+ protected VisitLesserThanExpression(node:any, context:any){
256
+ this.Visit(node.value.left, context);
257
+ this.Visit(node.value.right, context);
258
+ if (context.identifier) context.query[context.identifier] = { $lt: context.literal };
259
+ delete context.identifier;
260
+ delete context.literal;
261
+ }
262
+
263
+ protected VisitLesserOrEqualsExpression(node:any, context:any){
264
+ this.Visit(node.value.left, context);
265
+ this.Visit(node.value.right, context);
266
+ if (context.identifier) context.query[context.identifier] = { $lte: context.literal };
267
+ delete context.identifier;
268
+ delete context.literal;
269
+ }
270
+
271
+ protected VisitGreaterThanExpression(node:any, context:any){
272
+ this.Visit(node.value.left, context);
273
+ this.Visit(node.value.right, context);
274
+ if (context.identifier) context.query[context.identifier] = { $gt: context.literal };
275
+ delete context.identifier;
276
+ delete context.literal;
277
+ }
278
+
279
+ protected VisitGreaterOrEqualsExpression(node:any, context:any){
280
+ this.Visit(node.value.left, context);
281
+ this.Visit(node.value.right, context);
282
+ if (context.identifier) context.query[context.identifier] = { $gte: context.literal };
283
+ delete context.identifier;
284
+ delete context.literal;
285
+ }
286
+
287
+ protected VisitLiteral(node:any, context:any){
288
+ context.literal = Literal.convert(node.value, node.raw);
289
+ }
290
+
291
+ protected VisitMethodCallExpression(node: any, context: any) {
292
+ var method = node.value.method;
293
+ (node.value.parameters || []).forEach(p => this.Visit(p, context));
294
+ if (context.identifier) {
295
+ switch (method) {
296
+ case "contains":
297
+ context.query[context.identifier] = new RegExp(context.literal, "gi");
298
+ break;
299
+ case "endswith":
300
+ context.query[context.identifier] = new RegExp(context.literal + "$", "gi");
301
+ break;
302
+ case "startswith":
303
+ context.query[context.identifier] = new RegExp("^" + context.literal, "gi");
304
+ break;
305
+ default:
306
+ throw new Error("Method call not implemented.")
307
+ }
308
+ delete context.identifier;
309
+ }
310
+ }
311
+
312
+ }