breeze-client 2.2.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +83 -63
- package/adapters/abstract-data-service-adapter.d.ts +120 -0
- package/adapters/abstract-data-service-adapter.js +395 -0
- package/adapters/abstract-data-service-adapter.js.map +1 -0
- package/adapters/adapter-ajax-fetch.d.ts +12 -0
- package/adapters/adapter-ajax-fetch.js +13 -0
- package/adapters/adapter-ajax-fetch.js.map +1 -0
- package/{adapter-ajax-post → adapters}/adapter-ajax-post.d.ts +40 -36
- package/{fesm2022/breeze-client-adapter-ajax-post.mjs → adapters/adapter-ajax-post.js} +80 -82
- package/adapters/adapter-ajax-post.js.map +1 -0
- package/{adapter-ajax-fetch → adapters}/adapter-core.d.ts +4 -4
- package/{fesm2022/breeze-client-adapter-uri-builder-json.mjs → adapters/adapter-core.js} +48 -91
- package/adapters/adapter-core.js.map +1 -0
- package/adapters/adapter-data-service-webapi.d.ts +9 -0
- package/{fesm2022/breeze-client-adapter-data-service-webapi.mjs → adapters/adapter-data-service-webapi.js} +105 -107
- package/adapters/adapter-data-service-webapi.js.map +1 -0
- package/adapters/adapter-model-library-backing-store.d.ts +32 -0
- package/adapters/adapter-model-library-backing-store.js +321 -0
- package/adapters/adapter-model-library-backing-store.js.map +1 -0
- package/adapters/adapter-uri-builder-json.d.ts +11 -0
- package/adapters/adapter-uri-builder-json.js +40 -0
- package/adapters/adapter-uri-builder-json.js.map +1 -0
- package/adapters/http.d.ts +52 -0
- package/adapters/http.js +202 -0
- package/adapters/http.js.map +1 -0
- package/angular/adapter-angular-httpclient.d.ts +21 -0
- package/angular/adapter-angular-httpclient.js +70 -0
- package/angular/adapter-angular-httpclient.js.map +1 -0
- package/breeze.d.ts +215 -0
- package/breeze.js +141 -0
- package/breeze.js.map +1 -0
- package/config/config.d.ts +212 -0
- package/config/config.js +392 -0
- package/config/config.js.map +1 -0
- package/config/configure.d.ts +66 -0
- package/config/configure.js +46 -0
- package/config/configure.js.map +1 -0
- package/config/default-adapters.d.ts +1 -0
- package/config/default-adapters.js +40 -0
- package/config/default-adapters.js.map +1 -0
- package/config/interface-registry.d.ts +191 -0
- package/config/interface-registry.js +5 -0
- package/config/interface-registry.js.map +1 -0
- package/{src → core}/assert-param.d.ts +52 -60
- package/core/assert-param.js +390 -0
- package/core/assert-param.js.map +1 -0
- package/core/core.d.ts +285 -0
- package/core/core.js +772 -0
- package/core/core.js.map +1 -0
- package/core/enum.d.ts +116 -0
- package/core/enum.js +167 -0
- package/core/enum.js.map +1 -0
- package/core/event.d.ts +169 -0
- package/core/event.js +338 -0
- package/core/event.js.map +1 -0
- package/entity/array.d.ts +3 -0
- package/entity/array.js +4 -0
- package/entity/array.js.map +1 -0
- package/entity/complex-array.d.ts +17 -0
- package/entity/complex-array.js +104 -0
- package/entity/complex-array.js.map +1 -0
- package/entity/default-property-interceptor.d.ts +1 -0
- package/entity/default-property-interceptor.js +496 -0
- package/entity/default-property-interceptor.js.map +1 -0
- package/{src → entity}/entity-action.d.ts +36 -42
- package/entity/entity-action.js +45 -0
- package/entity/entity-action.js.map +1 -0
- package/entity/entity-aspect.d.ts +457 -0
- package/entity/entity-aspect.js +1262 -0
- package/entity/entity-aspect.js.map +1 -0
- package/entity/entity-base.d.ts +55 -0
- package/entity/entity-base.js +41 -0
- package/entity/entity-base.js.map +1 -0
- package/entity/entity-group.d.ts +1 -0
- package/entity/entity-group.js +224 -0
- package/entity/entity-group.js.map +1 -0
- package/entity/entity-key.d.ts +95 -0
- package/entity/entity-key.js +189 -0
- package/entity/entity-key.js.map +1 -0
- package/{src → entity}/entity-state.d.ts +112 -84
- package/entity/entity-state.js +135 -0
- package/entity/entity-state.js.map +1 -0
- package/entity/key-generator.d.ts +57 -0
- package/entity/key-generator.js +155 -0
- package/entity/key-generator.js.map +1 -0
- package/entity/observable-array.d.ts +25 -0
- package/entity/observable-array.js +230 -0
- package/entity/observable-array.js.map +1 -0
- package/entity/primitive-array.d.ts +12 -0
- package/entity/primitive-array.js +71 -0
- package/entity/primitive-array.js.map +1 -0
- package/entity/relation-array.d.ts +24 -0
- package/entity/relation-array.js +176 -0
- package/entity/relation-array.js.map +1 -0
- package/entity/unattached-children-map.d.ts +1 -0
- package/entity/unattached-children-map.js +105 -0
- package/entity/unattached-children-map.js.map +1 -0
- package/generate-entity-classes.js +1176 -0
- package/manager/entity-manager.d.ts +801 -0
- package/manager/entity-manager.js +2395 -0
- package/manager/entity-manager.js.map +1 -0
- package/{src → manager}/save-options.d.ts +48 -42
- package/manager/save-options.js +51 -0
- package/manager/save-options.js.map +1 -0
- package/{src → metadata}/data-service.d.ts +229 -184
- package/metadata/data-service.js +246 -0
- package/metadata/data-service.js.map +1 -0
- package/metadata/data-type.d.ts +122 -0
- package/metadata/data-type.js +465 -0
- package/metadata/data-type.js.map +1 -0
- package/metadata/entity-metadata.d.ts +1193 -0
- package/metadata/entity-metadata.js +2502 -0
- package/metadata/entity-metadata.js.map +1 -0
- package/{src → metadata}/local-query-comparison-options.d.ts +65 -59
- package/metadata/local-query-comparison-options.js +69 -0
- package/metadata/local-query-comparison-options.js.map +1 -0
- package/{src → metadata}/naming-convention.d.ts +75 -72
- package/metadata/naming-convention.js +97 -0
- package/metadata/naming-convention.js.map +1 -0
- package/mixins/mixin-get-entity-graph.d.ts +63 -0
- package/{fesm2022/breeze-client-mixin-get-entity-graph.mjs → mixins/mixin-get-entity-graph.js} +331 -253
- package/mixins/mixin-get-entity-graph.js.map +1 -0
- package/mixins/mixin-save-queuing.d.ts +98 -0
- package/{fesm2022/breeze-client-mixin-save-queuing.mjs → mixins/mixin-save-queuing.js} +493 -365
- package/mixins/mixin-save-queuing.js.map +1 -0
- package/package.json +63 -94
- package/query/entity-query.d.ts +521 -0
- package/query/entity-query.js +1149 -0
- package/query/entity-query.js.map +1 -0
- package/{src → query}/mapping-context.d.ts +44 -46
- package/query/mapping-context.js +412 -0
- package/query/mapping-context.js.map +1 -0
- package/query/predicate.d.ts +394 -0
- package/query/predicate.js +1294 -0
- package/query/predicate.js.map +1 -0
- package/query/property-path.d.ts +252 -0
- package/query/property-path.js +2 -0
- package/query/property-path.js.map +1 -0
- package/{src → query}/query-options.d.ts +143 -119
- package/query/query-options.js +180 -0
- package/query/query-options.js.map +1 -0
- package/rxjs/breeze-rxjs.d.ts +72 -0
- package/rxjs/breeze-rxjs.js +117 -0
- package/rxjs/breeze-rxjs.js.map +1 -0
- package/validation/validate.d.ts +583 -0
- package/validation/validate.js +961 -0
- package/validation/validate.js.map +1 -0
- package/{src → validation}/validation-options.d.ts +67 -63
- package/validation/validation-options.js +70 -0
- package/validation/validation-options.js.map +1 -0
- package/adapter-ajax-angularjs/adapter-ajax-angularjs.d.ts +0 -17
- package/adapter-ajax-angularjs/adapter-core.d.ts +0 -4
- package/adapter-ajax-angularjs/index.d.ts +0 -5
- package/adapter-ajax-fetch/adapter-ajax-fetch.d.ts +0 -18
- package/adapter-ajax-fetch/index.d.ts +0 -5
- package/adapter-ajax-httpclient/adapter-ajax-httpclient.d.ts +0 -13
- package/adapter-ajax-httpclient/adapter-core.d.ts +0 -4
- package/adapter-ajax-httpclient/index.d.ts +0 -5
- package/adapter-ajax-jquery/adapter-ajax-jquery.d.ts +0 -15
- package/adapter-ajax-jquery/index.d.ts +0 -5
- package/adapter-ajax-post/index.d.ts +0 -5
- package/adapter-data-service-odata/adapter-core.d.ts +0 -4
- package/adapter-data-service-odata/adapter-data-service-odata.d.ts +0 -17
- package/adapter-data-service-odata/index.d.ts +0 -5
- package/adapter-data-service-webapi/adapter-data-service-webapi.d.ts +0 -15
- package/adapter-data-service-webapi/index.d.ts +0 -5
- package/adapter-model-library-backing-store/adapter-model-library-backing-store.d.ts +0 -10
- package/adapter-model-library-backing-store/index.d.ts +0 -5
- package/adapter-model-library-ko/adapter-model-library-ko.d.ts +0 -10
- package/adapter-model-library-ko/index.d.ts +0 -5
- package/adapter-uri-builder-json/adapter-core.d.ts +0 -4
- package/adapter-uri-builder-json/adapter-uri-builder-json.d.ts +0 -8
- package/adapter-uri-builder-json/index.d.ts +0 -5
- package/adapter-uri-builder-odata/adapter-core.d.ts +0 -4
- package/adapter-uri-builder-odata/adapter-uri-builder-odata.d.ts +0 -8
- package/adapter-uri-builder-odata/index.d.ts +0 -5
- package/esm2022/adapter-ajax-angularjs/adapter-ajax-angularjs.mjs +0 -135
- package/esm2022/adapter-ajax-angularjs/adapter-core.mjs +0 -49
- package/esm2022/adapter-ajax-angularjs/breeze-client-adapter-ajax-angularjs.mjs +0 -5
- package/esm2022/adapter-ajax-fetch/adapter-ajax-fetch.mjs +0 -135
- package/esm2022/adapter-ajax-fetch/adapter-core.mjs +0 -49
- package/esm2022/adapter-ajax-fetch/breeze-client-adapter-ajax-fetch.mjs +0 -5
- package/esm2022/adapter-ajax-httpclient/adapter-ajax-httpclient.mjs +0 -139
- package/esm2022/adapter-ajax-httpclient/adapter-core.mjs +0 -49
- package/esm2022/adapter-ajax-httpclient/breeze-client-adapter-ajax-httpclient.mjs +0 -5
- package/esm2022/adapter-ajax-jquery/adapter-ajax-jquery.mjs +0 -107
- package/esm2022/adapter-ajax-jquery/breeze-client-adapter-ajax-jquery.mjs +0 -5
- package/esm2022/adapter-ajax-post/adapter-ajax-post.mjs +0 -77
- package/esm2022/adapter-ajax-post/breeze-client-adapter-ajax-post.mjs +0 -5
- package/esm2022/adapter-data-service-odata/adapter-core.mjs +0 -49
- package/esm2022/adapter-data-service-odata/adapter-data-service-odata.mjs +0 -461
- package/esm2022/adapter-data-service-odata/breeze-client-adapter-data-service-odata.mjs +0 -5
- package/esm2022/adapter-data-service-webapi/adapter-data-service-webapi.mjs +0 -100
- package/esm2022/adapter-data-service-webapi/breeze-client-adapter-data-service-webapi.mjs +0 -5
- package/esm2022/adapter-model-library-backing-store/adapter-model-library-backing-store.mjs +0 -269
- package/esm2022/adapter-model-library-backing-store/breeze-client-adapter-model-library-backing-store.mjs +0 -5
- package/esm2022/adapter-model-library-ko/adapter-model-library-ko.mjs +0 -241
- package/esm2022/adapter-model-library-ko/breeze-client-adapter-model-library-ko.mjs +0 -5
- package/esm2022/adapter-uri-builder-json/adapter-core.mjs +0 -49
- package/esm2022/adapter-uri-builder-json/adapter-uri-builder-json.mjs +0 -37
- package/esm2022/adapter-uri-builder-json/breeze-client-adapter-uri-builder-json.mjs +0 -5
- package/esm2022/adapter-uri-builder-odata/adapter-core.mjs +0 -49
- package/esm2022/adapter-uri-builder-odata/adapter-uri-builder-odata.mjs +0 -191
- package/esm2022/adapter-uri-builder-odata/breeze-client-adapter-uri-builder-odata.mjs +0 -5
- package/esm2022/breeze-client.mjs +0 -5
- package/esm2022/index.mjs +0 -2
- package/esm2022/mixin-get-entity-graph/breeze-client-mixin-get-entity-graph.mjs +0 -5
- package/esm2022/mixin-get-entity-graph/mixin-get-entity-graph.mjs +0 -269
- package/esm2022/mixin-save-queuing/breeze-client-mixin-save-queuing.mjs +0 -5
- package/esm2022/mixin-save-queuing/mixin-save-queuing.mjs +0 -421
- package/esm2022/src/abstract-data-service-adapter.mjs +0 -325
- package/esm2022/src/array.mjs +0 -4
- package/esm2022/src/assert-param.mjs +0 -330
- package/esm2022/src/breeze.mjs +0 -85
- package/esm2022/src/complex-array.mjs +0 -117
- package/esm2022/src/config.mjs +0 -232
- package/esm2022/src/core.mjs +0 -712
- package/esm2022/src/csdl-metadata-parser.mjs +0 -385
- package/esm2022/src/data-service.mjs +0 -221
- package/esm2022/src/data-type.mjs +0 -510
- package/esm2022/src/default-property-interceptor.mjs +0 -460
- package/esm2022/src/entity-action.mjs +0 -45
- package/esm2022/src/entity-aspect.mjs +0 -897
- package/esm2022/src/entity-group.mjs +0 -192
- package/esm2022/src/entity-key.mjs +0 -125
- package/esm2022/src/entity-manager.mjs +0 -2163
- package/esm2022/src/entity-metadata.mjs +0 -2242
- package/esm2022/src/entity-query.mjs +0 -1024
- package/esm2022/src/entity-state.mjs +0 -107
- package/esm2022/src/enum.mjs +0 -143
- package/esm2022/src/event.mjs +0 -264
- package/esm2022/src/interface-registry.mjs +0 -39
- package/esm2022/src/key-generator.mjs +0 -110
- package/esm2022/src/local-query-comparison-options.mjs +0 -65
- package/esm2022/src/mapping-context.mjs +0 -411
- package/esm2022/src/naming-convention.mjs +0 -92
- package/esm2022/src/observable-array.mjs +0 -183
- package/esm2022/src/predicate.mjs +0 -1259
- package/esm2022/src/primitive-array.mjs +0 -85
- package/esm2022/src/query-options.mjs +0 -152
- package/esm2022/src/relation-array.mjs +0 -173
- package/esm2022/src/save-options.mjs +0 -41
- package/esm2022/src/unattached-children-map.mjs +0 -56
- package/esm2022/src/validate.mjs +0 -995
- package/esm2022/src/validation-options.mjs +0 -64
- package/fesm2022/breeze-client-adapter-ajax-angularjs.mjs +0 -190
- package/fesm2022/breeze-client-adapter-ajax-angularjs.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-ajax-fetch.mjs +0 -190
- package/fesm2022/breeze-client-adapter-ajax-fetch.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-ajax-httpclient.mjs +0 -194
- package/fesm2022/breeze-client-adapter-ajax-httpclient.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-ajax-jquery.mjs +0 -114
- package/fesm2022/breeze-client-adapter-ajax-jquery.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-ajax-post.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-data-service-odata.mjs +0 -516
- package/fesm2022/breeze-client-adapter-data-service-odata.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-data-service-webapi.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-model-library-backing-store.mjs +0 -276
- package/fesm2022/breeze-client-adapter-model-library-backing-store.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-model-library-ko.mjs +0 -248
- package/fesm2022/breeze-client-adapter-model-library-ko.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-uri-builder-json.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-uri-builder-odata.mjs +0 -246
- package/fesm2022/breeze-client-adapter-uri-builder-odata.mjs.map +0 -1
- package/fesm2022/breeze-client-mixin-get-entity-graph.mjs.map +0 -1
- package/fesm2022/breeze-client-mixin-save-queuing.mjs.map +0 -1
- package/fesm2022/breeze-client.mjs +0 -14144
- package/fesm2022/breeze-client.mjs.map +0 -1
- package/index.d.ts +0 -1
- package/mixin-get-entity-graph/index.d.ts +0 -5
- package/mixin-get-entity-graph/mixin-get-entity-graph.d.ts +0 -31
- package/mixin-save-queuing/index.d.ts +0 -5
- package/mixin-save-queuing/mixin-save-queuing.d.ts +0 -32
- package/src/abstract-data-service-adapter.d.ts +0 -84
- package/src/array.d.ts +0 -3
- package/src/breeze.d.ts +0 -139
- package/src/complex-array.d.ts +0 -13
- package/src/config.d.ts +0 -104
- package/src/core.d.ts +0 -164
- package/src/csdl-metadata-parser.d.ts +0 -7
- package/src/data-type.d.ts +0 -89
- package/src/default-property-interceptor.d.ts +0 -4
- package/src/entity-aspect.d.ts +0 -332
- package/src/entity-group.d.ts +0 -28
- package/src/entity-key.d.ts +0 -77
- package/src/entity-manager.d.ts +0 -680
- package/src/entity-metadata.d.ts +0 -988
- package/src/entity-query.d.ts +0 -444
- package/src/enum.d.ts +0 -100
- package/src/event.d.ts +0 -142
- package/src/interface-registry.d.ts +0 -150
- package/src/key-generator.d.ts +0 -12
- package/src/observable-array.d.ts +0 -52
- package/src/predicate.d.ts +0 -376
- package/src/primitive-array.d.ts +0 -7
- package/src/relation-array.d.ts +0 -18
- package/src/unattached-children-map.d.ts +0 -19
- package/src/validate.d.ts +0 -650
|
@@ -0,0 +1,1294 @@
|
|
|
1
|
+
import { core, arraySlice, hasOwnProperty as hasOwnProp, stringStartsWith as strStartsWith, stringEndsWith as strEndsWith } from '../core/core.js';
|
|
2
|
+
import { EntityType, DataProperty } from '../metadata/entity-metadata.js';
|
|
3
|
+
import { DataType } from '../metadata/data-type.js';
|
|
4
|
+
import { EntityAspect } from '../entity/entity-aspect.js';
|
|
5
|
+
/**
|
|
6
|
+
Used to define a 'where' predicate for an {@link EntityQuery}. Predicates are immutable, which means that any
|
|
7
|
+
method that would modify a Predicate actually returns a new Predicate.
|
|
8
|
+
```ts
|
|
9
|
+
const p = Predicate.for(Order);
|
|
10
|
+
const p1 = p("freight", ">", 100);
|
|
11
|
+
const p2 = p1.and(p("shipCity", "startsWith", "C"));
|
|
12
|
+
const query = EntityQuery.from(Order).where(p2);
|
|
13
|
+
```
|
|
14
|
+
*/
|
|
15
|
+
export class Predicate {
|
|
16
|
+
/**
|
|
17
|
+
Predicate constructor. The compiler checks nothing it is given; for that, build the Predicate with
|
|
18
|
+
{@link Predicate.for} or {@link Predicate.create} and a type argument instead.
|
|
19
|
+
```ts
|
|
20
|
+
const p1 = new Predicate("companyName", "startsWith", "B");
|
|
21
|
+
const query = EntityQuery.from(Customer).where(p1);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
or
|
|
25
|
+
```ts
|
|
26
|
+
const p2 = new Predicate("region", FilterQueryOp.Equals, null);
|
|
27
|
+
const query = EntityQuery.from(Customer).where(p2);
|
|
28
|
+
```
|
|
29
|
+
@param args - `property, operator, value`, in that order:
|
|
30
|
+
- **property**: A property name, a nested property name or an expression involving a property name.
|
|
31
|
+
- **operator**: the filter query operator.
|
|
32
|
+
- **value**: This will be treated as either a property expression or a literal depending on context. In general,
|
|
33
|
+
if the value can be interpreted as a property expression it will be, otherwise it will be treated as a literal.
|
|
34
|
+
In most cases this works well, but you can also force the interpretation by making the value argument itself an object with a 'value'
|
|
35
|
+
property and an 'isLiteral' property set to either true or false. Breeze also tries to infer the dataType of any
|
|
36
|
+
literal based on context, if this fails you can force this inference by making the value argument an object with a
|
|
37
|
+
'value' property and a 'dataType' property set to one of the breeze.DataType enumeration instances.
|
|
38
|
+
*/
|
|
39
|
+
constructor(...args) {
|
|
40
|
+
if (args.length === 0)
|
|
41
|
+
return;
|
|
42
|
+
if (!(this instanceof Predicate)) {
|
|
43
|
+
return new Predicate(...args);
|
|
44
|
+
}
|
|
45
|
+
return Predicate.create(...args);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
Same as using the ctor, except that given the entity type as a type argument, the compiler checks
|
|
49
|
+
the predicate against that type - everything in the object form, the property path in the
|
|
50
|
+
three-argument form:
|
|
51
|
+
```ts
|
|
52
|
+
const p1 = Predicate.create<Order>({ freight: { gt: 100 } });
|
|
53
|
+
const p2 = Predicate.create<Order>("freight", ">", 100);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Without one, nothing is checked, and
|
|
57
|
+
```ts
|
|
58
|
+
const p3 = Predicate.create("freight", ">", 100);
|
|
59
|
+
// is the same as
|
|
60
|
+
const p4 = new Predicate("freight", ">", 100);
|
|
61
|
+
```
|
|
62
|
+
@param args - `property, operator, value`, in that order:
|
|
63
|
+
- **property**: A property name, a nested property name or an expression involving a property name.
|
|
64
|
+
- **operator**: the filter query operator.
|
|
65
|
+
- **value**: This will be treated as either a property expression or a literal depending on context. In general,
|
|
66
|
+
if the value can be interpreted as a property expression it will be, otherwise it will be treated as a literal.
|
|
67
|
+
In most cases this works well, but you can also force the interpretation by making the value argument itself an object with a 'value'
|
|
68
|
+
property and an 'isLiteral' property set to either true or false. Breeze also tries to infer the dataType of any
|
|
69
|
+
literal based on context, if this fails you can force this inference by making the value argument an object with a
|
|
70
|
+
'value' property and a 'dataType' property set to one of the breeze.DataType enumeration instances.
|
|
71
|
+
*/
|
|
72
|
+
static create(...args) {
|
|
73
|
+
// can be called from std javascript without new ( legacy )
|
|
74
|
+
// empty ctor is used by all subclasses.
|
|
75
|
+
if (args.length === 0)
|
|
76
|
+
return new Predicate();
|
|
77
|
+
if (args.length === 1) {
|
|
78
|
+
// possibilities:
|
|
79
|
+
// Predicate([ aPredicate ]) or Predicate(["freight", ">", 100]) - an array
|
|
80
|
+
// Predicate(aPredicate) - a predicate
|
|
81
|
+
// Predicate( "freight gt 100" } // passthru ( i.e. a raw filter string) - a string
|
|
82
|
+
// Predicate( { freight: { ">": 100 } }) - an object
|
|
83
|
+
let arg = arguments[0];
|
|
84
|
+
if (Array.isArray(arg)) {
|
|
85
|
+
if (arg.length === 1) {
|
|
86
|
+
// recurse
|
|
87
|
+
return new Predicate(arg[0]);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
return createPredicateFromArray(arg);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (arg instanceof Predicate) {
|
|
94
|
+
return arg;
|
|
95
|
+
}
|
|
96
|
+
else if (typeof arg === 'string') {
|
|
97
|
+
return new PassthruPredicate(arg);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
return createPredicateFromObject(arg);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// 2 possibilities
|
|
105
|
+
// Predicate("freight", ">", 100");
|
|
106
|
+
// Predicate("orders", "any", "freight", ">", 950);
|
|
107
|
+
return createPredicateFromArray(args);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** @hidden @internal */
|
|
111
|
+
_validate(entityType, usesNameOnServer) {
|
|
112
|
+
// noop here;
|
|
113
|
+
}
|
|
114
|
+
static and(...args) {
|
|
115
|
+
return new AndOrPredicate("and", args);
|
|
116
|
+
}
|
|
117
|
+
static or(...args) {
|
|
118
|
+
return new AndOrPredicate("or", args);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
Creates a 'composite' Predicate by 'negating' a specified predicate.
|
|
122
|
+
```ts
|
|
123
|
+
const p1 = Predicate.create<Order>({ freight: { gt: 100 } });
|
|
124
|
+
const not_p1 = Predicate.not(p1);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
This can also be accomplished using the 'instance' version of the 'not' method
|
|
128
|
+
```ts
|
|
129
|
+
const not_p1 = p1.not();
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Both of which would be the same as
|
|
133
|
+
```ts
|
|
134
|
+
const not_p1 = Predicate.create<Order>({ freight: { le: 100 } });
|
|
135
|
+
```
|
|
136
|
+
*/
|
|
137
|
+
static not(pred) {
|
|
138
|
+
return pred.not();
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
Builds Predicates checked against one entity type.
|
|
142
|
+
```ts
|
|
143
|
+
const p = Predicate.for(Customer);
|
|
144
|
+
const pred = p("companyName", "startsWith", "C").and(p("city", "eq", "Vienna"));
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
A standalone Predicate has no query to take an entity type from, so on its own
|
|
148
|
+
{@link Predicate.create} cannot check anything. Naming the type supplies what is missing, the
|
|
149
|
+
same way {@link EntityQuery.from} does for a query - and from a constructor rather than a type
|
|
150
|
+
argument, so the type is written once.
|
|
151
|
+
|
|
152
|
+
The constructor is read for its type only; nothing about it is kept. The predicates are ordinary
|
|
153
|
+
Predicates at run time, typed `Predicate<U>`, so that what they are combined with, and the query
|
|
154
|
+
they are used in, is checked too. The constructor does not have to be registered with a MetadataStore for this - though
|
|
155
|
+
if it is not, nothing validates the path against the metadata either, exactly as before.
|
|
156
|
+
@param ctor - The entity class to check property paths against.
|
|
157
|
+
@returns A factory that builds Predicates for that type.
|
|
158
|
+
*/
|
|
159
|
+
static for(ctor) {
|
|
160
|
+
return ((...args) => Predicate.create(...args));
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
Adds functions that predicates can call, such as `toupper` in `Predicate.create<Customer>("toupper(companyName)", "==", "ACME")`,
|
|
164
|
+
or replaces existing ones. Each entry maps a function name to `fn`, which evaluates it in local
|
|
165
|
+
queries, and the {@link DataType} it returns. A server query sends the call by name, so the
|
|
166
|
+
server must support the function too.
|
|
167
|
+
```ts
|
|
168
|
+
Predicate.extendFuncMap({
|
|
169
|
+
initial: { fn: (s: string) => s.charAt(0), dataType: DataType.String }
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
@param funcMap - The functions to add, keyed by name. Write names in lower case: Breeze lower-cases a
|
|
173
|
+
function name when it parses a predicate.
|
|
174
|
+
*/
|
|
175
|
+
static extendFuncMap(funcMap) {
|
|
176
|
+
for (let func in (funcMap || {})) {
|
|
177
|
+
let config = funcMap[func];
|
|
178
|
+
FnExpr._funcMap.set(func, config);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
;
|
|
182
|
+
/**
|
|
183
|
+
'And's this Predicate with one or more other Predicates and returns a new 'composite' Predicate
|
|
184
|
+
```ts
|
|
185
|
+
const p = Predicate.for(Order);
|
|
186
|
+
const dt = new Date(1988, 9, 12);
|
|
187
|
+
const p1 = p("orderDate", "ne", dt);
|
|
188
|
+
const p2 = p("shipCity", "startsWith", "C");
|
|
189
|
+
const p3 = p("freight", ">", 100);
|
|
190
|
+
const newPred = p1.and(p2, p3);
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
or
|
|
194
|
+
```ts
|
|
195
|
+
const preds = [p2, p3];
|
|
196
|
+
const newPred = p1.and(preds);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The 'and' method is also used to write "fluent" expressions
|
|
200
|
+
```ts
|
|
201
|
+
const p4 = p("shipCity", "startsWith", "F")
|
|
202
|
+
.and(p("freight", "gt", 2000));
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
It also takes the arguments of {@link Predicate.create} in place of a Predicate - such as
|
|
206
|
+
`.and("freight", "gt", 2000)` - checked against the Predicate's entity type, as
|
|
207
|
+
{@link EntityQuery.where} checks them against the query's.
|
|
208
|
+
@param args - multiple Predicates or an array of Predicates.
|
|
209
|
+
Any null or undefined values passed in will be automatically filtered out before constructing the composite predicate.
|
|
210
|
+
*/
|
|
211
|
+
and(...args) {
|
|
212
|
+
return new AndOrPredicate("and", argsForAndOrPredicates(this, args));
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
'Or's this Predicate with one or more other Predicates and returns a new 'composite' Predicate
|
|
216
|
+
```ts
|
|
217
|
+
const p = Predicate.for(Order);
|
|
218
|
+
const dt = new Date(1988, 9, 12);
|
|
219
|
+
const p1 = p("orderDate", "ne", dt);
|
|
220
|
+
const p2 = p("shipCity", "startsWith", "C");
|
|
221
|
+
const p3 = p("freight", ">", 100);
|
|
222
|
+
const newPred = p1.or(p2, p3);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
or
|
|
226
|
+
```ts
|
|
227
|
+
const preds = [p2, p3];
|
|
228
|
+
const newPred = p1.or(preds);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The 'or' method is also used to write "fluent" expressions
|
|
232
|
+
```ts
|
|
233
|
+
const p4 = p("shipCity", "startsWith", "F")
|
|
234
|
+
.or(p("freight", "gt", 2000));
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
It also takes the arguments of {@link Predicate.create} in place of a Predicate - such as
|
|
238
|
+
`.or("freight", "gt", 2000)` - checked against the Predicate's entity type, as
|
|
239
|
+
{@link EntityQuery.where} checks them against the query's.
|
|
240
|
+
@param args - multiple Predicates or an array of Predicates.
|
|
241
|
+
Any null or undefined values passed in will be automatically filtered out before constructing the composite predicate.
|
|
242
|
+
*/
|
|
243
|
+
or(...args) {
|
|
244
|
+
return new AndOrPredicate("or", argsForAndOrPredicates(this, args));
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
Returns the 'negated' version of this Predicate
|
|
248
|
+
```ts
|
|
249
|
+
const p1 = Predicate.create<Order>({ freight: { gt: 100 } });
|
|
250
|
+
const not_p1 = p1.not();
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
This can also be accomplished using the 'static' version of the 'not' method
|
|
254
|
+
```ts
|
|
255
|
+
const not_p1 = Predicate.not(p1);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
which would be the same as
|
|
259
|
+
```ts
|
|
260
|
+
const not_p1 = Predicate.create<Order>({ freight: { le: 100 } });
|
|
261
|
+
```
|
|
262
|
+
*/
|
|
263
|
+
not() {
|
|
264
|
+
return new UnaryPredicate("not", this);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
Returns this predicate in Breeze's JSON query syntax, with property paths as written - the object
|
|
268
|
+
form that {@link Predicate.create} and {@link EntityQuery.where} also accept, such as
|
|
269
|
+
`{ freight: { gt: 100 } }`. `JSON.stringify` calls it.
|
|
270
|
+
*/
|
|
271
|
+
//
|
|
272
|
+
toJSON() {
|
|
273
|
+
// toJSON ( part of js standard - takes a single parameter
|
|
274
|
+
// that is either "" or the name of the property being serialized.
|
|
275
|
+
return this.toJSONExt({ entityType: this._entityType });
|
|
276
|
+
}
|
|
277
|
+
/** For use by breeze plugin authors only. The class is for use in building a {@link UriBuilderAdapter} implementation.
|
|
278
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
279
|
+
@hidden @internal
|
|
280
|
+
*/
|
|
281
|
+
toJSONExt(context) {
|
|
282
|
+
return this.visit(context, toJSONVisitor);
|
|
283
|
+
}
|
|
284
|
+
/** For use by breeze plugin authors only. The class is for use in building a {@link UriBuilderAdapter} implementation.
|
|
285
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
286
|
+
@hidden @internal
|
|
287
|
+
*/
|
|
288
|
+
toFunction(context) {
|
|
289
|
+
return this.visit(context, toFunctionVisitor);
|
|
290
|
+
}
|
|
291
|
+
/** Returns this predicate as a JSON string: `JSON.stringify` of {@link Predicate.toJSON}. Useful for logging and debugging. */
|
|
292
|
+
toString() {
|
|
293
|
+
return JSON.stringify(this);
|
|
294
|
+
}
|
|
295
|
+
/** For use by breeze plugin authors only. The class is for use in building a {@link UriBuilderAdapter} implementation.
|
|
296
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
297
|
+
@hidden @internal
|
|
298
|
+
*/
|
|
299
|
+
visit(context, visitor) {
|
|
300
|
+
if (core.isEmpty(context)) {
|
|
301
|
+
context = { entityType: undefined };
|
|
302
|
+
}
|
|
303
|
+
else if (context instanceof EntityType) {
|
|
304
|
+
context = { entityType: context };
|
|
305
|
+
}
|
|
306
|
+
else if (!hasOwnProp(context, "entityType")) {
|
|
307
|
+
throw new Error("All visitor methods must be called with a context object containing at least an 'entityType' property");
|
|
308
|
+
}
|
|
309
|
+
if (visitor) {
|
|
310
|
+
context.visitor = visitor;
|
|
311
|
+
}
|
|
312
|
+
let tVisitor = visitor || context.visitor;
|
|
313
|
+
let fn = tVisitor[this.visitorMethodName];
|
|
314
|
+
if (fn == null) {
|
|
315
|
+
throw new Error("Unable to locate method: " + this.visitorMethodName + " on visitor");
|
|
316
|
+
}
|
|
317
|
+
let entityType = context.entityType;
|
|
318
|
+
// don't bother validating if already done so ( or if no _validate method
|
|
319
|
+
if (this._validate && (entityType == null || this._entityType !== entityType)) {
|
|
320
|
+
// don't need to capture return value because validation fn doesn't have one.
|
|
321
|
+
this._validate(entityType, context.toNameOnServer);
|
|
322
|
+
this._entityType = entityType;
|
|
323
|
+
}
|
|
324
|
+
return fn.call(this, context);
|
|
325
|
+
}
|
|
326
|
+
/** @hidden @internal */
|
|
327
|
+
_initialize(visitorMethodName, opMap = {}) {
|
|
328
|
+
this.visitorMethodName = visitorMethodName;
|
|
329
|
+
let aliasMap = this.aliasMap = {};
|
|
330
|
+
for (let op in opMap) {
|
|
331
|
+
updateAliasMap(aliasMap, op, opMap[op]);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/** @hidden @internal */
|
|
335
|
+
_resolveOp(op, okIfNotFound) {
|
|
336
|
+
let opStr = (typeof op === "string") ? op : op.operator;
|
|
337
|
+
let result = this.aliasMap[opStr.toLowerCase()];
|
|
338
|
+
if (!result && !okIfNotFound) {
|
|
339
|
+
throw new Error("Unable to resolve operator: " + opStr);
|
|
340
|
+
}
|
|
341
|
+
return result;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function createPredicateFromArray(arr) {
|
|
345
|
+
// TODO: assert that length of the array should be > 3
|
|
346
|
+
// Needs to handle:
|
|
347
|
+
// [ "freight", ">", 100"];
|
|
348
|
+
// [ "orders", "any", "freight", ">", 950 ]
|
|
349
|
+
// [ "orders", "and", anotherPred ]
|
|
350
|
+
// [ "orders", "and", [ "freight, ">", 950 ]]
|
|
351
|
+
let json = {};
|
|
352
|
+
let value = {};
|
|
353
|
+
json[arr[0]] = value;
|
|
354
|
+
let op = arr[1];
|
|
355
|
+
op = op.operator || op; // incoming op will be either a string or a FilterQueryOp
|
|
356
|
+
if (arr.length === 3) {
|
|
357
|
+
value[op] = arr[2];
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
value[op] = createPredicateFromArray(arr.splice(2));
|
|
361
|
+
}
|
|
362
|
+
return createPredicateFromObject(json);
|
|
363
|
+
}
|
|
364
|
+
function createPredicateFromObject(obj) {
|
|
365
|
+
if (obj instanceof Predicate)
|
|
366
|
+
return obj;
|
|
367
|
+
if (typeof obj !== 'object') {
|
|
368
|
+
throw new Error("Unable to convert to a Predicate: " + obj);
|
|
369
|
+
}
|
|
370
|
+
let keys = Object.keys(obj);
|
|
371
|
+
let preds = keys.map(function (key) {
|
|
372
|
+
return createPredicateFromKeyValue(key, obj[key]);
|
|
373
|
+
});
|
|
374
|
+
return (preds.length === 1) ? preds[0] : new AndOrPredicate("and", preds);
|
|
375
|
+
}
|
|
376
|
+
function createPredicateFromKeyValue(key, value) {
|
|
377
|
+
// { and: [a,b] } key='and', value = [a,b]
|
|
378
|
+
if (AndOrPredicate.prototype._resolveOp(key, true)) {
|
|
379
|
+
return new AndOrPredicate(key, value);
|
|
380
|
+
}
|
|
381
|
+
// { not: a } key= 'not', value = a
|
|
382
|
+
if (UnaryPredicate.prototype._resolveOp(key, true)) {
|
|
383
|
+
return new UnaryPredicate(key, value);
|
|
384
|
+
}
|
|
385
|
+
if ((typeof value !== 'object') || value == null || core.isDate(value)) {
|
|
386
|
+
// { foo: bar } key='foo', value = bar ( where bar is a literal i.e. a string, a number, a boolean or a date.
|
|
387
|
+
return new BinaryPredicate("eq", key, value);
|
|
388
|
+
}
|
|
389
|
+
else if (hasOwnProp(value, 'value')) {
|
|
390
|
+
// { foo: { value: bar, dataType: xxx} } key='foo', value = bar ( where bar is an object representing a literal
|
|
391
|
+
return new BinaryPredicate("eq", key, value);
|
|
392
|
+
}
|
|
393
|
+
if (Array.isArray(value)) {
|
|
394
|
+
throw new Error("Unable to resolve predicate after the phrase: " + key);
|
|
395
|
+
}
|
|
396
|
+
let expr = key;
|
|
397
|
+
let keys = Object.keys(value);
|
|
398
|
+
let preds = keys.map(function (op) {
|
|
399
|
+
// { a: { any: b } op = 'any', expr=a, value[op] = b
|
|
400
|
+
if (AnyAllPredicate.prototype._resolveOp(op, true)) {
|
|
401
|
+
return new AnyAllPredicate(op, expr, value[op]);
|
|
402
|
+
}
|
|
403
|
+
if (BinaryPredicate.prototype._resolveOp(op, true)) {
|
|
404
|
+
// { a: { ">": b }} op = ">", expr=a, value[op] = b
|
|
405
|
+
return new BinaryPredicate(op, expr, value[op]);
|
|
406
|
+
}
|
|
407
|
+
else if (hasOwnProp(value[op], 'value')) {
|
|
408
|
+
// { a: { ">": { value: b, dataType: 'Int32' }} expr = a value[op] = { value: b, dataType: 'Int32' }
|
|
409
|
+
return new BinaryPredicate("eq", expr, value[op]);
|
|
410
|
+
}
|
|
411
|
+
let msg = core.formatString("Unable to resolve predicate after the phrase: '%1' for operator: '%2' and value: '%3'", expr, op, value[op]);
|
|
412
|
+
throw new Error(msg);
|
|
413
|
+
});
|
|
414
|
+
return (preds.length === 1) ? preds[0] : new AndOrPredicate("and", preds);
|
|
415
|
+
}
|
|
416
|
+
function argsForAndOrPredicates(obj, args) {
|
|
417
|
+
let preds = args[0];
|
|
418
|
+
if (preds instanceof Predicate) {
|
|
419
|
+
preds = arraySlice(args);
|
|
420
|
+
}
|
|
421
|
+
else if (!Array.isArray(preds)) {
|
|
422
|
+
preds = [new Predicate(arraySlice(args))];
|
|
423
|
+
}
|
|
424
|
+
return [obj].concat(preds);
|
|
425
|
+
}
|
|
426
|
+
function updateAliasMap(aliasMap, opStr, op) {
|
|
427
|
+
let key = opStr.toLowerCase();
|
|
428
|
+
op.key = key;
|
|
429
|
+
aliasMap[key] = op;
|
|
430
|
+
op.aliases && op.aliases.forEach((alias) => {
|
|
431
|
+
aliasMap[alias.toLowerCase()] = op;
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
/** For use by breeze plugin authors only. The class is for use in building a {@link UriBuilderAdapter} implementation.
|
|
435
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
436
|
+
@hidden @internal
|
|
437
|
+
*/
|
|
438
|
+
class PassthruPredicate extends Predicate {
|
|
439
|
+
value;
|
|
440
|
+
constructor(value) {
|
|
441
|
+
super();
|
|
442
|
+
this.value = value;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
PassthruPredicate.prototype._initialize('passthruPredicate');
|
|
446
|
+
/** For use by breeze plugin authors only. The class is for use in building a {@link UriBuilderAdapter} implementation.
|
|
447
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
448
|
+
@hidden
|
|
449
|
+
*/
|
|
450
|
+
export class UnaryPredicate extends Predicate {
|
|
451
|
+
pred;
|
|
452
|
+
constructor(op, ...args) {
|
|
453
|
+
super();
|
|
454
|
+
this.op = this._resolveOp(op);
|
|
455
|
+
this.pred = new Predicate(args);
|
|
456
|
+
}
|
|
457
|
+
_validate(entityType, usesNameOnServer) {
|
|
458
|
+
this.pred._validate(entityType, usesNameOnServer);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
UnaryPredicate.prototype._initialize('unaryPredicate', {
|
|
462
|
+
'not': { aliases: ['!', '~'] },
|
|
463
|
+
});
|
|
464
|
+
/** For use by breeze plugin authors only. The class is for use in building a {@link UriBuilderAdapter} implementation.
|
|
465
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
466
|
+
@hidden
|
|
467
|
+
*/
|
|
468
|
+
export class BinaryPredicate extends Predicate {
|
|
469
|
+
expr1Source;
|
|
470
|
+
expr2Source;
|
|
471
|
+
expr1;
|
|
472
|
+
expr2;
|
|
473
|
+
constructor(op, expr1, expr2) {
|
|
474
|
+
super();
|
|
475
|
+
// 5 public props op, expr1Source, expr2Source, expr1, expr2
|
|
476
|
+
this.op = this._resolveOp(op);
|
|
477
|
+
this.expr1Source = expr1;
|
|
478
|
+
this.expr2Source = expr2;
|
|
479
|
+
// this.expr1 and this.expr2 won't be
|
|
480
|
+
// determined until validate is run
|
|
481
|
+
}
|
|
482
|
+
_validate(entityType, usesNameOnServer) {
|
|
483
|
+
let expr1Context = { entityType: entityType, usesNameOnServer: usesNameOnServer };
|
|
484
|
+
this.expr1 = createExpr(this.expr1Source, expr1Context);
|
|
485
|
+
if (this.expr1 == null) {
|
|
486
|
+
throw new Error("Unable to validate 1st expression: " + this.expr1Source);
|
|
487
|
+
}
|
|
488
|
+
if (this.expr1 instanceof LitExpr) {
|
|
489
|
+
// lhs must be either a property or a function.
|
|
490
|
+
throw new Error("The left hand side of a binary predicate cannot be a literal expression, it must be a valid property or functional predicate expression: " + this.expr1Source);
|
|
491
|
+
}
|
|
492
|
+
if (this.op.key === 'in' && !Array.isArray(this.expr2Source)) {
|
|
493
|
+
throw new Error("The 'in' operator requires that its right hand argument be an array");
|
|
494
|
+
}
|
|
495
|
+
let expr2Context = core.extend(expr1Context, { isRHS: true, dataType: this.expr1.dataType });
|
|
496
|
+
this.expr2 = createExpr(this.expr2Source, expr2Context);
|
|
497
|
+
if (this.expr2 == null) {
|
|
498
|
+
throw new Error("Unable to validate 2nd expression: " + this.expr2Source);
|
|
499
|
+
}
|
|
500
|
+
if (this.expr1.dataType == null) {
|
|
501
|
+
this.expr1.dataType = this.expr2.dataType;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
BinaryPredicate.prototype._initialize('binaryPredicate', {
|
|
506
|
+
'eq': {
|
|
507
|
+
aliases: ["==", "equals", "equal"]
|
|
508
|
+
},
|
|
509
|
+
'ne': {
|
|
510
|
+
aliases: ["!=", "~=", "notequals", "notequal"]
|
|
511
|
+
},
|
|
512
|
+
'lt': {
|
|
513
|
+
aliases: ["<", "lessthan"]
|
|
514
|
+
},
|
|
515
|
+
'le': {
|
|
516
|
+
aliases: ["<=", "lessthanorequal"]
|
|
517
|
+
},
|
|
518
|
+
'gt': {
|
|
519
|
+
aliases: [">", "greaterthan"]
|
|
520
|
+
},
|
|
521
|
+
'ge': {
|
|
522
|
+
aliases: [">=", "greaterthanorequal"]
|
|
523
|
+
},
|
|
524
|
+
'startswith': {
|
|
525
|
+
isFunction: true
|
|
526
|
+
},
|
|
527
|
+
'endswith': {
|
|
528
|
+
isFunction: true
|
|
529
|
+
},
|
|
530
|
+
'contains': {
|
|
531
|
+
aliases: ["substringof"],
|
|
532
|
+
isFunction: true
|
|
533
|
+
},
|
|
534
|
+
'in': {}
|
|
535
|
+
});
|
|
536
|
+
/** For use by breeze plugin authors only. The class is for use in building a {@link UriBuilderAdapter} implementation.
|
|
537
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
538
|
+
@hidden
|
|
539
|
+
*/
|
|
540
|
+
export class AndOrPredicate extends Predicate {
|
|
541
|
+
preds;
|
|
542
|
+
constructor(op, preds) {
|
|
543
|
+
super();
|
|
544
|
+
this.op = this._resolveOp(op);
|
|
545
|
+
if (preds.length === 1 && Array.isArray(preds[0])) {
|
|
546
|
+
preds = preds[0];
|
|
547
|
+
}
|
|
548
|
+
this.preds = preds.filter(function (pred) {
|
|
549
|
+
return pred != null;
|
|
550
|
+
}).map(function (pred) {
|
|
551
|
+
return new Predicate(pred);
|
|
552
|
+
});
|
|
553
|
+
if (this.preds.length === 1) {
|
|
554
|
+
return this.preds[0]; // HACK: this.preds[0] is actually NOT a AndOrPredicate but some other kind of pred.
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
_validate(entityType, usesNameOnServer) {
|
|
558
|
+
this.preds.forEach((pred) => {
|
|
559
|
+
pred._validate(entityType, usesNameOnServer);
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
AndOrPredicate.prototype._initialize("andOrPredicate", {
|
|
564
|
+
'and': { aliases: ['&&'] },
|
|
565
|
+
'or': { aliases: ['||'] }
|
|
566
|
+
});
|
|
567
|
+
/** For use by breeze plugin authors only. The class is for use in building a {@link UriBuilderAdapter} implementation.
|
|
568
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
569
|
+
@hidden
|
|
570
|
+
*/
|
|
571
|
+
export class AnyAllPredicate extends Predicate {
|
|
572
|
+
/** @internal */
|
|
573
|
+
expr;
|
|
574
|
+
exprSource;
|
|
575
|
+
pred;
|
|
576
|
+
// 4 public props: op, exprSource, expr, pred
|
|
577
|
+
constructor(op, expr, pred) {
|
|
578
|
+
super();
|
|
579
|
+
this.op = this._resolveOp(op);
|
|
580
|
+
this.exprSource = expr;
|
|
581
|
+
// this.expr will not be resolved until validate is called
|
|
582
|
+
this.pred = new Predicate(pred);
|
|
583
|
+
}
|
|
584
|
+
_validate(entityType, usesNameOnServer) {
|
|
585
|
+
this.expr = createExpr(this.exprSource, { entityType: entityType, usesNameOnServer: usesNameOnServer });
|
|
586
|
+
// can't really know the predicateEntityType unless the original entity type was known.
|
|
587
|
+
if (entityType == null || entityType.isAnonymous) {
|
|
588
|
+
this.expr.dataType = undefined;
|
|
589
|
+
}
|
|
590
|
+
this.pred._validate(this.expr.dataType, usesNameOnServer);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
AnyAllPredicate.prototype._initialize("anyAllPredicate", {
|
|
594
|
+
'any': { aliases: ['some'] },
|
|
595
|
+
'all': { aliases: ["every"] }
|
|
596
|
+
});
|
|
597
|
+
/** @hidden */
|
|
598
|
+
export class PredicateExpression {
|
|
599
|
+
visitorMethodName;
|
|
600
|
+
visit; // TODO
|
|
601
|
+
dataType;
|
|
602
|
+
constructor(visitorMethodName) {
|
|
603
|
+
this.visitorMethodName = visitorMethodName;
|
|
604
|
+
// give expressions the Predicate prototype method
|
|
605
|
+
this.visit = Predicate.prototype.visit;
|
|
606
|
+
}
|
|
607
|
+
// default impls - may/will be overridden be subclass expressions
|
|
608
|
+
_validate(entityType, usesNameOnServer) {
|
|
609
|
+
// noop;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
/** For use by breeze plugin authors only. The class is for use in building a {@link UriBuilderAdapter} implementation.
|
|
613
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
614
|
+
@hidden
|
|
615
|
+
*/
|
|
616
|
+
export class LitExpr extends PredicateExpression {
|
|
617
|
+
value;
|
|
618
|
+
hasExplicitDataType;
|
|
619
|
+
// 2 public props: value, dataType
|
|
620
|
+
constructor(value, dataType, hasExplicitDataType) {
|
|
621
|
+
super("litExpr");
|
|
622
|
+
// dataType may come is an a string
|
|
623
|
+
let dt1 = resolveDataType(dataType);
|
|
624
|
+
// if the DataType comes in as Undefined this means
|
|
625
|
+
// that we should NOT attempt to parse it but just leave it alone
|
|
626
|
+
// for now - this is usually because it is part of a Func expr.
|
|
627
|
+
// The cast holds for test/tsconfig.json, which checks src/ again with strictNullChecks off;
|
|
628
|
+
// `resolveDataType` returns `DataType | string | undefined`, and only the strict pass drops
|
|
629
|
+
// the `string` here.
|
|
630
|
+
let dt2 = (dt1 || DataType.fromValue(value));
|
|
631
|
+
if (dt2.parse) {
|
|
632
|
+
if (Array.isArray(value)) {
|
|
633
|
+
this.value = value.map((v) => { return dt2.parse(v, typeof v); });
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
this.value = dt2.parse(value, typeof value);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
this.value = value;
|
|
641
|
+
}
|
|
642
|
+
this.dataType = dt2;
|
|
643
|
+
this.hasExplicitDataType = !!hasExplicitDataType;
|
|
644
|
+
}
|
|
645
|
+
toString() {
|
|
646
|
+
return " LitExpr - value: " + this.value.toString() + " dataType: " + this.dataType.toString();
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function resolveDataType(dataType) {
|
|
650
|
+
if (dataType == null)
|
|
651
|
+
return dataType;
|
|
652
|
+
// if (DataType.contains(dataType)) {
|
|
653
|
+
if (dataType instanceof DataType) {
|
|
654
|
+
return dataType;
|
|
655
|
+
}
|
|
656
|
+
if (typeof dataType === 'string') {
|
|
657
|
+
let dt = DataType.fromName(dataType);
|
|
658
|
+
if (dt)
|
|
659
|
+
return dt;
|
|
660
|
+
throw new Error("Unable to resolve a dataType named: " + dataType);
|
|
661
|
+
}
|
|
662
|
+
throw new Error("The dataType parameter passed into this literal expression is not a 'DataType'" + dataType);
|
|
663
|
+
}
|
|
664
|
+
/** For use by breeze plugin authors only. The class is for use in building a {@link UriBuilderAdapter} implementation.
|
|
665
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
666
|
+
@hidden
|
|
667
|
+
*/
|
|
668
|
+
export class PropExpr extends PredicateExpression {
|
|
669
|
+
propertyPath;
|
|
670
|
+
// two public props: propertyPath, dateType
|
|
671
|
+
constructor(propertyPath) {
|
|
672
|
+
super('propExpr');
|
|
673
|
+
this.propertyPath = propertyPath;
|
|
674
|
+
//this.dataType = DataType.Undefined;
|
|
675
|
+
// this.dataType resolved after validate ( if not on an anon type }
|
|
676
|
+
}
|
|
677
|
+
toString() {
|
|
678
|
+
return " PropExpr - " + this.propertyPath;
|
|
679
|
+
}
|
|
680
|
+
_validate(entityType, usesNameOnServer) {
|
|
681
|
+
if (entityType == null || entityType.isAnonymous)
|
|
682
|
+
return;
|
|
683
|
+
let props = entityType.getPropertiesOnPath(this.propertyPath, null, false);
|
|
684
|
+
if (!props) {
|
|
685
|
+
let msg = core.formatString("Unable to resolve propertyPath. EntityType: '%1' PropertyPath: '%2'", entityType.name, this.propertyPath);
|
|
686
|
+
throw new Error(msg);
|
|
687
|
+
}
|
|
688
|
+
// get the last property
|
|
689
|
+
let prop = props[props.length - 1];
|
|
690
|
+
if (prop instanceof DataProperty) {
|
|
691
|
+
this.dataType = prop.dataType;
|
|
692
|
+
}
|
|
693
|
+
else {
|
|
694
|
+
this.dataType = prop.entityType;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
/** For use by breeze plugin authors only. The class is for use in building a {@link UriBuilderAdapter} implementation.
|
|
699
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
700
|
+
@hidden
|
|
701
|
+
*/
|
|
702
|
+
export class FnExpr extends PredicateExpression {
|
|
703
|
+
fnName;
|
|
704
|
+
exprs;
|
|
705
|
+
localFn; // TODO:
|
|
706
|
+
constructor(fnName, exprs) {
|
|
707
|
+
super('fnExpr');
|
|
708
|
+
// 4 public props: fnName, exprs, localFn, dataType
|
|
709
|
+
this.fnName = fnName;
|
|
710
|
+
this.exprs = exprs;
|
|
711
|
+
let qf = FnExpr._funcMap.get(fnName);
|
|
712
|
+
if (qf == null) {
|
|
713
|
+
throw new Error("Unknown function: " + fnName);
|
|
714
|
+
}
|
|
715
|
+
this.localFn = qf.fn;
|
|
716
|
+
this.dataType = qf.dataType;
|
|
717
|
+
}
|
|
718
|
+
toString() {
|
|
719
|
+
let exprStr = this.exprs.map(function (expr) {
|
|
720
|
+
expr.toString();
|
|
721
|
+
}).toString();
|
|
722
|
+
return "FnExpr - " + this.fnName + "(" + exprStr + ")";
|
|
723
|
+
}
|
|
724
|
+
_validate(entityType, usesNameOnServer) {
|
|
725
|
+
this.exprs.forEach(function (expr) {
|
|
726
|
+
expr._validate(entityType, usesNameOnServer);
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
static _funcMap = new Map(Object.entries({
|
|
730
|
+
toupper: {
|
|
731
|
+
fn: function (source) {
|
|
732
|
+
return source.toUpperCase();
|
|
733
|
+
}, dataType: DataType.String
|
|
734
|
+
},
|
|
735
|
+
tolower: {
|
|
736
|
+
fn: function (source) {
|
|
737
|
+
return source.toLowerCase();
|
|
738
|
+
}, dataType: DataType.String
|
|
739
|
+
},
|
|
740
|
+
substring: {
|
|
741
|
+
// As on the server (.NET String.Substring(startIndex, length)), the third argument is
|
|
742
|
+
// a length, not an end index. Function arguments arrive unparsed, often as strings.
|
|
743
|
+
fn: function (source, pos, length) {
|
|
744
|
+
const start = Number(pos);
|
|
745
|
+
return length == null ? source.substring(start) : source.substring(start, start + Number(length));
|
|
746
|
+
}, dataType: DataType.String
|
|
747
|
+
},
|
|
748
|
+
substringof: {
|
|
749
|
+
fn: function (find, source) {
|
|
750
|
+
return source.indexOf(find) >= 0;
|
|
751
|
+
}, dataType: DataType.Boolean
|
|
752
|
+
},
|
|
753
|
+
length: {
|
|
754
|
+
fn: function (source) {
|
|
755
|
+
return source.length;
|
|
756
|
+
}, dataType: DataType.Int32
|
|
757
|
+
},
|
|
758
|
+
trim: {
|
|
759
|
+
fn: function (source) {
|
|
760
|
+
return source.trim();
|
|
761
|
+
}, dataType: DataType.String
|
|
762
|
+
},
|
|
763
|
+
concat: {
|
|
764
|
+
fn: function (s1, s2) {
|
|
765
|
+
return s1.concat(s2);
|
|
766
|
+
}, dataType: DataType.String
|
|
767
|
+
},
|
|
768
|
+
replace: {
|
|
769
|
+
fn: function (source, find, replace) {
|
|
770
|
+
return source.replace(find, replace);
|
|
771
|
+
}, dataType: DataType.String
|
|
772
|
+
},
|
|
773
|
+
startswith: {
|
|
774
|
+
fn: function (source, find) {
|
|
775
|
+
return strStartsWith(source, find);
|
|
776
|
+
}, dataType: DataType.Boolean
|
|
777
|
+
},
|
|
778
|
+
endswith: {
|
|
779
|
+
fn: function (source, find) {
|
|
780
|
+
return strEndsWith(source, find);
|
|
781
|
+
}, dataType: DataType.Boolean
|
|
782
|
+
},
|
|
783
|
+
indexof: {
|
|
784
|
+
fn: function (source, find) {
|
|
785
|
+
return source.indexOf(find);
|
|
786
|
+
}, dataType: DataType.Int32
|
|
787
|
+
},
|
|
788
|
+
round: {
|
|
789
|
+
fn: function (source) {
|
|
790
|
+
return Math.round(source);
|
|
791
|
+
}, dataType: DataType.Int32
|
|
792
|
+
},
|
|
793
|
+
ceiling: {
|
|
794
|
+
fn: function (source) {
|
|
795
|
+
return Math.ceil(source);
|
|
796
|
+
}, dataType: DataType.Int32
|
|
797
|
+
},
|
|
798
|
+
floor: {
|
|
799
|
+
fn: function (source) {
|
|
800
|
+
return Math.floor(source);
|
|
801
|
+
}, dataType: DataType.Int32
|
|
802
|
+
},
|
|
803
|
+
second: {
|
|
804
|
+
fn: function (source) {
|
|
805
|
+
return source.getSeconds();
|
|
806
|
+
}, dataType: DataType.Int32
|
|
807
|
+
},
|
|
808
|
+
minute: {
|
|
809
|
+
fn: function (source) {
|
|
810
|
+
return source.getMinutes();
|
|
811
|
+
}, dataType: DataType.Int32
|
|
812
|
+
},
|
|
813
|
+
hour: {
|
|
814
|
+
fn: function (source) {
|
|
815
|
+
return source.getHours();
|
|
816
|
+
}, dataType: DataType.Int32
|
|
817
|
+
},
|
|
818
|
+
day: {
|
|
819
|
+
fn: function (source) {
|
|
820
|
+
return source.getDate();
|
|
821
|
+
}, dataType: DataType.Int32
|
|
822
|
+
},
|
|
823
|
+
month: {
|
|
824
|
+
fn: function (source) {
|
|
825
|
+
return source.getMonth() + 1;
|
|
826
|
+
}, dataType: DataType.Int32
|
|
827
|
+
},
|
|
828
|
+
year: {
|
|
829
|
+
fn: function (source) {
|
|
830
|
+
return source.getFullYear();
|
|
831
|
+
}, dataType: DataType.Int32
|
|
832
|
+
}
|
|
833
|
+
}));
|
|
834
|
+
}
|
|
835
|
+
// TODO: add dataTypes for the args next - will help to infer other dataTypes.
|
|
836
|
+
let RX_IDENTIFIER = /^[a-z_][\w.$]*$/i;
|
|
837
|
+
// comma delimited expressions ignoring commas inside of both single and double quotes.
|
|
838
|
+
let RX_COMMA_DELIM1 = /('[^']*'|[^,]+)/g;
|
|
839
|
+
let RX_COMMA_DELIM2 = /("[^"]*"|[^,]+)/g;
|
|
840
|
+
let DELIM = String.fromCharCode(191);
|
|
841
|
+
function createExpr(source, exprContext) {
|
|
842
|
+
let entityType = exprContext.entityType;
|
|
843
|
+
// the right hand side of an 'in' clause
|
|
844
|
+
if (Array.isArray(source)) {
|
|
845
|
+
if (!exprContext.isRHS) {
|
|
846
|
+
throw new Error("Array expressions are only permitted on the right hand side of a BinaryPredicate");
|
|
847
|
+
}
|
|
848
|
+
return new LitExpr(source, exprContext.dataType);
|
|
849
|
+
}
|
|
850
|
+
if (!(typeof source === 'string')) {
|
|
851
|
+
if (source != null && typeof source === 'object' && !source.toISOString) {
|
|
852
|
+
// source is an object but not a Date-like thing such as a JS or MomentJS Date
|
|
853
|
+
if (source.value === undefined) {
|
|
854
|
+
throw new Error("Unable to resolve an expression for: " + source + " on entityType: " + (entityType ? entityType.name : 'null'));
|
|
855
|
+
}
|
|
856
|
+
// { value, isProperty: true } or { value, isLiteral: false } forces a property;
|
|
857
|
+
// anything else (including { value, isLiteral: true }) is a literal.
|
|
858
|
+
if (source.isProperty || source.isLiteral === false) {
|
|
859
|
+
return new PropExpr(source.value);
|
|
860
|
+
}
|
|
861
|
+
else {
|
|
862
|
+
// we want to insure that any LitExpr created this way is tagged with 'hasExplicitDataType: true'
|
|
863
|
+
// because we want to insure that if we roundtrip thru toJSON that we don't
|
|
864
|
+
// accidentally reinterpret this node as a PropExpr.
|
|
865
|
+
// return new LitExpr(source.value, source.dataType || context.dataType, !!source.dataType);
|
|
866
|
+
return new LitExpr(source.value, source.dataType || exprContext.dataType, true);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
return new LitExpr(source, exprContext.dataType);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (exprContext.isRHS) {
|
|
874
|
+
if (entityType == null || entityType.isAnonymous) {
|
|
875
|
+
// if entityType is unknown then assume that the rhs is a literal
|
|
876
|
+
return new LitExpr(source, exprContext.dataType);
|
|
877
|
+
}
|
|
878
|
+
else {
|
|
879
|
+
return parseLitOrPropExpr(source, exprContext);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
else {
|
|
883
|
+
let regex = /\([^()]*\)/;
|
|
884
|
+
let m;
|
|
885
|
+
let tokens = [];
|
|
886
|
+
let i = 0;
|
|
887
|
+
while (m = regex.exec(source)) {
|
|
888
|
+
let token = m[0];
|
|
889
|
+
tokens.push(token);
|
|
890
|
+
let repl = DELIM + i++;
|
|
891
|
+
source = source.replace(token, repl);
|
|
892
|
+
}
|
|
893
|
+
let expr = parseExpr(source, tokens, exprContext);
|
|
894
|
+
expr._validate(entityType, exprContext.usesNameOnServer);
|
|
895
|
+
return expr;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
function parseExpr(source, tokens, exprContext) {
|
|
899
|
+
let parts = source.split(DELIM);
|
|
900
|
+
if (parts.length === 1) {
|
|
901
|
+
return parseLitOrPropExpr(parts[0], exprContext);
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
return parseFnExpr(source, parts, tokens, exprContext);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
function parseLitOrPropExpr(value, exprContext) {
|
|
908
|
+
value = value.trim();
|
|
909
|
+
// value is either a string, a quoted string, a number, a bool value, or a date
|
|
910
|
+
// if a string ( not a quoted string) then this represents a property name ( 1st ) or a lit string ( 2nd)
|
|
911
|
+
let firstChar = value.substr(0, 1);
|
|
912
|
+
let isQuoted = (firstChar === "'" || firstChar === '"') && value.length > 1 && value.substr(value.length - 1) === firstChar;
|
|
913
|
+
if (isQuoted) {
|
|
914
|
+
let unquotedValue = value.substr(1, value.length - 2);
|
|
915
|
+
return new LitExpr(unquotedValue, exprContext.dataType || DataType.String);
|
|
916
|
+
}
|
|
917
|
+
else {
|
|
918
|
+
let entityType = exprContext.entityType;
|
|
919
|
+
// TODO: get rid of isAnonymous below when we get the chance.
|
|
920
|
+
if (entityType == null || entityType.isAnonymous) {
|
|
921
|
+
// this fork will only be reached on the LHS of an BinaryPredicate -
|
|
922
|
+
// a RHS expr cannot get here with an anon type
|
|
923
|
+
return new PropExpr(value);
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
let mayBeIdentifier = RX_IDENTIFIER.test(value);
|
|
927
|
+
if (mayBeIdentifier) {
|
|
928
|
+
// if (entityType.getProperty(value, false) != null) {
|
|
929
|
+
if (entityType.getPropertiesOnPath(value, null, false) != null) {
|
|
930
|
+
return new PropExpr(value);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
// we don't really know the datatype here because even though it comes in as a string
|
|
935
|
+
// its usually a string BUT it might be a number i.e. the "1" or the "2" from an expr
|
|
936
|
+
// like "toUpper(substring(companyName, 1, 2))"
|
|
937
|
+
return new LitExpr(value, exprContext.dataType);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
function parseFnExpr(source, parts, tokens, exprContext) {
|
|
941
|
+
let fnName = parts[0].trim().toLowerCase();
|
|
942
|
+
let argSource = tokens[parts[1]].trim();
|
|
943
|
+
if (argSource.substr(0, 1) === "(") {
|
|
944
|
+
argSource = argSource.substr(1, argSource.length - 2);
|
|
945
|
+
}
|
|
946
|
+
let commaMatchStr = source.indexOf("'") >= 0 ? RX_COMMA_DELIM1 : RX_COMMA_DELIM2;
|
|
947
|
+
let args = argSource.match(commaMatchStr);
|
|
948
|
+
let newContext = core.extend({}, exprContext);
|
|
949
|
+
// a dataType of Undefined on a context basically means not to try parsing
|
|
950
|
+
// the value if the expr is a literal
|
|
951
|
+
newContext.dataType = DataType.Undefined;
|
|
952
|
+
newContext.isFnArg = true;
|
|
953
|
+
let exprs = args.map(function (a) {
|
|
954
|
+
return parseExpr(a, tokens, newContext);
|
|
955
|
+
});
|
|
956
|
+
return new FnExpr(fnName, exprs);
|
|
957
|
+
}
|
|
958
|
+
// toFunctionVisitor
|
|
959
|
+
let toFunctionVisitor = {
|
|
960
|
+
isExtended: false,
|
|
961
|
+
passthruPredicate: function () {
|
|
962
|
+
throw new Error("Cannot execute an PassthruPredicate expression against the local cache: " + this.value);
|
|
963
|
+
},
|
|
964
|
+
unaryPredicate: function (context) {
|
|
965
|
+
let predFn = this.pred.visit(context);
|
|
966
|
+
switch (this.op.key) {
|
|
967
|
+
case "not":
|
|
968
|
+
return function (entity) {
|
|
969
|
+
return !predFn(entity);
|
|
970
|
+
};
|
|
971
|
+
default:
|
|
972
|
+
throw new Error("Invalid unary operator:" + this.op.key);
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
binaryPredicate: function (context) {
|
|
976
|
+
let expr1Fn = this.expr1.visit(context);
|
|
977
|
+
let expr2Fn = this.expr2.visit(context);
|
|
978
|
+
let dataType = this.expr1.dataType || this.expr2.dataType;
|
|
979
|
+
let lqco = context.entityType.metadataStore.localQueryComparisonOptions;
|
|
980
|
+
let predFn = getBinaryPredicateFn(this, dataType, lqco);
|
|
981
|
+
if (predFn == null) {
|
|
982
|
+
throw new Error("Invalid binaryPredicate operator:" + this.op.key);
|
|
983
|
+
}
|
|
984
|
+
return function (entity) {
|
|
985
|
+
return predFn(expr1Fn(entity), expr2Fn(entity));
|
|
986
|
+
};
|
|
987
|
+
},
|
|
988
|
+
andOrPredicate: function (context) {
|
|
989
|
+
let predFns = this.preds.map((pred) => {
|
|
990
|
+
return pred.visit(context);
|
|
991
|
+
});
|
|
992
|
+
switch (this.op.key) {
|
|
993
|
+
case "and":
|
|
994
|
+
return function (entity) {
|
|
995
|
+
let result = predFns.reduce(function (prev, cur) {
|
|
996
|
+
return prev && cur(entity);
|
|
997
|
+
}, true);
|
|
998
|
+
return result;
|
|
999
|
+
};
|
|
1000
|
+
case "or":
|
|
1001
|
+
return function (entity) {
|
|
1002
|
+
let result = predFns.reduce(function (prev, cur) {
|
|
1003
|
+
return prev || cur(entity);
|
|
1004
|
+
}, false);
|
|
1005
|
+
return result;
|
|
1006
|
+
};
|
|
1007
|
+
default:
|
|
1008
|
+
throw new Error("Invalid boolean operator:" + this.op.key);
|
|
1009
|
+
}
|
|
1010
|
+
},
|
|
1011
|
+
anyAllPredicate: function (context) {
|
|
1012
|
+
let exprFn = this.expr.visit(context);
|
|
1013
|
+
let newContext = core.extend({}, context);
|
|
1014
|
+
newContext.entityType = this.expr.dataType;
|
|
1015
|
+
let predFn = this.pred.visit(newContext);
|
|
1016
|
+
let anyAllPredFn = getAnyAllPredicateFn(this.op);
|
|
1017
|
+
return function (entity) {
|
|
1018
|
+
return anyAllPredFn(exprFn(entity), predFn);
|
|
1019
|
+
};
|
|
1020
|
+
},
|
|
1021
|
+
litExpr: function () {
|
|
1022
|
+
let value = this.value;
|
|
1023
|
+
return function (entity) {
|
|
1024
|
+
return value;
|
|
1025
|
+
};
|
|
1026
|
+
},
|
|
1027
|
+
propExpr: function () {
|
|
1028
|
+
let propertyPath = this.propertyPath;
|
|
1029
|
+
let properties = propertyPath.split('.');
|
|
1030
|
+
if (properties.length === 1) {
|
|
1031
|
+
return function (entity) {
|
|
1032
|
+
return entity.getProperty(propertyPath);
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
else {
|
|
1036
|
+
return function (entity) {
|
|
1037
|
+
return EntityAspect.getPropertyPathValue(entity, properties);
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
},
|
|
1041
|
+
fnExpr: function (context) {
|
|
1042
|
+
let exprFns = this.exprs.map(function (expr) {
|
|
1043
|
+
return expr.visit(context);
|
|
1044
|
+
});
|
|
1045
|
+
let that = this;
|
|
1046
|
+
return function (entity) {
|
|
1047
|
+
let values = exprFns.map(function (exprFn) {
|
|
1048
|
+
let value = exprFn(entity);
|
|
1049
|
+
return value;
|
|
1050
|
+
});
|
|
1051
|
+
let result = that.localFn.apply(null, values);
|
|
1052
|
+
return result;
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
function getAnyAllPredicateFn(op) {
|
|
1057
|
+
switch (op.key) {
|
|
1058
|
+
case "any":
|
|
1059
|
+
return function (v1, v2) {
|
|
1060
|
+
return v1.some(function (v) {
|
|
1061
|
+
return v2(v);
|
|
1062
|
+
});
|
|
1063
|
+
};
|
|
1064
|
+
case "all":
|
|
1065
|
+
return function (v1, v2) {
|
|
1066
|
+
return v1.every(function (v) {
|
|
1067
|
+
return v2(v);
|
|
1068
|
+
});
|
|
1069
|
+
};
|
|
1070
|
+
default:
|
|
1071
|
+
throw new Error("Unknown operator: " + op.key);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
function getBinaryPredicateFn(binaryPredicate, dataType, lqco) {
|
|
1075
|
+
let op = binaryPredicate.op;
|
|
1076
|
+
let mc = DataType.getComparableFn(dataType);
|
|
1077
|
+
let predFn;
|
|
1078
|
+
switch (op.key) {
|
|
1079
|
+
case 'eq':
|
|
1080
|
+
predFn = function (v1, v2) {
|
|
1081
|
+
if (v1 && typeof v1 === 'string') {
|
|
1082
|
+
return stringEquals(v1, v2, lqco);
|
|
1083
|
+
}
|
|
1084
|
+
else {
|
|
1085
|
+
return mc(v1) === mc(v2);
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
break;
|
|
1089
|
+
case 'ne':
|
|
1090
|
+
predFn = function (v1, v2) {
|
|
1091
|
+
if (v1 && typeof v1 === 'string') {
|
|
1092
|
+
return !stringEquals(v1, v2, lqco);
|
|
1093
|
+
}
|
|
1094
|
+
else {
|
|
1095
|
+
return mc(v1) !== mc(v2);
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
break;
|
|
1099
|
+
case 'gt':
|
|
1100
|
+
predFn = function (v1, v2) {
|
|
1101
|
+
return mc(v1) > mc(v2);
|
|
1102
|
+
};
|
|
1103
|
+
break;
|
|
1104
|
+
case 'ge':
|
|
1105
|
+
predFn = function (v1, v2) {
|
|
1106
|
+
return mc(v1) >= mc(v2);
|
|
1107
|
+
};
|
|
1108
|
+
break;
|
|
1109
|
+
case 'lt':
|
|
1110
|
+
predFn = function (v1, v2) {
|
|
1111
|
+
return mc(v1) < mc(v2);
|
|
1112
|
+
};
|
|
1113
|
+
break;
|
|
1114
|
+
case 'le':
|
|
1115
|
+
predFn = function (v1, v2) {
|
|
1116
|
+
return mc(v1) <= mc(v2);
|
|
1117
|
+
};
|
|
1118
|
+
break;
|
|
1119
|
+
case 'startswith':
|
|
1120
|
+
predFn = function (v1, v2) {
|
|
1121
|
+
return stringStartsWith(v1, v2, lqco);
|
|
1122
|
+
};
|
|
1123
|
+
break;
|
|
1124
|
+
case 'endswith':
|
|
1125
|
+
predFn = function (v1, v2) {
|
|
1126
|
+
return stringEndsWith(v1, v2, lqco);
|
|
1127
|
+
};
|
|
1128
|
+
break;
|
|
1129
|
+
case 'contains':
|
|
1130
|
+
predFn = function (v1, v2) {
|
|
1131
|
+
return stringContains(v1, v2, lqco);
|
|
1132
|
+
};
|
|
1133
|
+
break;
|
|
1134
|
+
case 'in':
|
|
1135
|
+
predFn = function (v1, v2) {
|
|
1136
|
+
v1 = mc(v1);
|
|
1137
|
+
v2 = v2.map(function (v) { return mc(v); });
|
|
1138
|
+
return v2.indexOf(v1) >= 0;
|
|
1139
|
+
};
|
|
1140
|
+
break;
|
|
1141
|
+
default:
|
|
1142
|
+
return null;
|
|
1143
|
+
}
|
|
1144
|
+
return predFn;
|
|
1145
|
+
}
|
|
1146
|
+
function stringEquals(a, b, lqco) {
|
|
1147
|
+
if (b == null)
|
|
1148
|
+
return false;
|
|
1149
|
+
if (typeof b !== 'string') {
|
|
1150
|
+
b = b.toString();
|
|
1151
|
+
}
|
|
1152
|
+
if (lqco.usesSql92CompliantStringComparison) {
|
|
1153
|
+
a = (a || "").trim();
|
|
1154
|
+
b = (b || "").trim();
|
|
1155
|
+
}
|
|
1156
|
+
if (!lqco.isCaseSensitive) {
|
|
1157
|
+
a = (a || "").toLowerCase();
|
|
1158
|
+
b = (b || "").toLowerCase();
|
|
1159
|
+
}
|
|
1160
|
+
return a === b;
|
|
1161
|
+
}
|
|
1162
|
+
function stringStartsWith(a, b, lqco) {
|
|
1163
|
+
if (!lqco.isCaseSensitive) {
|
|
1164
|
+
a = (a || "").toLowerCase();
|
|
1165
|
+
b = (b || "").toLowerCase();
|
|
1166
|
+
}
|
|
1167
|
+
return strStartsWith(a, b);
|
|
1168
|
+
}
|
|
1169
|
+
function stringEndsWith(a, b, lqco) {
|
|
1170
|
+
if (!lqco.isCaseSensitive) {
|
|
1171
|
+
a = (a || "").toLowerCase();
|
|
1172
|
+
b = (b || "").toLowerCase();
|
|
1173
|
+
}
|
|
1174
|
+
return strEndsWith(a, b);
|
|
1175
|
+
}
|
|
1176
|
+
function stringContains(a, b, lqco) {
|
|
1177
|
+
if (!lqco.isCaseSensitive) {
|
|
1178
|
+
a = (a || "").toLowerCase();
|
|
1179
|
+
b = (b || "").toLowerCase();
|
|
1180
|
+
}
|
|
1181
|
+
return a.indexOf(b) >= 0;
|
|
1182
|
+
}
|
|
1183
|
+
// toJSONVisitor
|
|
1184
|
+
let toJSONVisitor = {
|
|
1185
|
+
passthruPredicate: function () {
|
|
1186
|
+
return this.value;
|
|
1187
|
+
},
|
|
1188
|
+
unaryPredicate: function (context) {
|
|
1189
|
+
let predVal = this.pred.visit(context);
|
|
1190
|
+
let json = {};
|
|
1191
|
+
json[this.op.key] = predVal;
|
|
1192
|
+
return json;
|
|
1193
|
+
},
|
|
1194
|
+
binaryPredicate: function (context) {
|
|
1195
|
+
let expr1Val = this.expr1.visit(context);
|
|
1196
|
+
let expr2Val = this.expr2.visit(context);
|
|
1197
|
+
let json = {};
|
|
1198
|
+
if (this.expr2 instanceof PropExpr) {
|
|
1199
|
+
expr2Val = { value: expr2Val, isProperty: true };
|
|
1200
|
+
}
|
|
1201
|
+
if (this.op.key === "eq") {
|
|
1202
|
+
json[expr1Val] = expr2Val;
|
|
1203
|
+
}
|
|
1204
|
+
else {
|
|
1205
|
+
let value = {};
|
|
1206
|
+
json[expr1Val] = value;
|
|
1207
|
+
value[this.op.key] = expr2Val;
|
|
1208
|
+
}
|
|
1209
|
+
return json;
|
|
1210
|
+
},
|
|
1211
|
+
andOrPredicate: function (context) {
|
|
1212
|
+
let predVals = this.preds.map(function (pred) {
|
|
1213
|
+
return pred.visit(context);
|
|
1214
|
+
});
|
|
1215
|
+
if (!predVals || !predVals.length) {
|
|
1216
|
+
return {};
|
|
1217
|
+
}
|
|
1218
|
+
let json;
|
|
1219
|
+
// normalizeAnd clauses if possible.
|
|
1220
|
+
// passthru predicate will appear as string and their 'ands' can't be 'normalized'
|
|
1221
|
+
if (this.op.key === 'and' && predVals.length === 2 && !predVals.some((v) => v.or || typeof (v) === 'string')) {
|
|
1222
|
+
// normalize 'and' clauses - will return null if can't be combined.
|
|
1223
|
+
json = predVals.reduce(combine);
|
|
1224
|
+
}
|
|
1225
|
+
if (json == null) {
|
|
1226
|
+
json = {};
|
|
1227
|
+
json[this.op.key] = predVals;
|
|
1228
|
+
}
|
|
1229
|
+
return json;
|
|
1230
|
+
},
|
|
1231
|
+
anyAllPredicate: function (context) {
|
|
1232
|
+
let exprVal = this.expr.visit(context);
|
|
1233
|
+
let newContext = core.extend({}, context);
|
|
1234
|
+
newContext.entityType = this.expr.dataType;
|
|
1235
|
+
let predVal = this.pred.visit(newContext);
|
|
1236
|
+
let json = {};
|
|
1237
|
+
let value = {};
|
|
1238
|
+
value[this.op.key] = predVal;
|
|
1239
|
+
json[exprVal] = value;
|
|
1240
|
+
return json;
|
|
1241
|
+
},
|
|
1242
|
+
litExpr: function (context) {
|
|
1243
|
+
// special handling for DateOnly because it serializes improperly
|
|
1244
|
+
const value = (this.dataType === DataType.DateOnly) ? DataType.toDateOnlyString(this.value) : this.value;
|
|
1245
|
+
if (this.hasExplicitDataType || context.useExplicitDataType) {
|
|
1246
|
+
return { value: value, dataType: this.dataType.name };
|
|
1247
|
+
}
|
|
1248
|
+
else {
|
|
1249
|
+
return value;
|
|
1250
|
+
}
|
|
1251
|
+
},
|
|
1252
|
+
propExpr: function (context) {
|
|
1253
|
+
if (context.toNameOnServer) {
|
|
1254
|
+
if (!context.entityType) {
|
|
1255
|
+
console.warn(`No EntityType for propertyPath "${this.propertyPath}". ${core.strings.TO_TYPE}`);
|
|
1256
|
+
return this.propertyPath;
|
|
1257
|
+
}
|
|
1258
|
+
return context.entityType.clientPropertyPathToServer(this.propertyPath);
|
|
1259
|
+
}
|
|
1260
|
+
else {
|
|
1261
|
+
return this.propertyPath;
|
|
1262
|
+
}
|
|
1263
|
+
},
|
|
1264
|
+
fnExpr: function (context) {
|
|
1265
|
+
let exprVals = this.exprs.map(function (expr) {
|
|
1266
|
+
return expr.visit(context);
|
|
1267
|
+
});
|
|
1268
|
+
return this.fnName + "(" + exprVals.join(",") + ")";
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
function combine(j1, j2) {
|
|
1272
|
+
const a = j1, b = j2;
|
|
1273
|
+
let ok = Object.keys(b).every(function (key) {
|
|
1274
|
+
if (a.hasOwnProperty(key)) {
|
|
1275
|
+
if (typeof (b[key]) !== 'object') {
|
|
1276
|
+
// exit and indicate that we can't combine
|
|
1277
|
+
return false;
|
|
1278
|
+
}
|
|
1279
|
+
if (combine(a[key], b[key]) == null) {
|
|
1280
|
+
return false;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
else if (typeof (a) !== 'object') {
|
|
1284
|
+
// cannot assign to j1[key]
|
|
1285
|
+
return false;
|
|
1286
|
+
}
|
|
1287
|
+
else {
|
|
1288
|
+
a[key] = b[key];
|
|
1289
|
+
}
|
|
1290
|
+
return true;
|
|
1291
|
+
});
|
|
1292
|
+
return ok ? j1 : null;
|
|
1293
|
+
}
|
|
1294
|
+
//# sourceMappingURL=predicate.js.map
|