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,1149 @@
|
|
|
1
|
+
import { core, arrayFlatMap, stringStartsWith } from '../core/core.js';
|
|
2
|
+
import { assertParam } from '../core/assert-param.js';
|
|
3
|
+
import { DataType } from '../metadata/data-type.js';
|
|
4
|
+
import { EntityAspect } from '../entity/entity-aspect.js';
|
|
5
|
+
import { EntityKey } from '../entity/entity-key.js';
|
|
6
|
+
import { BreezeEnum } from '../core/enum.js';
|
|
7
|
+
import { DataService } from '../metadata/data-service.js';
|
|
8
|
+
import { EntityType, entityTypeForCtor } from '../metadata/entity-metadata.js';
|
|
9
|
+
import { QueryOptions } from './query-options.js';
|
|
10
|
+
import { Predicate } from './predicate.js';
|
|
11
|
+
/**
|
|
12
|
+
An EntityQuery instance is used to query entities either from a remote datasource or from a local {@link EntityManager}.
|
|
13
|
+
|
|
14
|
+
EntityQueries are immutable - this means that all EntityQuery methods that return an EntityQuery actually create a new EntityQuery. This means that
|
|
15
|
+
EntityQueries can be 'modified' without affecting any current instances.
|
|
16
|
+
*/
|
|
17
|
+
export class EntityQuery {
|
|
18
|
+
/** Constructor. Most queries start from the static {@link EntityQuery.from} instead, which takes a
|
|
19
|
+
registered entity class and so gives a typed query:
|
|
20
|
+
```ts
|
|
21
|
+
const query = EntityQuery.from(Customer); // EntityQuery<Customer>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
When a constructor is needed, supply the type argument. As with a type argument on
|
|
25
|
+
`EntityQuery.from(resourceName)`, nothing checks it against the resource name:
|
|
26
|
+
```ts
|
|
27
|
+
const query = new EntityQuery<Customer>("Customers");
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Usually this constructor will be followed by calls to filtering, ordering or selection methods
|
|
31
|
+
```ts
|
|
32
|
+
const query = new EntityQuery<Customer>("Customers")
|
|
33
|
+
.where("companyName", "startsWith", "C")
|
|
34
|
+
.orderBy("region");
|
|
35
|
+
```
|
|
36
|
+
@param resourceName - either a resource name or a serialized EntityQuery ( created by {@link EntityQuery.toJSON})
|
|
37
|
+
*/
|
|
38
|
+
constructor(resourceName) {
|
|
39
|
+
if (resourceName != null && (typeof resourceName !== 'string')) {
|
|
40
|
+
return fromJSON(this, resourceName);
|
|
41
|
+
}
|
|
42
|
+
// The cast holds for test/tsconfig.json, which checks src/ again with strictNullChecks off;
|
|
43
|
+
// without it the guard above narrows to `string | Object` rather than `string | undefined`.
|
|
44
|
+
this.resourceName = resourceName;
|
|
45
|
+
this.fromEntityType = undefined;
|
|
46
|
+
this.wherePredicate = undefined;
|
|
47
|
+
this.orderByClause = undefined;
|
|
48
|
+
this.selectClause = undefined;
|
|
49
|
+
this.skipCount = undefined;
|
|
50
|
+
this.takeCount = undefined;
|
|
51
|
+
this.expandClause = undefined;
|
|
52
|
+
this.parameters = {};
|
|
53
|
+
this.inlineCountEnabled = false;
|
|
54
|
+
this.noTrackingEnabled = false;
|
|
55
|
+
this.usePostEnabled = false;
|
|
56
|
+
// default is to get queryOptions and dataService from the entityManager.
|
|
57
|
+
// this.queryOptions = new QueryOptions();
|
|
58
|
+
// this.dataService = new DataService();
|
|
59
|
+
this.entityManager = undefined;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
Specifies the resource to query for this EntityQuery.
|
|
63
|
+
```ts
|
|
64
|
+
const query = new EntityQuery()
|
|
65
|
+
.from("Customers");
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
is the same as
|
|
69
|
+
```ts
|
|
70
|
+
const query = new EntityQuery("Customers");
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
For a typed query, use the static `EntityQuery.from(Customer)` instead.
|
|
74
|
+
@param resourceName - The resource to query.
|
|
75
|
+
*/
|
|
76
|
+
from(resourceName) {
|
|
77
|
+
// TODO: think about allowing entityType as well
|
|
78
|
+
assertParam(resourceName, "resourceName").isString().check();
|
|
79
|
+
return clone(this, "resourceName", resourceName);
|
|
80
|
+
}
|
|
81
|
+
static from(arg) {
|
|
82
|
+
if (typeof arg === 'function') {
|
|
83
|
+
const entityType = entityTypeForCtor(arg);
|
|
84
|
+
if (!entityType.defaultResourceName) {
|
|
85
|
+
throw new Error(`'${entityType.name}' has no defaultResourceName, so a query cannot be built from the constructor. ` +
|
|
86
|
+
`Use EntityQuery.from(resourceName).toType(...) instead, or call metadataStore.setEntityTypeForResourceName.`);
|
|
87
|
+
}
|
|
88
|
+
return new EntityQuery(entityType.defaultResourceName);
|
|
89
|
+
}
|
|
90
|
+
assertParam(arg, "resourceName").isString().check();
|
|
91
|
+
return new EntityQuery(arg);
|
|
92
|
+
}
|
|
93
|
+
toType(entityType) {
|
|
94
|
+
// Naming the type is what `toType` is for, so a registered constructor names it *and* types
|
|
95
|
+
// the query - and unlike a type argument on `from(resourceName)`, this one is checked against
|
|
96
|
+
// the metadata.
|
|
97
|
+
if (typeof entityType === 'function') {
|
|
98
|
+
entityType = entityTypeForCtor(entityType);
|
|
99
|
+
}
|
|
100
|
+
assertParam(entityType, "entityType").isString().or().isInstanceOf(EntityType).check();
|
|
101
|
+
return clone(this, "resultEntityType", entityType);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
Returns a new query with an added filter criteria; Can be called multiple times which means to 'and' with any existing
|
|
105
|
+
Predicate or can be called with null to clear all predicates.
|
|
106
|
+
```ts
|
|
107
|
+
const query = EntityQuery.from(Customer)
|
|
108
|
+
.where("companyName", "startsWith", "C");
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
or in object form
|
|
112
|
+
```ts
|
|
113
|
+
const query = EntityQuery.from(Customer)
|
|
114
|
+
.where({ companyName: { startsWith: "C" } });
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
This can also be expressed using an explicit {@link FilterQueryOp} (the operator is then not checked against the property's type) as
|
|
118
|
+
```ts
|
|
119
|
+
const query = EntityQuery.from(Customer)
|
|
120
|
+
.where("companyName", FilterQueryOp.StartsWith, "C");
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
or a preconstructed {@link Predicate} may be used
|
|
124
|
+
```ts
|
|
125
|
+
const p = Predicate.for(Customer);
|
|
126
|
+
const query = EntityQuery.from(Customer).where(p("companyName", "startsWith", "C"));
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Predicates are often useful when you want to combine multiple conditions in a single filter, such as
|
|
130
|
+
```ts
|
|
131
|
+
const pred = p("companyName", "startsWith", "C").and(p("region", "eq", null));
|
|
132
|
+
const query = EntityQuery.from(Customer)
|
|
133
|
+
.where(pred);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
More complicated queries can make use of nested property paths
|
|
137
|
+
```ts
|
|
138
|
+
const query = EntityQuery.from(Product)
|
|
139
|
+
.where("category.categoryName", "startsWith", "S");
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
or query functions - A list of valid functions can be found within the {@link Predicate} documentation.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
const query = EntityQuery.from(Customer)
|
|
146
|
+
.where("toLower(companyName)", "startsWith", "c");
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
or to be even more baroque
|
|
150
|
+
```ts
|
|
151
|
+
const query = EntityQuery.from(Customer)
|
|
152
|
+
.where("toUpper(substring(companyName, 1, 2))", FilterQueryOp.Equals, "OM");
|
|
153
|
+
```
|
|
154
|
+
@param predicate - Can be either
|
|
155
|
+
- a single {@link Predicate}
|
|
156
|
+
|
|
157
|
+
- the parameters to create a 'simple' Predicate
|
|
158
|
+
- - a property name, a property path with '.' as path seperators or a property expression {String}
|
|
159
|
+
- - an operator - {@link FilterQueryOp} or it's string representation. Case is ignored
|
|
160
|
+
when if a string is provided and any string that matches one of the FilterQueryOp aliases will be accepted.
|
|
161
|
+
- - a value {Object} - This will be treated as either a property expression or a literal depending on context.
|
|
162
|
+
In general, if the value can be interpreted as a property expression it will be, otherwise it will be treated as a literal.
|
|
163
|
+
In most cases this works well, but you can also force the interpretation by making the value argument itself an object
|
|
164
|
+
with a 'value' property and an 'isLiteral' property set to either true or false.
|
|
165
|
+
Breeze also tries to infer the dataType of any literal based on context, if this fails you can force this inference by making the value argument
|
|
166
|
+
an object with a 'value' property and a 'dataType'property set to one of the DataType enumeration instances.
|
|
167
|
+
|
|
168
|
+
- a null or undefined ( this causes any existing where clause to be removed)
|
|
169
|
+
*/
|
|
170
|
+
where(...args) {
|
|
171
|
+
let wherePredicate;
|
|
172
|
+
if (args.length > 0 && args[0] != null) {
|
|
173
|
+
wherePredicate = Predicate.create(...args);
|
|
174
|
+
if (this.fromEntityType)
|
|
175
|
+
wherePredicate._validate(this.fromEntityType);
|
|
176
|
+
if (this.wherePredicate) {
|
|
177
|
+
wherePredicate = this.wherePredicate.and(wherePredicate);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return clone(this, "wherePredicate", wherePredicate);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
Returns a new query that orders the results of the query by property name. By default sorting occurs is ascending order, but sorting in descending order is supported as well.
|
|
184
|
+
OrderBy clauses may be chained.
|
|
185
|
+
```ts
|
|
186
|
+
const query = EntityQuery.from(Customer)
|
|
187
|
+
.orderBy("companyName");
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
or to sort across multiple properties
|
|
191
|
+
```ts
|
|
192
|
+
const query = EntityQuery.from(Customer)
|
|
193
|
+
.orderBy(["region", "companyName"]);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
A comma-separated string, `"region, companyName"`, is also accepted, but its paths are not checked.
|
|
197
|
+
|
|
198
|
+
Nested property paths are also supported
|
|
199
|
+
```ts
|
|
200
|
+
const query = EntityQuery.from(Product)
|
|
201
|
+
.orderBy("category.categoryName");
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Sorting in descending order is supported via the addition of ' desc' to the end of any property path.
|
|
205
|
+
```ts
|
|
206
|
+
const query = EntityQuery.from(Customer)
|
|
207
|
+
.orderBy("companyName desc");
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
or
|
|
211
|
+
```ts
|
|
212
|
+
const query = EntityQuery.from(Customer)
|
|
213
|
+
.orderBy(["region desc", "companyName desc"]);
|
|
214
|
+
```
|
|
215
|
+
@param propertyPaths - A comma-separated (',') string of property paths or an array of property paths.
|
|
216
|
+
Each property path can optionally end with " desc" to force a descending sort order. If 'propertyPaths' is either null or omitted then all ordering is removed.
|
|
217
|
+
@param isDescending - If specified, overrides all of the embedded 'desc' tags in the previously specified property paths.
|
|
218
|
+
*/
|
|
219
|
+
orderBy(propertyPaths, isDescending) {
|
|
220
|
+
// propertyPaths: can pass in create("A.X,B") or create("A.X desc, B") or create("A.X desc,B", true])
|
|
221
|
+
// isDesc parameter trumps isDesc in propertyName.
|
|
222
|
+
let orderByClause = propertyPaths == null ? null : new OrderByClause(normalizePropertyPaths(propertyPaths), isDescending);
|
|
223
|
+
if (this.orderByClause && orderByClause) {
|
|
224
|
+
orderByClause = new OrderByClause([this.orderByClause, orderByClause]);
|
|
225
|
+
}
|
|
226
|
+
return clone(this, "orderByClause", orderByClause);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
Returns a new query that orders the results of the query by property name in descending order.
|
|
230
|
+
```ts
|
|
231
|
+
const query = EntityQuery.from(Customer)
|
|
232
|
+
.orderByDesc("companyName");
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
or to sort across multiple properties
|
|
236
|
+
```ts
|
|
237
|
+
const query = EntityQuery.from(Customer)
|
|
238
|
+
.orderByDesc(["region", "companyName"]);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Nested property paths are also supported
|
|
242
|
+
```ts
|
|
243
|
+
const query = EntityQuery.from(Product)
|
|
244
|
+
.orderByDesc("category.categoryName");
|
|
245
|
+
```
|
|
246
|
+
@param propertyPaths - A comma-separated (',') string of property paths or an array of property paths.
|
|
247
|
+
If 'propertyPaths' is either null or omitted then all ordering is removed.
|
|
248
|
+
*/
|
|
249
|
+
orderByDesc(propertyPaths) {
|
|
250
|
+
return this.orderBy(propertyPaths, true);
|
|
251
|
+
}
|
|
252
|
+
select(propertyPaths) {
|
|
253
|
+
let selectClause = propertyPaths == null ? null : new SelectClause(normalizePropertyPaths(propertyPaths));
|
|
254
|
+
// A projection no longer returns the entity type, so the type parameter is dropped rather
|
|
255
|
+
// than carried forward as something the results will not be.
|
|
256
|
+
return clone(this, "selectClause", selectClause);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
Returns a new query that skips the specified number of entities when returning results.
|
|
260
|
+
Any existing 'skip' can be cleared by calling 'skip' with no arguments.
|
|
261
|
+
```ts
|
|
262
|
+
const query = EntityQuery.from(Customer)
|
|
263
|
+
.where("companyName", "startsWith", "C")
|
|
264
|
+
.skip(5);
|
|
265
|
+
```
|
|
266
|
+
@param count - The number of entities to skip over. If omitted or null any existing skip count on the query is removed.
|
|
267
|
+
*/
|
|
268
|
+
skip(count) {
|
|
269
|
+
assertParam(count, "count").isOptional().isNumber().check();
|
|
270
|
+
return clone(this, "skipCount", (count == null) ? null : count);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
Returns a new query that returns only the specified number of entities when returning results. - Same as 'take'.
|
|
274
|
+
Any existing 'top' can be cleared by calling 'top' with no arguments.
|
|
275
|
+
```ts
|
|
276
|
+
const query = EntityQuery.from(Customer)
|
|
277
|
+
.top(5);
|
|
278
|
+
```
|
|
279
|
+
@param count - The number of entities to return.
|
|
280
|
+
If 'count' is either null or omitted then any existing 'top' count on the query is removed.
|
|
281
|
+
*/
|
|
282
|
+
top(count) {
|
|
283
|
+
return this.take(count);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
Returns a new query that returns only the specified number of entities when returning results - Same as 'top'.
|
|
287
|
+
Any existing take can be cleared by calling take with no arguments.
|
|
288
|
+
```ts
|
|
289
|
+
const query = EntityQuery.from(Customer)
|
|
290
|
+
.take(5);
|
|
291
|
+
```
|
|
292
|
+
@param count - The number of entities to return.
|
|
293
|
+
If 'count' is either null or omitted then any existing 'take' count on the query is removed.
|
|
294
|
+
*/
|
|
295
|
+
take(count) {
|
|
296
|
+
assertParam(count, "count").isOptional().isNumber().check();
|
|
297
|
+
return clone(this, "takeCount", (count == null) ? null : count);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
Returns a new query that will return related entities nested within its results. The expand method allows you to identify related entities, via navigation property
|
|
301
|
+
names such that a graph of entities may be retrieved with a single request. Any filtering occurs before the results are 'expanded'.
|
|
302
|
+
```ts
|
|
303
|
+
const query = EntityQuery.from(Customer)
|
|
304
|
+
.where("companyName", "startsWith", "C")
|
|
305
|
+
.expand("orders");
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
will return the filtered customers each with its "orders" properties fully resolved.
|
|
309
|
+
Multiple paths may be specified in an array
|
|
310
|
+
```ts
|
|
311
|
+
const query = EntityQuery.from(Order)
|
|
312
|
+
.expand(["customer", "employee"]);
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
and nested property paths my be specified as well
|
|
316
|
+
```ts
|
|
317
|
+
const query = EntityQuery.from(Order)
|
|
318
|
+
.expand(["customer", "orderDetails", "orderDetails.product"]);
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
The paths may also be separated by a ',' in a single string, `"customer, employee"`, but those paths are not checked.
|
|
322
|
+
@param propertyPaths - A comma-separated list of navigation property names or an array of navigation property names. Each Navigation Property name can be followed
|
|
323
|
+
by a '.' and another navigation property name to enable identifying a multi-level relationship.
|
|
324
|
+
If 'propertyPaths' is either null or omitted then any existing 'expand' clause on the query is removed.
|
|
325
|
+
*/
|
|
326
|
+
expand(propertyPaths) {
|
|
327
|
+
let expandClause = propertyPaths == null ? null : new ExpandClause(normalizePropertyPaths(propertyPaths));
|
|
328
|
+
return clone(this, "expandClause", expandClause);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
Returns a new query that includes a collection of parameters to pass to the server.
|
|
332
|
+
```ts
|
|
333
|
+
const query = EntityQuery.from("EmployeesFilteredByCountryAndBirthdate")
|
|
334
|
+
.toType(Employee)
|
|
335
|
+
.withParameters({ birthDate: "1/1/1960", country: "USA" });
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
will call the 'EmployeesFilteredByCountryAndBirthdate' method on the server and pass in 2 parameters. This
|
|
339
|
+
query will be uri encoded as
|
|
340
|
+
```ts
|
|
341
|
+
{serviceApi}/EmployeesFilteredByCountryAndBirthdate?birthDate=1%2F1%2F1960&country=USA
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Parameters may also be mixed in with other query criteria.
|
|
345
|
+
```ts
|
|
346
|
+
const query = EntityQuery.from("EmployeesFilteredByCountryAndBirthdate")
|
|
347
|
+
.toType(Employee)
|
|
348
|
+
.withParameters({ birthDate: "1/1/1960", country: "USA" })
|
|
349
|
+
.where("lastName", "startsWith", "S")
|
|
350
|
+
.orderBy("birthDate");
|
|
351
|
+
```
|
|
352
|
+
@param parameters - A parameters object where the keys are the parameter names and the values are the parameter values.
|
|
353
|
+
*/
|
|
354
|
+
withParameters(parameters) {
|
|
355
|
+
assertParam(parameters, "parameters").isObject().check();
|
|
356
|
+
return clone(this, "parameters", parameters);
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
Returns a query with the `inlineCount` capability either enabled or disabled. With `inlineCount` enabled, an additional 'inlineCount' property
|
|
360
|
+
will be returned with the query results that will contain the number of entities that would have been returned by this
|
|
361
|
+
query with only the 'where'/'filter' clauses applied, i.e. without any 'skip'/'take' operators applied. For local queries this clause is ignored.
|
|
362
|
+
```ts
|
|
363
|
+
const query = EntityQuery.from(Customer)
|
|
364
|
+
.take(20)
|
|
365
|
+
.orderBy("companyName")
|
|
366
|
+
.inlineCount(true);
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
will return the first 20 customers as well as a count of _all_ of the customers in the remote store.
|
|
370
|
+
@param enabled - (default = true) Whether or not inlineCount capability should be enabled. If this parameter is omitted, true is assumed.
|
|
371
|
+
*/
|
|
372
|
+
inlineCount(enabled) {
|
|
373
|
+
assertParam(enabled, "enabled").isBoolean().isOptional().check();
|
|
374
|
+
enabled = (enabled === undefined) ? true : !!enabled;
|
|
375
|
+
return clone(this, "inlineCountEnabled", enabled);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
Returns a query whose property paths are the server's names rather than the client's, so Breeze
|
|
379
|
+
sends them as written instead of translating them with the `MetadataStore`'s naming convention.
|
|
380
|
+
```ts
|
|
381
|
+
EntityQuery.from('Customers').where('CompanyName', 'startsWith', 'A').useNameOnServer();
|
|
382
|
+
```
|
|
383
|
+
The query is built from a resource name because a typed query, `EntityQuery.from(Customer)`,
|
|
384
|
+
checks its paths against the client's names and would reject `'CompanyName'`.
|
|
385
|
+
Only the query sent to the server is affected: run against the cache, a query works on the
|
|
386
|
+
client's objects and needs the client's names.
|
|
387
|
+
@param usesNameOnServer - (default = true)
|
|
388
|
+
*/
|
|
389
|
+
useNameOnServer(usesNameOnServer) {
|
|
390
|
+
assertParam(usesNameOnServer, "usesNameOnServer").isBoolean().isOptional().check();
|
|
391
|
+
usesNameOnServer = (usesNameOnServer === undefined) ? true : !!usesNameOnServer;
|
|
392
|
+
return clone(this, "usesNameOnServer", usesNameOnServer);
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
Returns a query with the `noTracking` capability either enabled or disabled. With `noTracking` enabled, the results of this query
|
|
396
|
+
will not be coerced into entities but will instead look like raw javascript projections. i.e. simple javascript objects.
|
|
397
|
+
```ts
|
|
398
|
+
const query = EntityQuery.from(Customer)
|
|
399
|
+
.take(20)
|
|
400
|
+
.orderBy("companyName")
|
|
401
|
+
.noTracking(true);
|
|
402
|
+
```
|
|
403
|
+
@param enabled - (default = true) Whether or not the noTracking capability should be enabled. If this parameter is omitted, true is assumed.
|
|
404
|
+
*/
|
|
405
|
+
noTracking(enabled) {
|
|
406
|
+
assertParam(enabled, "enabled").isBoolean().isOptional().check();
|
|
407
|
+
enabled = (enabled === undefined) ? true : !!enabled;
|
|
408
|
+
return clone(this, "noTrackingEnabled", enabled);
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
Returns a query with the `usePost` capability either enabled or disabled. With `usePost` enabled, the query is sent
|
|
412
|
+
as a POST request (instead of GET) and the query expression will be sent as JSON in the body of the post.
|
|
413
|
+
Note that the server must be able to parse the body of the request; otherwise the query expression will be ignored.
|
|
414
|
+
```ts
|
|
415
|
+
const query = EntityQuery.from(Order)
|
|
416
|
+
.where("freight", "gt", 100)
|
|
417
|
+
.usePost(true);
|
|
418
|
+
```
|
|
419
|
+
results in a POST request to `{host}/{path}/Orders`
|
|
420
|
+
with body `{"where": {"Freight":{"gt":100}}}`
|
|
421
|
+
@param enabled - (default = true) Whether or not usePost should be enabled. If this parameter is omitted, true is assumed.
|
|
422
|
+
*/
|
|
423
|
+
usePost(enabled) {
|
|
424
|
+
assertParam(enabled, "enabled").isBoolean().isOptional().check();
|
|
425
|
+
enabled = (enabled === undefined) ? true : !!enabled;
|
|
426
|
+
return clone(this, "usePostEnabled", enabled);
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
Returns a copy of this EntityQuery with the specified {@link EntityManager}, {@link DataService},
|
|
430
|
+
{@link JsonResultsAdapter}, {@link MergeStrategy} or {@link FetchStrategy} applied.
|
|
431
|
+
```ts
|
|
432
|
+
// 'using' can be used to return a new query with a specified EntityManager.
|
|
433
|
+
const em = new EntityManager(serviceName);
|
|
434
|
+
const query = EntityQuery.from(Order)
|
|
435
|
+
.using(em);
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
or with a specified {@link MergeStrategy}
|
|
439
|
+
```ts
|
|
440
|
+
const query = EntityQuery.from(Order)
|
|
441
|
+
.using(MergeStrategy.PreserveChanges);
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
or with a specified {@link FetchStrategy}
|
|
445
|
+
```ts
|
|
446
|
+
const query = EntityQuery.from(Order)
|
|
447
|
+
.using(FetchStrategy.FromLocalCache);
|
|
448
|
+
```
|
|
449
|
+
@param obj - The object to update in creating a new EntityQuery from an existing one.
|
|
450
|
+
*/
|
|
451
|
+
using(obj) {
|
|
452
|
+
if (!obj)
|
|
453
|
+
return this;
|
|
454
|
+
let eq = clone(this);
|
|
455
|
+
processUsing(eq, {
|
|
456
|
+
"entityManager": null,
|
|
457
|
+
"dataService": null,
|
|
458
|
+
"queryOptions": null,
|
|
459
|
+
"fetchStrategy": (eq, val) => {
|
|
460
|
+
eq.queryOptions = (eq.queryOptions || new QueryOptions()).using(val);
|
|
461
|
+
},
|
|
462
|
+
"mergeStrategy": (eq, val) => {
|
|
463
|
+
eq.queryOptions = (eq.queryOptions || new QueryOptions()).using(val);
|
|
464
|
+
},
|
|
465
|
+
"jsonResultsAdapter": (eq, val) => {
|
|
466
|
+
eq.dataService = (eq.dataService || new DataService()).using({ jsonResultsAdapter: val });
|
|
467
|
+
}
|
|
468
|
+
}, obj);
|
|
469
|
+
return eq;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
Executes this query. This method requires that an EntityManager has been previously specified via the "using" method.
|
|
473
|
+
|
|
474
|
+
It returns a promise:
|
|
475
|
+
```ts
|
|
476
|
+
const em = new EntityManager(serviceName);
|
|
477
|
+
const query = EntityQuery.from(Order).using(em);
|
|
478
|
+
const data = await query.execute(); // rejects if the query fails
|
|
479
|
+
const orders = data.results; // Order[]
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
The `callback` and `errorCallback` arguments are deprecated. They still work, but the
|
|
483
|
+
promise is the supported form and the callbacks will be removed in a future major version.
|
|
484
|
+
|
|
485
|
+
This method is the same as calling the EntityManager 'executeQuery' method.
|
|
486
|
+
```ts
|
|
487
|
+
const em = new EntityManager(serviceName);
|
|
488
|
+
const query = EntityQuery.from(Order);
|
|
489
|
+
const data = await em.executeQuery(query);
|
|
490
|
+
const orders = data.results; // Order[]
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
@param callback - Deprecated. Function called on success.
|
|
494
|
+
@param errorCallback - Deprecated. Function called on failure.
|
|
495
|
+
@returns Promise
|
|
496
|
+
*/
|
|
497
|
+
execute(callback, errorCallback) {
|
|
498
|
+
if (!this.entityManager) {
|
|
499
|
+
throw new Error("An EntityQuery must have its EntityManager property set before calling 'execute'");
|
|
500
|
+
}
|
|
501
|
+
return this.entityManager.executeQuery(this, callback, errorCallback);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
Executes this query against the local cache. This method requires that an EntityManager have been previously specified via the "using" method.
|
|
505
|
+
```ts
|
|
506
|
+
// assume em is an entityManager already filled with order entities;
|
|
507
|
+
const query = EntityQuery.from(Order).using(em);
|
|
508
|
+
const orders = query.executeLocally(); // Order[]
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
Note that calling this method is the same as calling {@link EntityManager.executeQueryLocally}.
|
|
512
|
+
*/
|
|
513
|
+
executeLocally() {
|
|
514
|
+
if (!this.entityManager) {
|
|
515
|
+
throw new Error("An EntityQuery must have its EntityManager property set before calling 'executeLocally'");
|
|
516
|
+
}
|
|
517
|
+
return this.entityManager.executeQueryLocally(this);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
Executes this query against the server and returns only the number of matching entities, without
|
|
521
|
+
materializing any of them. Requires an EntityManager, set via {@link EntityQuery.using}.
|
|
522
|
+
```ts
|
|
523
|
+
const count = await EntityQuery.from(Order).where("freight", ">", 100).using(em).executeCount();
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
It is `take(0).inlineCount(true)` and reads `inlineCount` off the result, so the server must
|
|
527
|
+
support inline count.
|
|
528
|
+
*/
|
|
529
|
+
async executeCount() {
|
|
530
|
+
if (!this.entityManager) {
|
|
531
|
+
throw new Error("An EntityQuery must have its EntityManager property set before calling 'executeCount'");
|
|
532
|
+
}
|
|
533
|
+
const qr = await this.take(0).inlineCount(true).execute();
|
|
534
|
+
return qr.inlineCount;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
Returns the serializable form of this query: its resource name, clauses, parameters and options,
|
|
538
|
+
with client property names. `JSON.stringify` calls it, and passing the parsed JSON to the
|
|
539
|
+
`EntityQuery` constructor recreates the query. The `EntityManager` and `DataService` are not
|
|
540
|
+
included.
|
|
541
|
+
*/
|
|
542
|
+
toJSON() {
|
|
543
|
+
const json = this.toJSONExt();
|
|
544
|
+
// usePost is not part of the query sent to the server, so toJSONExt leaves it out;
|
|
545
|
+
// it is part of the query itself, and fromJSON reads it back.
|
|
546
|
+
if (this.usePostEnabled)
|
|
547
|
+
json.usePost = true;
|
|
548
|
+
return json;
|
|
549
|
+
}
|
|
550
|
+
/** Typically only for use when building UriBuilderAdapters.
|
|
551
|
+
@hidden @internal
|
|
552
|
+
*/
|
|
553
|
+
toJSONExt(context) {
|
|
554
|
+
context = context || {};
|
|
555
|
+
context.entityType = context.entityType || this.fromEntityType;
|
|
556
|
+
// A query written in the server's names (useNameOnServer) is sent as it is.
|
|
557
|
+
if (this.usesNameOnServer)
|
|
558
|
+
context.toNameOnServer = false;
|
|
559
|
+
context.propertyPathFn = context.toNameOnServer ? context.entityType.clientPropertyPathToServer.bind(context.entityType) : core.identity;
|
|
560
|
+
let toJSONExtFn = function (v) {
|
|
561
|
+
return v ? v.toJSONExt(context) : undefined;
|
|
562
|
+
};
|
|
563
|
+
return core.toJson(this, {
|
|
564
|
+
"from,resourceName": null,
|
|
565
|
+
"toType,resultEntityType": function (v) {
|
|
566
|
+
// resultEntityType can be either a string or an entityType
|
|
567
|
+
return v ? (typeof v === 'string' ? v : v.name) : undefined;
|
|
568
|
+
},
|
|
569
|
+
"where,wherePredicate": toJSONExtFn,
|
|
570
|
+
"orderBy,orderByClause": toJSONExtFn,
|
|
571
|
+
"select,selectClause": toJSONExtFn,
|
|
572
|
+
"expand,expandClause": toJSONExtFn,
|
|
573
|
+
"skip,skipCount": null,
|
|
574
|
+
"take,takeCount": null,
|
|
575
|
+
parameters: function (v) {
|
|
576
|
+
return core.isEmpty(v) ? undefined : v;
|
|
577
|
+
},
|
|
578
|
+
"inlineCount,inlineCountEnabled": false,
|
|
579
|
+
"noTracking,noTrackingEnabled": false,
|
|
580
|
+
queryOptions: null
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
Static method that creates an EntityQuery that will allow 'requerying' an entity or a collection of entities by primary key. This can be useful
|
|
585
|
+
to force a requery of selected entities, or to restrict an existing collection of entities according to some filter.
|
|
586
|
+
|
|
587
|
+
Works for a single entity or an array of entities of the SAME type.
|
|
588
|
+
Does not work for an array of entities of different types.
|
|
589
|
+
```ts
|
|
590
|
+
// assuming 'customers' is an array of 'Customer' entities retrieved earlier.
|
|
591
|
+
const customersQuery = EntityQuery.fromEntities(customers); // EntityQuery<Customer>
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
The resulting query can, of course, be extended
|
|
595
|
+
```ts
|
|
596
|
+
// assuming 'customers' is an array of 'Customer' entities retrieved earlier.
|
|
597
|
+
const customersQuery = EntityQuery.fromEntities(customers)
|
|
598
|
+
.where("region", "ne", null);
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
Single entities can requeried as well.
|
|
602
|
+
```ts
|
|
603
|
+
// assuming 'customer' is a 'Customer' entity retrieved earlier.
|
|
604
|
+
const customerQuery = EntityQuery.fromEntities(customer);
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
will create a query that will return an array containing a single customer entity.
|
|
608
|
+
@param entities - The entities for which we want to create an EntityQuery.
|
|
609
|
+
*/
|
|
610
|
+
static fromEntities(entities) {
|
|
611
|
+
assertParam(entities, "entities").isEntity().or().isNonEmptyArray().isEntity().check();
|
|
612
|
+
let ents = (Array.isArray(entities)) ? entities : [entities];
|
|
613
|
+
let firstEntity = ents[0];
|
|
614
|
+
let type = firstEntity.entityType;
|
|
615
|
+
if (ents.some(function (e) {
|
|
616
|
+
return e.entityType !== type;
|
|
617
|
+
})) {
|
|
618
|
+
throw new Error("All 'fromEntities' must be the same type; at least one is not of type " +
|
|
619
|
+
type.name);
|
|
620
|
+
}
|
|
621
|
+
let q = new EntityQuery(type.defaultResourceName);
|
|
622
|
+
let preds = ents.map(function (entity) {
|
|
623
|
+
return buildPredicate(entity);
|
|
624
|
+
});
|
|
625
|
+
let pred = Predicate.or(preds);
|
|
626
|
+
q = q.where(pred);
|
|
627
|
+
let em = firstEntity.entityAspect.entityManager;
|
|
628
|
+
if (em) {
|
|
629
|
+
q = q.using(em);
|
|
630
|
+
}
|
|
631
|
+
return q;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
Creates an EntityQuery for the specified {@link EntityKey}.
|
|
635
|
+
```ts
|
|
636
|
+
const entityKey = new EntityKey(Employee, 1);
|
|
637
|
+
const query = EntityQuery.fromEntityKey(entityKey);
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
or
|
|
641
|
+
```ts
|
|
642
|
+
// 'employee' is a previously queried employee
|
|
643
|
+
const entityKey = employee.entityAspect.getKey();
|
|
644
|
+
const query = EntityQuery.fromEntityKey(entityKey);
|
|
645
|
+
```
|
|
646
|
+
@param entityKey - The {@link EntityKey} for which a query will be created.
|
|
647
|
+
*/
|
|
648
|
+
static fromEntityKey(entityKey) {
|
|
649
|
+
assertParam(entityKey, "entityKey").isInstanceOf(EntityKey).check();
|
|
650
|
+
let q = new EntityQuery(entityKey.entityType.defaultResourceName);
|
|
651
|
+
let pred = buildKeyPredicate(entityKey);
|
|
652
|
+
q = q.where(pred).toType(entityKey.entityType);
|
|
653
|
+
return q;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
Creates an EntityQuery for the specified entity and {@link NavigationProperty}.
|
|
657
|
+
```ts
|
|
658
|
+
// 'employee' is a previously queried employee
|
|
659
|
+
const query = EntityQuery.fromEntityNavigation(employee, "orders");
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
will return a query for the "orders" of the specified 'employee'. The {@link NavigationProperty}
|
|
663
|
+
itself may be passed instead of its name:
|
|
664
|
+
```ts
|
|
665
|
+
const ordersNavProp = employee.entityType.getNavigationProperty("orders");
|
|
666
|
+
const query = EntityQuery.fromEntityNavigation(employee, ordersNavProp);
|
|
667
|
+
```
|
|
668
|
+
@param entity - The Entity whose navigation property will be queried.
|
|
669
|
+
@param navigationProperty - The {@link NavigationProperty} or name of the NavigationProperty to be queried.
|
|
670
|
+
*/
|
|
671
|
+
static fromEntityNavigation = function (entity, navigationProperty) {
|
|
672
|
+
assertParam(entity, "entity").isEntity().check();
|
|
673
|
+
let navProperty = entity.entityType._checkNavProperty(navigationProperty);
|
|
674
|
+
let q = new EntityQuery(navProperty.entityType.defaultResourceName);
|
|
675
|
+
let pred = buildNavigationPredicate(entity, navProperty);
|
|
676
|
+
if (pred == null) {
|
|
677
|
+
throw new Error("Unable to create a NavigationQuery for navigationProperty: " + navProperty.name);
|
|
678
|
+
}
|
|
679
|
+
q = q.where(pred);
|
|
680
|
+
let em = entity.entityAspect.entityManager;
|
|
681
|
+
return em ? q.using(em) : q;
|
|
682
|
+
};
|
|
683
|
+
// protected methods
|
|
684
|
+
/** @hidden @internal */
|
|
685
|
+
_getFromEntityType(metadataStore, throwErrorIfNotFound) {
|
|
686
|
+
// Uncomment next two lines if we make this method public.
|
|
687
|
+
// assertParam(metadataStore, "metadataStore").isInstanceOf(MetadataStore).check();
|
|
688
|
+
// assertParam(throwErrorIfNotFound, "throwErrorIfNotFound").isBoolean().isOptional().check();
|
|
689
|
+
let entityType = this.fromEntityType;
|
|
690
|
+
if (entityType)
|
|
691
|
+
return entityType;
|
|
692
|
+
let resourceName = this.resourceName;
|
|
693
|
+
if (!resourceName) {
|
|
694
|
+
throw new Error("There is no resourceName for this query");
|
|
695
|
+
}
|
|
696
|
+
if (metadataStore.isEmpty()) {
|
|
697
|
+
if (throwErrorIfNotFound) {
|
|
698
|
+
throw new Error("There is no metadata available for this query. " +
|
|
699
|
+
"Are you querying the local cache before you've fetched metadata?");
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
return undefined;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
let entityTypeName = metadataStore.getEntityTypeNameForResourceName(resourceName);
|
|
706
|
+
if (entityTypeName) {
|
|
707
|
+
entityType = metadataStore._getStructuralType(entityTypeName);
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
entityType = this._getToEntityType(metadataStore, true);
|
|
711
|
+
}
|
|
712
|
+
if (!entityType) {
|
|
713
|
+
if (throwErrorIfNotFound) {
|
|
714
|
+
throw new Error(core.formatString("Cannot find an entityType for resourceName: '%1'. ", resourceName) + core.strings.TO_TYPE);
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
return undefined;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
this.fromEntityType = entityType;
|
|
721
|
+
return entityType;
|
|
722
|
+
}
|
|
723
|
+
/** @hidden @internal */
|
|
724
|
+
_getToEntityType(metadataStore, skipFromCheck) {
|
|
725
|
+
// skipFromCheck is to avoid recursion if called from _getFromEntityType;
|
|
726
|
+
if (this.resultEntityType instanceof EntityType) {
|
|
727
|
+
return this.resultEntityType;
|
|
728
|
+
}
|
|
729
|
+
else if (this.resultEntityType) {
|
|
730
|
+
// resultEntityType is a string
|
|
731
|
+
this.resultEntityType = metadataStore._getStructuralType(this.resultEntityType, false);
|
|
732
|
+
return this.resultEntityType;
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
// resolve it, if possible, via the resourceName
|
|
736
|
+
// do not cache this value in this case
|
|
737
|
+
// cannot determine the resultEntityType if a selectClause is present.
|
|
738
|
+
// return skipFromCheck ? null : (!this.selectClause) && this._getFromEntityType(metadataStore, false);
|
|
739
|
+
if (skipFromCheck || this.selectClause) {
|
|
740
|
+
return undefined;
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
return this._getFromEntityType(metadataStore, false);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
/** @hidden @internal */
|
|
748
|
+
// for testing
|
|
749
|
+
_toUri(em) {
|
|
750
|
+
let ds = DataService.resolve([em.dataService]);
|
|
751
|
+
return ds.uriBuilder.buildUri(this, em.metadataStore);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
EntityQuery.prototype._$typeName = "EntityQuery";
|
|
755
|
+
// private functions
|
|
756
|
+
function fromJSON(eq, json) {
|
|
757
|
+
core.toJson(json, {
|
|
758
|
+
"resourceName,from": null,
|
|
759
|
+
// just the name comes back and will be resolved later
|
|
760
|
+
"resultEntityType,toType": null,
|
|
761
|
+
"wherePredicate,where": function (v) {
|
|
762
|
+
return v ? new Predicate(v) : undefined;
|
|
763
|
+
},
|
|
764
|
+
"orderByClause,orderBy": function (v) {
|
|
765
|
+
return v ? new OrderByClause(v) : undefined;
|
|
766
|
+
},
|
|
767
|
+
"selectClause,select": function (v) {
|
|
768
|
+
return v ? new SelectClause(v) : undefined;
|
|
769
|
+
},
|
|
770
|
+
"expandClause,expand": function (v) {
|
|
771
|
+
return v ? new ExpandClause(v) : undefined;
|
|
772
|
+
},
|
|
773
|
+
"skipCount,skip": null,
|
|
774
|
+
"takeCount,take": null,
|
|
775
|
+
parameters: function (v) {
|
|
776
|
+
return core.isEmpty(v) ? undefined : v;
|
|
777
|
+
},
|
|
778
|
+
"inlineCountEnabled,inlineCount": false,
|
|
779
|
+
"noTrackingEnabled,noTracking": false,
|
|
780
|
+
"usePostEnabled,usePost": false,
|
|
781
|
+
queryOptions: function (v) {
|
|
782
|
+
return v ? QueryOptions.fromJSON(v) : undefined;
|
|
783
|
+
}
|
|
784
|
+
}, eq);
|
|
785
|
+
return eq;
|
|
786
|
+
}
|
|
787
|
+
function clone(eq, propName, value) {
|
|
788
|
+
// immutable queries mean that we don't need to clone if no change in value.
|
|
789
|
+
if (propName) {
|
|
790
|
+
if (eq[propName] === value)
|
|
791
|
+
return eq;
|
|
792
|
+
}
|
|
793
|
+
// copying QueryOptions is safe because they are are immutable;
|
|
794
|
+
let copy = core.extend(new EntityQuery(), eq, [
|
|
795
|
+
"resourceName",
|
|
796
|
+
"fromEntityType",
|
|
797
|
+
"wherePredicate",
|
|
798
|
+
"orderByClause",
|
|
799
|
+
"selectClause",
|
|
800
|
+
"skipCount",
|
|
801
|
+
"takeCount",
|
|
802
|
+
"expandClause",
|
|
803
|
+
"inlineCountEnabled",
|
|
804
|
+
"noTrackingEnabled",
|
|
805
|
+
"usePostEnabled",
|
|
806
|
+
"usesNameOnServer",
|
|
807
|
+
"queryOptions",
|
|
808
|
+
"entityManager",
|
|
809
|
+
"dataService",
|
|
810
|
+
"resultEntityType"
|
|
811
|
+
]);
|
|
812
|
+
copy.parameters = core.extend({}, eq.parameters);
|
|
813
|
+
if (propName) {
|
|
814
|
+
copy[propName] = value;
|
|
815
|
+
}
|
|
816
|
+
return copy;
|
|
817
|
+
}
|
|
818
|
+
function processUsing(eq, mapArg, value, propertyName) {
|
|
819
|
+
const map = mapArg;
|
|
820
|
+
const eqx = eq;
|
|
821
|
+
let typeName = value._$typeName || ((value instanceof BreezeEnum) && value.constructor.name);
|
|
822
|
+
let key = typeName && typeName.substr(0, 1).toLowerCase() + typeName.substr(1);
|
|
823
|
+
if (propertyName && key !== propertyName) {
|
|
824
|
+
throw new Error("Invalid value for property: " + propertyName);
|
|
825
|
+
}
|
|
826
|
+
if (key) {
|
|
827
|
+
let fn = map[key];
|
|
828
|
+
if (fn === undefined) {
|
|
829
|
+
throw new Error("Invalid config property: " + key);
|
|
830
|
+
}
|
|
831
|
+
else if (fn === null) {
|
|
832
|
+
eqx[key] = value;
|
|
833
|
+
}
|
|
834
|
+
else {
|
|
835
|
+
fn(eq, value);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
else {
|
|
839
|
+
core.objectForEach(value, (propName, val) => {
|
|
840
|
+
processUsing(eq, map, val, propName);
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
function normalizePropertyPaths(propertyPaths) {
|
|
845
|
+
assertParam(propertyPaths, "propertyPaths").isOptional().isString().or().isArray().isString().check();
|
|
846
|
+
if (typeof propertyPaths === 'string') {
|
|
847
|
+
propertyPaths = propertyPaths.split(",");
|
|
848
|
+
}
|
|
849
|
+
propertyPaths = propertyPaths.map(function (pp) {
|
|
850
|
+
return pp.trim();
|
|
851
|
+
});
|
|
852
|
+
return propertyPaths;
|
|
853
|
+
}
|
|
854
|
+
function buildPredicate(entity) {
|
|
855
|
+
let entityType = entity.entityType;
|
|
856
|
+
let predParts = entityType.keyProperties.map(function (kp) {
|
|
857
|
+
return Predicate.create(kp.name, FilterQueryOp.Equals, entity.getProperty(kp.name));
|
|
858
|
+
});
|
|
859
|
+
let pred = Predicate.and(predParts);
|
|
860
|
+
return pred;
|
|
861
|
+
}
|
|
862
|
+
function buildKeyPredicate(entityKey) {
|
|
863
|
+
let keyProps = entityKey.entityType.keyProperties;
|
|
864
|
+
let preds = core.arrayZip(keyProps, entityKey.values, function (kp, v) {
|
|
865
|
+
return Predicate.create(kp.name, FilterQueryOp.Equals, v);
|
|
866
|
+
});
|
|
867
|
+
let pred = Predicate.and(preds);
|
|
868
|
+
return pred;
|
|
869
|
+
}
|
|
870
|
+
function buildNavigationPredicate(entity, navigationProperty) {
|
|
871
|
+
if (navigationProperty.isScalar) {
|
|
872
|
+
if (navigationProperty.foreignKeyNames.length === 0)
|
|
873
|
+
return null;
|
|
874
|
+
let relatedKeyValues = navigationProperty.foreignKeyNames.map((fkName) => {
|
|
875
|
+
return entity.getProperty(fkName);
|
|
876
|
+
});
|
|
877
|
+
let entityKey = new EntityKey(navigationProperty.entityType, relatedKeyValues);
|
|
878
|
+
return buildKeyPredicate(entityKey);
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
let inverseNp = navigationProperty.inverse;
|
|
882
|
+
let foreignKeyNames = inverseNp ? inverseNp.foreignKeyNames : navigationProperty.invForeignKeyNames;
|
|
883
|
+
if (foreignKeyNames.length === 0)
|
|
884
|
+
return null;
|
|
885
|
+
let keyValues = entity.entityAspect.getKey().values;
|
|
886
|
+
let predParts = core.arrayZip(foreignKeyNames, keyValues, (fkName, kv) => {
|
|
887
|
+
return Predicate.create(fkName, FilterQueryOp.Equals, kv);
|
|
888
|
+
});
|
|
889
|
+
return Predicate.and(predParts);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
FilterQueryOp is an 'Enum' containing all of the valid {@link Predicate}
|
|
894
|
+
filter operators for an {@link EntityQuery}.
|
|
895
|
+
*/
|
|
896
|
+
export class FilterQueryOp extends BreezeEnum {
|
|
897
|
+
/** Aliases: "eq", "==" */
|
|
898
|
+
static Equals = new FilterQueryOp({ operator: "eq" });
|
|
899
|
+
/** Aliases: "ne", "!=" */
|
|
900
|
+
static NotEquals = new FilterQueryOp({ operator: "ne" });
|
|
901
|
+
/** Aliases: "gt", ">" */
|
|
902
|
+
static GreaterThan = new FilterQueryOp({ operator: "gt" });
|
|
903
|
+
/** Aliases: "lt", "<" */
|
|
904
|
+
static LessThan = new FilterQueryOp({ operator: "lt" });
|
|
905
|
+
/** Aliases: "ge", ">=" */
|
|
906
|
+
static GreaterThanOrEqual = new FilterQueryOp({ operator: "ge" });
|
|
907
|
+
/** Aliases: "le", "<=" */
|
|
908
|
+
static LessThanOrEqual = new FilterQueryOp({ operator: "le" });
|
|
909
|
+
/** String operation: Is a string a substring of another string. Aliases: "substringof" */
|
|
910
|
+
static Contains = new FilterQueryOp({ operator: "contains" });
|
|
911
|
+
/** No aliases */
|
|
912
|
+
static StartsWith = new FilterQueryOp({ operator: "startswith" });
|
|
913
|
+
/** No aliases */
|
|
914
|
+
static EndsWith = new FilterQueryOp({ operator: "endswith" });
|
|
915
|
+
/** Aliases: "some" */
|
|
916
|
+
static Any = new FilterQueryOp({ operator: "any" });
|
|
917
|
+
/** Aliases: "every" */
|
|
918
|
+
static All = new FilterQueryOp({ operator: "all" });
|
|
919
|
+
/** No aliases */
|
|
920
|
+
static In = new FilterQueryOp({ operator: "in" });
|
|
921
|
+
}
|
|
922
|
+
FilterQueryOp.prototype._$typeName = "FilterQueryOp";
|
|
923
|
+
FilterQueryOp.resolveSymbols();
|
|
924
|
+
/**
|
|
925
|
+
BooleanQueryOp is an 'Enum' containing all of the valid boolean
|
|
926
|
+
operators for an {@link EntityQuery}.
|
|
927
|
+
*/
|
|
928
|
+
export class BooleanQueryOp extends BreezeEnum {
|
|
929
|
+
static And = new BooleanQueryOp({ operator: "and" });
|
|
930
|
+
static Or = new BooleanQueryOp({ operator: "or" });
|
|
931
|
+
static Not = new BooleanQueryOp({ operator: "not" });
|
|
932
|
+
}
|
|
933
|
+
BooleanQueryOp.prototype._$typeName = "BooleanQueryOp";
|
|
934
|
+
BooleanQueryOp.resolveSymbols();
|
|
935
|
+
/** For use by breeze plugin authors only. The class is used in most {@link UriBuilderAdapter} implementations
|
|
936
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
937
|
+
@hidden
|
|
938
|
+
|
|
939
|
+
An OrderByClause is a description of the properties and direction that the result
|
|
940
|
+
of a query should be sorted in. OrderByClauses are immutable, which means that any
|
|
941
|
+
method that would modify an OrderByClause actually returns a new OrderByClause.
|
|
942
|
+
|
|
943
|
+
For example for an Employee object with properties of 'company' and 'lastName' the following would be valid expressions:
|
|
944
|
+
```ts
|
|
945
|
+
const obc = new OrderByClause(["company.companyName", "lastName"]);
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
or
|
|
949
|
+
```ts
|
|
950
|
+
const obc = new OrderByClause(["company.companyName desc", "lastName"]);
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
or
|
|
954
|
+
```ts
|
|
955
|
+
const obc = new OrderByClause(["company.companyName", "lastName"], true);
|
|
956
|
+
```
|
|
957
|
+
*/
|
|
958
|
+
export class OrderByClause {
|
|
959
|
+
/** @hidden @internal */
|
|
960
|
+
items;
|
|
961
|
+
constructor(propertyPaths, isDesc) {
|
|
962
|
+
if (propertyPaths.length === 0) {
|
|
963
|
+
throw new Error("OrderByClause cannot be empty");
|
|
964
|
+
}
|
|
965
|
+
// you can also pass in an array of orderByClauses
|
|
966
|
+
if (propertyPaths[0] instanceof OrderByClause) {
|
|
967
|
+
let clauses = propertyPaths;
|
|
968
|
+
this.items = arrayFlatMap(clauses, c => c.items);
|
|
969
|
+
// this.items = Array.prototype.concat.apply(clauses[0].items, clauses.slice(1).map(core.pluck("items")));
|
|
970
|
+
// this.items = Array.prototype.concat.apply([], clauses.map(core.pluck("items")));
|
|
971
|
+
}
|
|
972
|
+
else {
|
|
973
|
+
this.items = propertyPaths.map(function (pp) {
|
|
974
|
+
return new OrderByItem(pp, isDesc);
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
validate(entityType) {
|
|
979
|
+
if (entityType == null || entityType.isAnonymous)
|
|
980
|
+
return;
|
|
981
|
+
this.items.forEach((item) => {
|
|
982
|
+
item.validate(entityType);
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
getComparer(entityType) {
|
|
986
|
+
let orderByFuncs = this.items.map(function (obc) {
|
|
987
|
+
return obc.getComparer(entityType);
|
|
988
|
+
});
|
|
989
|
+
return function (entity1, entity2) {
|
|
990
|
+
for (let i = 0; i < orderByFuncs.length; i++) {
|
|
991
|
+
let result = orderByFuncs[i](entity1, entity2);
|
|
992
|
+
if (result !== 0) {
|
|
993
|
+
return result;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return 0;
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
toJSONExt(context) {
|
|
1000
|
+
return this.items.map(function (item) {
|
|
1001
|
+
return context.propertyPathFn(item.propertyPath) + (item.isDesc ? " desc" : "");
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
/** @hidden @internal */
|
|
1006
|
+
export class OrderByItem {
|
|
1007
|
+
propertyPath;
|
|
1008
|
+
isDesc;
|
|
1009
|
+
lastProperty;
|
|
1010
|
+
constructor(propertyPath, isDesc) {
|
|
1011
|
+
if (!(typeof propertyPath === 'string')) {
|
|
1012
|
+
throw new Error("propertyPath is not a string");
|
|
1013
|
+
}
|
|
1014
|
+
propertyPath = propertyPath.trim();
|
|
1015
|
+
let parts = propertyPath.split(' ');
|
|
1016
|
+
// parts[0] is the propertyPath; [1] would be whether descending or not.
|
|
1017
|
+
// if (parts.length > 1 && isDesc !== true && isDesc !== false) {
|
|
1018
|
+
if (parts.length > 1 && isDesc == null) {
|
|
1019
|
+
isDesc = stringStartsWith(parts[1].toLowerCase(), "desc");
|
|
1020
|
+
if (!isDesc) {
|
|
1021
|
+
// isDesc is false but check to make sure its intended.
|
|
1022
|
+
let isAsc = stringStartsWith(parts[1].toLowerCase(), "asc");
|
|
1023
|
+
if (!isAsc) {
|
|
1024
|
+
throw new Error("the second word in the propertyPath must begin with 'desc' or 'asc'");
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
this.propertyPath = parts[0];
|
|
1029
|
+
this.isDesc = isDesc || false;
|
|
1030
|
+
}
|
|
1031
|
+
validate(entityType) {
|
|
1032
|
+
if (entityType == null || entityType.isAnonymous)
|
|
1033
|
+
return;
|
|
1034
|
+
// will throw an exception on bad propertyPath
|
|
1035
|
+
this.lastProperty = entityType.getProperty(this.propertyPath, true);
|
|
1036
|
+
return this.lastProperty;
|
|
1037
|
+
}
|
|
1038
|
+
getComparer(entityType) {
|
|
1039
|
+
let propDataType;
|
|
1040
|
+
let isCaseSensitive;
|
|
1041
|
+
if (!this.lastProperty)
|
|
1042
|
+
this.validate(entityType);
|
|
1043
|
+
if (this.lastProperty) {
|
|
1044
|
+
propDataType = this.lastProperty.dataType;
|
|
1045
|
+
isCaseSensitive = this.lastProperty.parentType.metadataStore.localQueryComparisonOptions.isCaseSensitive;
|
|
1046
|
+
}
|
|
1047
|
+
let propertyPath = this.propertyPath;
|
|
1048
|
+
let isDesc = this.isDesc;
|
|
1049
|
+
return function (entity1, entity2) {
|
|
1050
|
+
let value1 = EntityAspect.getPropertyPathValue(entity1, propertyPath);
|
|
1051
|
+
let value2 = EntityAspect.getPropertyPathValue(entity2, propertyPath);
|
|
1052
|
+
let dataType = propDataType || (value1 && DataType.fromValue(value1)) || DataType.fromValue(value2);
|
|
1053
|
+
if (dataType === DataType.String) {
|
|
1054
|
+
if (isCaseSensitive) {
|
|
1055
|
+
value1 = value1 || "";
|
|
1056
|
+
value2 = value2 || "";
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
value1 = (value1 || "").toLowerCase();
|
|
1060
|
+
value2 = (value2 || "").toLowerCase();
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
else {
|
|
1064
|
+
let normalize = DataType.getComparableFn(dataType);
|
|
1065
|
+
value1 = normalize(value1);
|
|
1066
|
+
value2 = normalize(value2);
|
|
1067
|
+
}
|
|
1068
|
+
if (value1 === value2) {
|
|
1069
|
+
return 0;
|
|
1070
|
+
}
|
|
1071
|
+
else if (value1 > value2 || value2 === undefined) {
|
|
1072
|
+
return isDesc ? -1 : 1;
|
|
1073
|
+
}
|
|
1074
|
+
else {
|
|
1075
|
+
return isDesc ? 1 : -1;
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
/** For use by breeze plugin authors only. The class is used in most {@link UriBuilderAdapter} implementations
|
|
1081
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
1082
|
+
@hidden
|
|
1083
|
+
*/
|
|
1084
|
+
export class SelectClause {
|
|
1085
|
+
propertyPaths;
|
|
1086
|
+
/** @hidden @internal */
|
|
1087
|
+
_pathNames;
|
|
1088
|
+
constructor(propertyPaths) {
|
|
1089
|
+
this.propertyPaths = propertyPaths;
|
|
1090
|
+
this._pathNames = propertyPaths.map(function (pp) {
|
|
1091
|
+
// every dot, not just the first: order.customer.companyName -> order_customer_companyName
|
|
1092
|
+
return pp.split(".").join("_");
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
validate(entityType) {
|
|
1096
|
+
if (entityType == null || entityType.isAnonymous)
|
|
1097
|
+
return; // can't validate yet
|
|
1098
|
+
// will throw an exception on bad propertyPath
|
|
1099
|
+
this.propertyPaths.forEach(function (path) {
|
|
1100
|
+
entityType.getProperty(path, true);
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
/** @hidden @internal
|
|
1104
|
+
The property names of a projected result. Given the queried type, they are the names a
|
|
1105
|
+
remote query's results get: the server names each path by joining the server names of
|
|
1106
|
+
its properties (the ones sent in the select clause) with '_', and the client passes that
|
|
1107
|
+
name through the naming convention, as it does every key of an anonymous result. */
|
|
1108
|
+
_resultNames(entityType) {
|
|
1109
|
+
if (entityType == null || entityType.isAnonymous)
|
|
1110
|
+
return this._pathNames;
|
|
1111
|
+
const et = entityType;
|
|
1112
|
+
const toClient = et.metadataStore.namingConvention.serverPropertyNameToClient;
|
|
1113
|
+
return this.propertyPaths.map(function (pp) {
|
|
1114
|
+
return toClient(et.clientPropertyPathToServer(pp, "_"));
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
toFunction(config) {
|
|
1118
|
+
let that = this;
|
|
1119
|
+
let names = this._resultNames(config && config.entityType);
|
|
1120
|
+
return function (entity) {
|
|
1121
|
+
let result = {};
|
|
1122
|
+
that.propertyPaths.forEach(function (path, i) {
|
|
1123
|
+
result[names[i]] = EntityAspect.getPropertyPathValue(entity, path);
|
|
1124
|
+
});
|
|
1125
|
+
return result;
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
toJSONExt(context) {
|
|
1129
|
+
return this.propertyPaths.map(function (pp) {
|
|
1130
|
+
return context.propertyPathFn(pp);
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
/** For use by breeze plugin authors only. The class is used in most {@link UriBuilderAdapter} implementations
|
|
1135
|
+
@adapter (see {@link UriBuilderAdapter})
|
|
1136
|
+
@hidden
|
|
1137
|
+
*/
|
|
1138
|
+
export class ExpandClause {
|
|
1139
|
+
propertyPaths;
|
|
1140
|
+
constructor(propertyPaths) {
|
|
1141
|
+
this.propertyPaths = propertyPaths;
|
|
1142
|
+
}
|
|
1143
|
+
toJSONExt(context) {
|
|
1144
|
+
return this.propertyPaths.map(function (pp) {
|
|
1145
|
+
return context.propertyPathFn(pp);
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
//# sourceMappingURL=entity-query.js.map
|