@steedos/odata-v4-mongodb 2.5.3-beta.2 → 2.5.3-beta.20
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 +85 -85
- package/package.json +3 -3
- package/src/index.ts +40 -40
- package/src/visitor.ts +312 -312
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.
|
|
3
|
+
"version": "2.5.3-beta.20",
|
|
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.
|
|
29
|
+
"@steedos/odata-v4-parser": "2.5.3-beta.20",
|
|
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": "
|
|
36
|
+
"gitHead": "32606d2d07b6e53a4d6c8ba079e6356f5a2dbb75"
|
|
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
|
+
}
|