electrodb 2.7.1 → 2.8.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/index.d.ts +4 -0
- package/package.json +10 -5
- package/src/entity.js +93 -24
- package/src/errors.js +1 -1
- package/src/schema.js +10 -4
- package/src/types.js +6 -0
- package/src/validations.js +10 -0
- package/docker/dynamodb/shared-local-instance.db +0 -0
- package/output +0 -168
package/index.d.ts
CHANGED
|
@@ -1780,6 +1780,8 @@ export interface IndexWithSortKey {
|
|
|
1780
1780
|
|
|
1781
1781
|
export type AccessPatternCollection<C extends string> = C | ReadonlyArray<C>;
|
|
1782
1782
|
|
|
1783
|
+
export type KeyCastOption = 'string' | 'number'
|
|
1784
|
+
|
|
1783
1785
|
export interface Schema<A extends string, F extends string, C extends string> {
|
|
1784
1786
|
readonly model: {
|
|
1785
1787
|
readonly entity: string;
|
|
@@ -1800,12 +1802,14 @@ export interface Schema<A extends string, F extends string, C extends string> {
|
|
|
1800
1802
|
readonly field: string;
|
|
1801
1803
|
readonly composite: ReadonlyArray<F>;
|
|
1802
1804
|
readonly template?: string;
|
|
1805
|
+
readonly cast?: KeyCastOption;
|
|
1803
1806
|
}
|
|
1804
1807
|
readonly sk?: {
|
|
1805
1808
|
readonly casing?: "upper" | "lower" | "none" | "default";
|
|
1806
1809
|
readonly field: string;
|
|
1807
1810
|
readonly composite: ReadonlyArray<F>;
|
|
1808
1811
|
readonly template?: string;
|
|
1812
|
+
readonly cast?: KeyCastOption;
|
|
1809
1813
|
}
|
|
1810
1814
|
}
|
|
1811
1815
|
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electrodb",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "A library to more easily create and interact with multiple entities and heretical relationships in dynamodb",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "npm run test:types && npm run test:init && npm run test:unit",
|
|
8
|
-
"test:init": "
|
|
9
|
-
"test:unit": "
|
|
7
|
+
"test:run": "npm run test:types && npm run test:init && npm run test:unit",
|
|
8
|
+
"test:init": "node ./test/init.js",
|
|
9
|
+
"test:unit": "mocha -r ts-node/register ./test/**.spec.*",
|
|
10
10
|
"test:types": "tsd",
|
|
11
11
|
"coverage": "npm run test:init && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
|
|
12
12
|
"coverage:local:coveralls": "npm run test:init && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
|
|
13
13
|
"coverage:local:html": "npm run test:init && nyc npm run test:unit && nyc report --reporter=html",
|
|
14
14
|
"build:browser": "browserify playground/browser.js -o playground/bundle.js",
|
|
15
15
|
"build": "sh buildbrowser.sh",
|
|
16
|
-
"
|
|
16
|
+
"test": "./test.sh",
|
|
17
|
+
"test:ci": "npm install && npm test",
|
|
18
|
+
"ddb:start": "docker compose up -d",
|
|
19
|
+
"ddb:load": "docker compose exec electro npm run test:init",
|
|
20
|
+
"ddb:stop": "docker compose stop",
|
|
21
|
+
"example:taskapp": "npm run ddb:start && npm run ddb:load && ts-node ./examples/taskmanager/src/index.ts; npm run ddb:stop"
|
|
17
22
|
},
|
|
18
23
|
"repository": {
|
|
19
24
|
"type": "git",
|
package/src/entity.js
CHANGED
|
@@ -26,6 +26,7 @@ const {
|
|
|
26
26
|
PartialComparisons,
|
|
27
27
|
MethodTypeTranslation,
|
|
28
28
|
TransactionCommitSymbol,
|
|
29
|
+
CastKeyOptions,
|
|
29
30
|
} = require("./types");
|
|
30
31
|
const { FilterFactory } = require("./filters");
|
|
31
32
|
const { FilterOperations } = require("./operations");
|
|
@@ -1279,7 +1280,7 @@ class Entity {
|
|
|
1279
1280
|
}
|
|
1280
1281
|
indexParts = { ...indexParts, ...tableIndexParts };
|
|
1281
1282
|
}
|
|
1282
|
-
let noPartsFound = Object.keys(indexParts).length === 0;
|
|
1283
|
+
let noPartsFound = Object.keys(indexParts).length === 0 && this.model.facets.byIndex[tableIndex].all.length > 0;
|
|
1283
1284
|
let partsAreIncomplete = this.model.facets.byIndex[tableIndex].all.find(facet => indexParts[facet.name] === undefined);
|
|
1284
1285
|
if (noPartsFound || partsAreIncomplete) {
|
|
1285
1286
|
// In this case no suitable record could be found be the deconstructed pager.
|
|
@@ -2592,7 +2593,8 @@ class Entity {
|
|
|
2592
2593
|
version = "1",
|
|
2593
2594
|
tableIndex,
|
|
2594
2595
|
modelVersion,
|
|
2595
|
-
isClustered
|
|
2596
|
+
isClustered,
|
|
2597
|
+
schema
|
|
2596
2598
|
}) {
|
|
2597
2599
|
/*
|
|
2598
2600
|
Collections will prefix the sort key so they can be queried with
|
|
@@ -2608,12 +2610,14 @@ class Entity {
|
|
|
2608
2610
|
field: tableIndex.pk.field,
|
|
2609
2611
|
casing: tableIndex.pk.casing,
|
|
2610
2612
|
isCustom: tableIndex.customFacets.pk,
|
|
2613
|
+
cast: tableIndex.pk.cast,
|
|
2611
2614
|
},
|
|
2612
2615
|
sk: {
|
|
2613
2616
|
prefix: "",
|
|
2614
2617
|
casing: tableIndex.sk.casing,
|
|
2615
2618
|
isCustom: tableIndex.customFacets.sk,
|
|
2616
2619
|
field: tableIndex.sk ? tableIndex.sk.field : undefined,
|
|
2620
|
+
cast: tableIndex.sk ? tableIndex.sk.cast : undefined,
|
|
2617
2621
|
}
|
|
2618
2622
|
};
|
|
2619
2623
|
|
|
@@ -2652,7 +2656,7 @@ class Entity {
|
|
|
2652
2656
|
}
|
|
2653
2657
|
}
|
|
2654
2658
|
|
|
2655
|
-
// If keys
|
|
2659
|
+
// If keys are not custom, set the prefixes
|
|
2656
2660
|
if (!keys.pk.isCustom) {
|
|
2657
2661
|
keys.pk.prefix = u.formatKeyCasing(pk, tableIndex.pk.casing);
|
|
2658
2662
|
}
|
|
@@ -2662,6 +2666,40 @@ class Entity {
|
|
|
2662
2666
|
keys.sk.postfix = u.formatKeyCasing(postfix, tableIndex.sk.casing);
|
|
2663
2667
|
}
|
|
2664
2668
|
|
|
2669
|
+
const castKeys = tableIndex.hasSk
|
|
2670
|
+
? [tableIndex.pk, tableIndex.sk]
|
|
2671
|
+
: [tableIndex.pk];
|
|
2672
|
+
|
|
2673
|
+
for (const castKey of castKeys) {
|
|
2674
|
+
if (castKey.cast === CastKeyOptions.string) {
|
|
2675
|
+
keys[castKey.type].cast = CastKeyOptions.string;
|
|
2676
|
+
} else if (
|
|
2677
|
+
// custom keys with only one facet and no labels are numeric by default
|
|
2678
|
+
castKey.cast === undefined &&
|
|
2679
|
+
castKey.isCustom &&
|
|
2680
|
+
castKey.facets.length === 1 &&
|
|
2681
|
+
castKey.facetLabels.every(({label}) => !label) &&
|
|
2682
|
+
schema.attributes[castKey.facets[0]] &&
|
|
2683
|
+
schema.attributes[castKey.facets[0]].type === 'number'
|
|
2684
|
+
) {
|
|
2685
|
+
keys[castKey.type].cast = CastKeyOptions.number;
|
|
2686
|
+
} else if (
|
|
2687
|
+
castKey.cast === CastKeyOptions.number &&
|
|
2688
|
+
castKey.facets.length === 1 &&
|
|
2689
|
+
schema.attributes[castKey.facets[0]] &&
|
|
2690
|
+
['number', 'string', 'boolean'].includes(schema.attributes[castKey.facets[0]].type)
|
|
2691
|
+
) {
|
|
2692
|
+
keys[castKey.type].cast = CastKeyOptions.number;
|
|
2693
|
+
} else if (
|
|
2694
|
+
castKey.cast === CastKeyOptions.number &&
|
|
2695
|
+
castKey.facets.length > 1
|
|
2696
|
+
) {
|
|
2697
|
+
throw new e.ElectroError(e.ErrorCodes.InvalidModel, `Invalid "cast" option provided for ${castKey.type} definition on index "${u.formatIndexNameForDisplay(tableIndex.index)}". Keys can only be cast to 'number' if they are a composite of one numeric attribute.`);
|
|
2698
|
+
} else {
|
|
2699
|
+
keys[castKey.type].cast = CastKeyOptions.string;
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2665
2703
|
return keys;
|
|
2666
2704
|
}
|
|
2667
2705
|
|
|
@@ -2743,7 +2781,12 @@ class Entity {
|
|
|
2743
2781
|
throw new Error(`Invalid index: ${index}`);
|
|
2744
2782
|
}
|
|
2745
2783
|
// let partitionKey = this._makeKey(prefixes.pk, facets.pk, pkFacets, this.model.facets.labels[index].pk, { excludeLabelTail: true });
|
|
2746
|
-
let partitionKey = this._makeKey(
|
|
2784
|
+
let partitionKey = this._makeKey(
|
|
2785
|
+
prefixes.pk,
|
|
2786
|
+
facets.pk,
|
|
2787
|
+
pkFacets,
|
|
2788
|
+
this.model.facets.labels[index].pk,
|
|
2789
|
+
);
|
|
2747
2790
|
let pk = partitionKey.key;
|
|
2748
2791
|
let sk = [];
|
|
2749
2792
|
let fulfilled = false;
|
|
@@ -2822,21 +2865,45 @@ class Entity {
|
|
|
2822
2865
|
};
|
|
2823
2866
|
}
|
|
2824
2867
|
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2868
|
+
_formatNumericCastKey(attributeName, key) {
|
|
2869
|
+
const fulfilled = key !== undefined;
|
|
2870
|
+
if (!fulfilled) {
|
|
2871
|
+
return {
|
|
2872
|
+
fulfilled,
|
|
2873
|
+
key,
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
if (typeof key === 'number') {
|
|
2877
|
+
return {
|
|
2878
|
+
fulfilled,
|
|
2879
|
+
key,
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
if (typeof key === 'string') {
|
|
2883
|
+
const parsed = parseInt(key);
|
|
2884
|
+
if (!isNaN(parsed)) {
|
|
2885
|
+
return {
|
|
2886
|
+
fulfilled,
|
|
2887
|
+
key: parsed,
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
if (typeof key === 'boolean') {
|
|
2893
|
+
return {
|
|
2894
|
+
fulfilled,
|
|
2895
|
+
key: key === true ? 1 : 0,
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
throw new e.ElectroAttributeValidationError(attributeName, `Invalid key value provided, could not cast composite attribute ${attributeName} to number for index`);
|
|
2831
2900
|
}
|
|
2832
2901
|
|
|
2902
|
+
|
|
2833
2903
|
/* istanbul ignore next */
|
|
2834
|
-
_makeKey({prefix, isCustom, casing, postfix} = {}, facets = [], supplied = {}, labels = [], {excludeLabelTail, excludePostfix, transform = (val) => val} = {}) {
|
|
2835
|
-
if (
|
|
2836
|
-
return
|
|
2837
|
-
fulfilled: supplied[facets[0]] !== undefined,
|
|
2838
|
-
key: supplied[facets[0]],
|
|
2839
|
-
};
|
|
2904
|
+
_makeKey({prefix, isCustom, casing, postfix, cast} = {}, facets = [], supplied = {}, labels = [], {excludeLabelTail, excludePostfix, transform = (val) => val} = {}) {
|
|
2905
|
+
if (cast === CastKeyOptions.number) {
|
|
2906
|
+
return this._formatNumericCastKey(facets[0], supplied[facets[0]]);
|
|
2840
2907
|
}
|
|
2841
2908
|
|
|
2842
2909
|
let key = prefix;
|
|
@@ -3153,7 +3220,6 @@ class Entity {
|
|
|
3153
3220
|
};
|
|
3154
3221
|
let seenIndexes = {};
|
|
3155
3222
|
let seenIndexFields = {};
|
|
3156
|
-
|
|
3157
3223
|
let accessPatterns = Object.keys(indexes);
|
|
3158
3224
|
|
|
3159
3225
|
for (let i in accessPatterns) {
|
|
@@ -3202,6 +3268,7 @@ class Entity {
|
|
|
3202
3268
|
index: indexName,
|
|
3203
3269
|
casing: pkCasing,
|
|
3204
3270
|
type: KeyTypes.pk,
|
|
3271
|
+
cast: index.pk.cast,
|
|
3205
3272
|
field: index.pk.field || "",
|
|
3206
3273
|
facets: parsedPKAttributes.attributes,
|
|
3207
3274
|
isCustom: parsedPKAttributes.isCustom,
|
|
@@ -3219,6 +3286,7 @@ class Entity {
|
|
|
3219
3286
|
index: indexName,
|
|
3220
3287
|
casing: skCasing,
|
|
3221
3288
|
type: KeyTypes.sk,
|
|
3289
|
+
cast: index.sk.cast,
|
|
3222
3290
|
field: index.sk.field || "",
|
|
3223
3291
|
facets: parsedSKAttributes.attributes,
|
|
3224
3292
|
isCustom: parsedSKAttributes.isCustom,
|
|
@@ -3410,7 +3478,7 @@ class Entity {
|
|
|
3410
3478
|
return normalized;
|
|
3411
3479
|
}
|
|
3412
3480
|
|
|
3413
|
-
_normalizeKeyFixings({service, entity, version, indexes, modelVersion, clusteredIndexes}) {
|
|
3481
|
+
_normalizeKeyFixings({service, entity, version, indexes, modelVersion, clusteredIndexes, schema}) {
|
|
3414
3482
|
let prefixes = {};
|
|
3415
3483
|
for (let accessPattern of Object.keys(indexes)) {
|
|
3416
3484
|
let tableIndex = indexes[accessPattern];
|
|
@@ -3421,6 +3489,7 @@ class Entity {
|
|
|
3421
3489
|
tableIndex,
|
|
3422
3490
|
modelVersion,
|
|
3423
3491
|
isClustered: clusteredIndexes.has(accessPattern),
|
|
3492
|
+
schema,
|
|
3424
3493
|
});
|
|
3425
3494
|
}
|
|
3426
3495
|
return prefixes;
|
|
@@ -3569,17 +3638,17 @@ class Entity {
|
|
|
3569
3638
|
let schema = new Schema(model.attributes, facets, {client, isRoot: true});
|
|
3570
3639
|
let filters = this._normalizeFilters(model.filters);
|
|
3571
3640
|
// todo: consider a rename
|
|
3572
|
-
let prefixes = this._normalizeKeyFixings({service, entity, version, indexes, modelVersion, clusteredIndexes});
|
|
3641
|
+
let prefixes = this._normalizeKeyFixings({service, entity, version, indexes, modelVersion, clusteredIndexes, schema});
|
|
3573
3642
|
|
|
3574
3643
|
// apply model defined labels
|
|
3575
3644
|
let schemaDefinedLabels = schema.getLabels();
|
|
3645
|
+
const deconstructors = {};
|
|
3576
3646
|
facets.labels = this._mergeKeyDefinitions(facets.labels, schemaDefinedLabels);
|
|
3577
3647
|
for (let indexName of Object.keys(facets.labels)) {
|
|
3578
|
-
|
|
3579
|
-
indexes[
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
for (const indexName of Object.keys(facets.labels)) {
|
|
3648
|
+
const accessPattern = indexAccessPattern.fromIndexToAccessPattern[indexName];
|
|
3649
|
+
indexes[accessPattern].pk.labels = facets.labels[indexName].pk;
|
|
3650
|
+
indexes[accessPattern].sk.labels = facets.labels[indexName].sk;
|
|
3651
|
+
|
|
3583
3652
|
const keyTypes = prefixes[indexName] || {};
|
|
3584
3653
|
deconstructors[indexName] = {};
|
|
3585
3654
|
for (const keyType in keyTypes) {
|
package/src/errors.js
CHANGED
package/src/schema.js
CHANGED
|
@@ -542,8 +542,9 @@ class MapAttribute extends Attribute {
|
|
|
542
542
|
const getter = get || ((val) => {
|
|
543
543
|
const isEmpty = !val || Object.keys(val).length === 0;
|
|
544
544
|
const isNotRequired = !this.required;
|
|
545
|
+
const doesNotHaveDefault = this.default === undefined;
|
|
545
546
|
const isRoot = this.isRoot;
|
|
546
|
-
if (isEmpty && isRoot &&
|
|
547
|
+
if (isEmpty && isRoot && isNotRequired && doesNotHaveDefault) {
|
|
547
548
|
return undefined;
|
|
548
549
|
}
|
|
549
550
|
return val;
|
|
@@ -582,11 +583,16 @@ class MapAttribute extends Attribute {
|
|
|
582
583
|
const setter = set || ((val) => {
|
|
583
584
|
const isEmpty = !val || Object.keys(val).length === 0;
|
|
584
585
|
const isNotRequired = !this.required;
|
|
586
|
+
const doesNotHaveDefault = this.default === undefined;
|
|
587
|
+
const defaultIsValue = this.default === val;
|
|
585
588
|
const isRoot = this.isRoot;
|
|
586
|
-
if (
|
|
589
|
+
if (defaultIsValue) {
|
|
590
|
+
return val;
|
|
591
|
+
} else if (isEmpty && isRoot && isNotRequired && doesNotHaveDefault) {
|
|
587
592
|
return undefined;
|
|
593
|
+
} else {
|
|
594
|
+
return val;
|
|
588
595
|
}
|
|
589
|
-
return val;
|
|
590
596
|
});
|
|
591
597
|
|
|
592
598
|
return (values, siblings) => {
|
|
@@ -1113,7 +1119,7 @@ class Schema {
|
|
|
1113
1119
|
prefix,
|
|
1114
1120
|
postfix,
|
|
1115
1121
|
traverser,
|
|
1116
|
-
isKeyField,
|
|
1122
|
+
isKeyField: isKeyField || isKey,
|
|
1117
1123
|
isRoot: !!isRoot,
|
|
1118
1124
|
label: attribute.label,
|
|
1119
1125
|
required: !!attribute.required,
|
package/src/types.js
CHANGED
|
@@ -312,6 +312,11 @@ const DynamoDBAttributeTypes = Object.entries({
|
|
|
312
312
|
return obj;
|
|
313
313
|
}, {});
|
|
314
314
|
|
|
315
|
+
const CastKeyOptions = {
|
|
316
|
+
string: 'string',
|
|
317
|
+
number: 'number',
|
|
318
|
+
}
|
|
319
|
+
|
|
315
320
|
module.exports = {
|
|
316
321
|
Pager,
|
|
317
322
|
KeyTypes,
|
|
@@ -331,6 +336,7 @@ module.exports = {
|
|
|
331
336
|
ItemOperations,
|
|
332
337
|
AttributeTypes,
|
|
333
338
|
EntityVersions,
|
|
339
|
+
CastKeyOptions,
|
|
334
340
|
ServiceVersions,
|
|
335
341
|
ExpressionTypes,
|
|
336
342
|
ElectroInstance,
|
package/src/validations.js
CHANGED
|
@@ -113,6 +113,11 @@ const Index = {
|
|
|
113
113
|
type: "string",
|
|
114
114
|
enum: ["upper", "lower", "none", "default"],
|
|
115
115
|
required: false,
|
|
116
|
+
},
|
|
117
|
+
cast: {
|
|
118
|
+
type: "string",
|
|
119
|
+
enum: ["string", "number"],
|
|
120
|
+
required: false,
|
|
116
121
|
}
|
|
117
122
|
},
|
|
118
123
|
},
|
|
@@ -146,6 +151,11 @@ const Index = {
|
|
|
146
151
|
type: "string",
|
|
147
152
|
enum: ["upper", "lower", "none", "default"],
|
|
148
153
|
required: false,
|
|
154
|
+
},
|
|
155
|
+
cast: {
|
|
156
|
+
type: "string",
|
|
157
|
+
enum: ["string", "number"],
|
|
158
|
+
required: false,
|
|
149
159
|
}
|
|
150
160
|
},
|
|
151
161
|
},
|
|
Binary file
|
package/output
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
"target": "top",
|
|
2
|
-
"target": "byOrganization",
|
|
3
|
-
"target": "byCategory",
|
|
4
|
-
"target": "top",
|
|
5
|
-
"target": "byOrganization",
|
|
6
|
-
"target": "byCategory",
|
|
7
|
-
"target": "top",
|
|
8
|
-
"target": "byOrganization",
|
|
9
|
-
"target": "byCategory",
|
|
10
|
-
"target": "top",
|
|
11
|
-
"target": "byOrganization",
|
|
12
|
-
"target": "byCategory",
|
|
13
|
-
"target": "top",
|
|
14
|
-
"target": "byOrganization",
|
|
15
|
-
"target": "byCategory",
|
|
16
|
-
"target": "top",
|
|
17
|
-
"target": "byOrganization",
|
|
18
|
-
"target": "byCategory",
|
|
19
|
-
"target": "top",
|
|
20
|
-
"target": "byOrganization",
|
|
21
|
-
"target": "byCategory",
|
|
22
|
-
"target": "top",
|
|
23
|
-
"target": "byOrganization",
|
|
24
|
-
"target": "byCategory",
|
|
25
|
-
"target": "top",
|
|
26
|
-
"target": "byOrganization",
|
|
27
|
-
"target": "byCategory",
|
|
28
|
-
"target": "top",
|
|
29
|
-
"target": "byOrganization",
|
|
30
|
-
"target": "byCategory",
|
|
31
|
-
"target": "top",
|
|
32
|
-
"target": "byOrganization",
|
|
33
|
-
"target": "byCategory",
|
|
34
|
-
"target": "top",
|
|
35
|
-
"target": "byOrganization",
|
|
36
|
-
"target": "byCategory",
|
|
37
|
-
"target": "top",
|
|
38
|
-
"target": "byOrganization",
|
|
39
|
-
"target": "byCategory",
|
|
40
|
-
"target": "top",
|
|
41
|
-
"target": "byOrganization",
|
|
42
|
-
"target": "byCategory",
|
|
43
|
-
"target": "top",
|
|
44
|
-
"target": "byOrganization",
|
|
45
|
-
"target": "byCategory",
|
|
46
|
-
"target": "top",
|
|
47
|
-
"target": "byOrganization",
|
|
48
|
-
"target": "byCategory",
|
|
49
|
-
"target": "top",
|
|
50
|
-
"target": "byOrganization",
|
|
51
|
-
"target": "byCategory",
|
|
52
|
-
"target": "top",
|
|
53
|
-
"target": "byOrganization",
|
|
54
|
-
"target": "byCategory",
|
|
55
|
-
"target": "top",
|
|
56
|
-
"target": "byOrganization",
|
|
57
|
-
"target": "byCategory",
|
|
58
|
-
"target": "top",
|
|
59
|
-
"target": "byOrganization",
|
|
60
|
-
"target": "byCategory",
|
|
61
|
-
"target": "top",
|
|
62
|
-
"target": "byOrganization",
|
|
63
|
-
"target": "byCategory",
|
|
64
|
-
"target": "top",
|
|
65
|
-
"target": "byOrganization",
|
|
66
|
-
"target": "byCategory",
|
|
67
|
-
"target": "top",
|
|
68
|
-
"target": "byOrganization",
|
|
69
|
-
"target": "byCategory",
|
|
70
|
-
"target": "top",
|
|
71
|
-
"target": "byOrganization",
|
|
72
|
-
"target": "byCategory",
|
|
73
|
-
"target": "top",
|
|
74
|
-
"target": "byOrganization",
|
|
75
|
-
"target": "byCategory",
|
|
76
|
-
"target": "top",
|
|
77
|
-
"target": "byOrganization",
|
|
78
|
-
"target": "byCategory",
|
|
79
|
-
"target": "top",
|
|
80
|
-
"target": "byOrganization",
|
|
81
|
-
"target": "byCategory",
|
|
82
|
-
"target": "top",
|
|
83
|
-
"target": "byOrganization",
|
|
84
|
-
"target": "byCategory",
|
|
85
|
-
"target": "top",
|
|
86
|
-
"target": "byOrganization",
|
|
87
|
-
"target": "byCategory",
|
|
88
|
-
"target": "top",
|
|
89
|
-
"target": "byOrganization",
|
|
90
|
-
"target": "byCategory",
|
|
91
|
-
"target": "top",
|
|
92
|
-
"target": "byOrganization",
|
|
93
|
-
"target": "byCategory",
|
|
94
|
-
"target": "top",
|
|
95
|
-
"target": "byOrganization",
|
|
96
|
-
"target": "byCategory",
|
|
97
|
-
"target": "top",
|
|
98
|
-
"target": "byOrganization",
|
|
99
|
-
"target": "byCategory",
|
|
100
|
-
"target": "top",
|
|
101
|
-
"target": "byOrganization",
|
|
102
|
-
"target": "byCategory",
|
|
103
|
-
"target": "top",
|
|
104
|
-
"target": "byOrganization",
|
|
105
|
-
"target": "byCategory",
|
|
106
|
-
"target": "top",
|
|
107
|
-
"target": "byOrganization",
|
|
108
|
-
"target": "byCategory",
|
|
109
|
-
"target": "top",
|
|
110
|
-
"target": "byOrganization",
|
|
111
|
-
"target": "byCategory",
|
|
112
|
-
"target": "top",
|
|
113
|
-
"target": "byOrganization",
|
|
114
|
-
"target": "byCategory",
|
|
115
|
-
"target": "top",
|
|
116
|
-
"target": "byOrganization",
|
|
117
|
-
"target": "byCategory",
|
|
118
|
-
"target": "top",
|
|
119
|
-
"target": "byOrganization",
|
|
120
|
-
"target": "byCategory",
|
|
121
|
-
"target": "top",
|
|
122
|
-
"target": "byOrganization",
|
|
123
|
-
"target": "byCategory",
|
|
124
|
-
"target": "top",
|
|
125
|
-
"target": "byOrganization",
|
|
126
|
-
"target": "byCategory",
|
|
127
|
-
"target": "top",
|
|
128
|
-
"target": "byOrganization",
|
|
129
|
-
"target": "byCategory",
|
|
130
|
-
"target": "top",
|
|
131
|
-
"target": "byOrganization",
|
|
132
|
-
"target": "byCategory",
|
|
133
|
-
"target": "top",
|
|
134
|
-
"target": "byOrganization",
|
|
135
|
-
"target": "byCategory",
|
|
136
|
-
"target": "top",
|
|
137
|
-
"target": "byOrganization",
|
|
138
|
-
"target": "byCategory",
|
|
139
|
-
"target": "top",
|
|
140
|
-
"target": "byOrganization",
|
|
141
|
-
"target": "byCategory",
|
|
142
|
-
"target": "top",
|
|
143
|
-
"target": "byOrganization",
|
|
144
|
-
"target": "byCategory",
|
|
145
|
-
"target": "top",
|
|
146
|
-
"target": "byOrganization",
|
|
147
|
-
"target": "byCategory",
|
|
148
|
-
"target": "top",
|
|
149
|
-
"target": "byOrganization",
|
|
150
|
-
"target": "byCategory",
|
|
151
|
-
"target": "top",
|
|
152
|
-
"target": "byOrganization",
|
|
153
|
-
"target": "byCategory",
|
|
154
|
-
"target": "top",
|
|
155
|
-
"target": "byOrganization",
|
|
156
|
-
"target": "byCategory",
|
|
157
|
-
"target": "top",
|
|
158
|
-
"target": "byOrganization",
|
|
159
|
-
"target": "byCategory",
|
|
160
|
-
"target": "top",
|
|
161
|
-
"target": "byOrganization",
|
|
162
|
-
"target": "byCategory",
|
|
163
|
-
"target": "top",
|
|
164
|
-
"target": "byOrganization",
|
|
165
|
-
"target": "byCategory",
|
|
166
|
-
"target": "top",
|
|
167
|
-
"target": "byOrganization",
|
|
168
|
-
"target": "byCategory",
|