electrodb 2.15.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/index.d.ts +69 -163
- package/index.js +9 -0
- package/package.json +1 -1
- package/src/clauses.js +163 -56
- package/src/client.js +5 -5
- package/src/conversions.js +68 -0
- package/src/entity.js +331 -212
- package/src/errors.js +2 -2
- package/src/operations.js +7 -4
- package/src/schema.js +14 -22
- package/src/service.js +8 -8
- package/src/transaction.js +4 -4
- package/src/types.js +17 -2
- package/src/validations.js +2 -2
- package/src/where.js +14 -7
- package/isolated-error +0 -488
package/src/entity.js
CHANGED
|
@@ -23,10 +23,12 @@ const {
|
|
|
23
23
|
ResultOrderOption,
|
|
24
24
|
ResultOrderParam,
|
|
25
25
|
IndexTypes,
|
|
26
|
-
|
|
26
|
+
KeyAttributesComparisons,
|
|
27
27
|
MethodTypeTranslation,
|
|
28
28
|
TransactionCommitSymbol,
|
|
29
29
|
CastKeyOptions,
|
|
30
|
+
ComparisonTypes,
|
|
31
|
+
DataOptions,
|
|
30
32
|
} = require("./types");
|
|
31
33
|
const { FilterFactory } = require("./filters");
|
|
32
34
|
const { FilterOperations } = require("./operations");
|
|
@@ -40,9 +42,9 @@ const e = require("./errors");
|
|
|
40
42
|
const v = require("./validations");
|
|
41
43
|
|
|
42
44
|
const ImpactedIndexTypeSource = {
|
|
43
|
-
composite:
|
|
44
|
-
provided:
|
|
45
|
-
}
|
|
45
|
+
composite: "composite",
|
|
46
|
+
provided: "provided",
|
|
47
|
+
};
|
|
46
48
|
|
|
47
49
|
class Entity {
|
|
48
50
|
constructor(model, config = {}) {
|
|
@@ -76,27 +78,6 @@ class Entity {
|
|
|
76
78
|
);
|
|
77
79
|
|
|
78
80
|
this.query = {};
|
|
79
|
-
this.conversions = {
|
|
80
|
-
fromComposite: {
|
|
81
|
-
toKeys: (composite, options = {}) =>
|
|
82
|
-
this._fromCompositeToKeys({ provided: composite }, options),
|
|
83
|
-
toCursor: (composite) =>
|
|
84
|
-
this._fromCompositeToCursor(
|
|
85
|
-
{ provided: composite },
|
|
86
|
-
{ strict: "all" },
|
|
87
|
-
),
|
|
88
|
-
},
|
|
89
|
-
fromKeys: {
|
|
90
|
-
toCursor: (keys) => this._fromKeysToCursor({ provided: keys }, {}),
|
|
91
|
-
toComposite: (keys) => this._fromKeysToComposite({ provided: keys }),
|
|
92
|
-
},
|
|
93
|
-
fromCursor: {
|
|
94
|
-
toKeys: (cursor) => this._fromCursorToKeys({ provided: cursor }),
|
|
95
|
-
toComposite: (cursor) =>
|
|
96
|
-
this._fromCursorToComposite({ provided: cursor }),
|
|
97
|
-
},
|
|
98
|
-
byAccessPattern: {},
|
|
99
|
-
};
|
|
100
81
|
for (let accessPattern in this.model.indexes) {
|
|
101
82
|
let index = this.model.indexes[accessPattern].index;
|
|
102
83
|
this.query[accessPattern] = (...values) => {
|
|
@@ -111,42 +92,6 @@ class Entity {
|
|
|
111
92
|
options,
|
|
112
93
|
).query(...values);
|
|
113
94
|
};
|
|
114
|
-
|
|
115
|
-
this.conversions.byAccessPattern[accessPattern] = {
|
|
116
|
-
fromKeys: {
|
|
117
|
-
toCursor: (keys) =>
|
|
118
|
-
this._fromKeysToCursorByIndex({ indexName: index, provided: keys }),
|
|
119
|
-
toComposite: (keys) =>
|
|
120
|
-
this._fromKeysToCompositeByIndex({
|
|
121
|
-
indexName: index,
|
|
122
|
-
provided: keys,
|
|
123
|
-
}),
|
|
124
|
-
},
|
|
125
|
-
fromCursor: {
|
|
126
|
-
toKeys: (cursor) =>
|
|
127
|
-
this._fromCursorToKeysByIndex({
|
|
128
|
-
indexName: index,
|
|
129
|
-
provided: cursor,
|
|
130
|
-
}),
|
|
131
|
-
toComposite: (cursor) =>
|
|
132
|
-
this._fromCursorToCompositeByIndex({
|
|
133
|
-
indexName: index,
|
|
134
|
-
provided: cursor,
|
|
135
|
-
}),
|
|
136
|
-
},
|
|
137
|
-
fromComposite: {
|
|
138
|
-
toCursor: (composite) =>
|
|
139
|
-
this._fromCompositeToCursorByIndex(
|
|
140
|
-
{ indexName: index, provided: composite },
|
|
141
|
-
{ strict: "all" },
|
|
142
|
-
),
|
|
143
|
-
toKeys: (composite, options = {}) =>
|
|
144
|
-
this._fromCompositeToKeysByIndex(
|
|
145
|
-
{ indexName: index, provided: composite },
|
|
146
|
-
options,
|
|
147
|
-
),
|
|
148
|
-
},
|
|
149
|
-
};
|
|
150
95
|
}
|
|
151
96
|
|
|
152
97
|
this.config.identifiers = config.identifiers || {};
|
|
@@ -633,7 +578,7 @@ class Entity {
|
|
|
633
578
|
_safeMinimum(...values) {
|
|
634
579
|
let eligibleNumbers = [];
|
|
635
580
|
for (let value of values) {
|
|
636
|
-
if (typeof value ===
|
|
581
|
+
if (typeof value === "number") {
|
|
637
582
|
eligibleNumbers.push(value);
|
|
638
583
|
}
|
|
639
584
|
}
|
|
@@ -733,16 +678,14 @@ class Entity {
|
|
|
733
678
|
ExclusiveStartKey = undefined;
|
|
734
679
|
}
|
|
735
680
|
let pages = this._normalizePagesValue(config.pages);
|
|
736
|
-
let max = this._normalizeLimitValue(config.limit);
|
|
737
681
|
let iterations = 0;
|
|
738
682
|
let count = 0;
|
|
739
683
|
let hydratedUnprocessed = [];
|
|
740
684
|
const shouldHydrate = config.hydrate && method === MethodTypes.query;
|
|
741
685
|
do {
|
|
742
|
-
let limit = max === undefined ? parameters.Limit : max - count;
|
|
743
686
|
let response = await this._exec(
|
|
744
687
|
method,
|
|
745
|
-
{ ExclusiveStartKey, ...parameters
|
|
688
|
+
{ ExclusiveStartKey, ...parameters },
|
|
746
689
|
config,
|
|
747
690
|
);
|
|
748
691
|
|
|
@@ -750,17 +693,17 @@ class Entity {
|
|
|
750
693
|
|
|
751
694
|
response = this.formatResponse(response, parameters.IndexName, {
|
|
752
695
|
...config,
|
|
753
|
-
|
|
696
|
+
data:
|
|
697
|
+
shouldHydrate &&
|
|
698
|
+
(!config.data || config.data === DataOptions.attributes)
|
|
699
|
+
? "includeKeys"
|
|
700
|
+
: config.data,
|
|
754
701
|
ignoreOwnership: shouldHydrate || config.ignoreOwnership,
|
|
755
702
|
});
|
|
756
|
-
|
|
757
|
-
if (config.raw) {
|
|
703
|
+
if (config.data === DataOptions.raw) {
|
|
758
704
|
return response;
|
|
759
705
|
} else if (config._isCollectionQuery) {
|
|
760
706
|
for (const entity in response.data) {
|
|
761
|
-
if (max) {
|
|
762
|
-
count += response.data[entity].length;
|
|
763
|
-
}
|
|
764
707
|
let items = response.data[entity];
|
|
765
708
|
if (shouldHydrate && items.length) {
|
|
766
709
|
const hydrated = await config.hydrator(
|
|
@@ -778,8 +721,8 @@ class Entity {
|
|
|
778
721
|
results[entity] = [...results[entity], ...items];
|
|
779
722
|
}
|
|
780
723
|
} else if (Array.isArray(response.data)) {
|
|
781
|
-
let prevCount = count
|
|
782
|
-
if (
|
|
724
|
+
let prevCount = count;
|
|
725
|
+
if (config.count) {
|
|
783
726
|
count += response.data.length;
|
|
784
727
|
}
|
|
785
728
|
let items = response.data;
|
|
@@ -801,7 +744,10 @@ class Entity {
|
|
|
801
744
|
results = [...results, ...items];
|
|
802
745
|
if (moreItemsThanRequired || count === config.count) {
|
|
803
746
|
const lastItem = results[results.length - 1];
|
|
804
|
-
ExclusiveStartKey = this._fromCompositeToKeysByIndex({
|
|
747
|
+
ExclusiveStartKey = this._fromCompositeToKeysByIndex({
|
|
748
|
+
indexName,
|
|
749
|
+
provided: lastItem,
|
|
750
|
+
});
|
|
805
751
|
break;
|
|
806
752
|
}
|
|
807
753
|
} else {
|
|
@@ -810,8 +756,9 @@ class Entity {
|
|
|
810
756
|
iterations++;
|
|
811
757
|
} while (
|
|
812
758
|
ExclusiveStartKey &&
|
|
813
|
-
(pages === AllPages ||
|
|
814
|
-
|
|
759
|
+
(pages === AllPages ||
|
|
760
|
+
config.count !== undefined ||
|
|
761
|
+
iterations < pages) &&
|
|
815
762
|
(config.count === undefined || count < config.count)
|
|
816
763
|
);
|
|
817
764
|
|
|
@@ -869,14 +816,13 @@ class Entity {
|
|
|
869
816
|
}
|
|
870
817
|
|
|
871
818
|
cleanseRetrievedData(item = {}, options = {}) {
|
|
872
|
-
let { includeKeys } = options;
|
|
873
819
|
let data = {};
|
|
874
820
|
let names = this.model.schema.translationForRetrieval;
|
|
875
821
|
for (let [attr, value] of Object.entries(item)) {
|
|
876
822
|
let name = names[attr];
|
|
877
823
|
if (name) {
|
|
878
824
|
data[name] = value;
|
|
879
|
-
} else if (includeKeys) {
|
|
825
|
+
} else if (options.data === DataOptions.includeKeys) {
|
|
880
826
|
data[attr] = value;
|
|
881
827
|
}
|
|
882
828
|
}
|
|
@@ -963,14 +909,14 @@ class Entity {
|
|
|
963
909
|
let results = {};
|
|
964
910
|
if (validations.isFunction(config.parse)) {
|
|
965
911
|
results = config.parse(config, response);
|
|
966
|
-
} else if (config.raw && !config._isPagination) {
|
|
912
|
+
} else if (config.data === DataOptions.raw && !config._isPagination) {
|
|
967
913
|
if (response.TableName) {
|
|
968
914
|
results = {};
|
|
969
915
|
} else {
|
|
970
916
|
results = response;
|
|
971
917
|
}
|
|
972
918
|
} else if (
|
|
973
|
-
config.raw &&
|
|
919
|
+
config.data === DataOptions.raw &&
|
|
974
920
|
(config._isPagination || config.lastEvaluatedKeyRaw)
|
|
975
921
|
) {
|
|
976
922
|
results = response;
|
|
@@ -1372,7 +1318,7 @@ class Entity {
|
|
|
1372
1318
|
|
|
1373
1319
|
_formatReturnPager(config, lastEvaluatedKey) {
|
|
1374
1320
|
let page = lastEvaluatedKey || null;
|
|
1375
|
-
if (config.raw || config.pager === Pager.raw) {
|
|
1321
|
+
if (config.data === DataOptions.raw || config.pager === Pager.raw) {
|
|
1376
1322
|
return page;
|
|
1377
1323
|
}
|
|
1378
1324
|
return config.formatCursor.serialize(page) || null;
|
|
@@ -1380,7 +1326,7 @@ class Entity {
|
|
|
1380
1326
|
|
|
1381
1327
|
_formatExclusiveStartKey({ config, indexName = TableIndex }) {
|
|
1382
1328
|
let exclusiveStartKey = config.cursor;
|
|
1383
|
-
if (config.raw || config.pager === Pager.raw) {
|
|
1329
|
+
if (config.data === DataOptions.raw || config.pager === Pager.raw) {
|
|
1384
1330
|
return (
|
|
1385
1331
|
this._trimKeysToIndex({ provided: exclusiveStartKey, indexName }) ||
|
|
1386
1332
|
null
|
|
@@ -1683,6 +1629,9 @@ class Entity {
|
|
|
1683
1629
|
response: "default",
|
|
1684
1630
|
cursor: null,
|
|
1685
1631
|
data: "attributes",
|
|
1632
|
+
consistent: undefined,
|
|
1633
|
+
compare: ComparisonTypes.keys,
|
|
1634
|
+
complete: false,
|
|
1686
1635
|
ignoreOwnership: false,
|
|
1687
1636
|
_providedIgnoreOwnership: false,
|
|
1688
1637
|
_isPagination: false,
|
|
@@ -1717,6 +1666,23 @@ class Entity {
|
|
|
1717
1666
|
}
|
|
1718
1667
|
}
|
|
1719
1668
|
|
|
1669
|
+
if (typeof option.compare === "string") {
|
|
1670
|
+
const type = ComparisonTypes[option.compare.toLowerCase()];
|
|
1671
|
+
if (type) {
|
|
1672
|
+
config.compare = type;
|
|
1673
|
+
if (type === ComparisonTypes.v2 && option.complete === undefined) {
|
|
1674
|
+
config.complete = true;
|
|
1675
|
+
}
|
|
1676
|
+
} else {
|
|
1677
|
+
throw new e.ElectroError(
|
|
1678
|
+
e.ErrorCodes.InvalidOptions,
|
|
1679
|
+
`Invalid value for query option "compare" provided. Valid options include ${u.commaSeparatedString(
|
|
1680
|
+
Object.keys(ComparisonTypes),
|
|
1681
|
+
)}, received: "${option.compare}"`,
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1720
1686
|
if (typeof option.response === "string" && option.response.length) {
|
|
1721
1687
|
const format = ReturnValues[option.response];
|
|
1722
1688
|
if (format === undefined) {
|
|
@@ -1728,13 +1694,14 @@ class Entity {
|
|
|
1728
1694
|
Object.keys(ReturnValues),
|
|
1729
1695
|
)}.`,
|
|
1730
1696
|
);
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1697
|
+
} else if (format !== ReturnValues.default) {
|
|
1698
|
+
config.response = format;
|
|
1699
|
+
if (context.operation === MethodTypes.transactWrite) {
|
|
1700
|
+
config.params.ReturnValuesOnConditionCheckFailure =
|
|
1701
|
+
FormatToReturnValues[format];
|
|
1702
|
+
} else {
|
|
1703
|
+
config.params.ReturnValues = FormatToReturnValues[format];
|
|
1704
|
+
}
|
|
1738
1705
|
}
|
|
1739
1706
|
}
|
|
1740
1707
|
|
|
@@ -1801,12 +1768,20 @@ class Entity {
|
|
|
1801
1768
|
}
|
|
1802
1769
|
|
|
1803
1770
|
if (option.data) {
|
|
1771
|
+
if (!DataOptions[option.data]) {
|
|
1772
|
+
throw new e.ElectroError(
|
|
1773
|
+
e.ErrorCodes.InvalidOptions,
|
|
1774
|
+
`Query option 'data' must be one of ${u.commaSeparatedString(
|
|
1775
|
+
Object.keys(DataOptions),
|
|
1776
|
+
)}.`,
|
|
1777
|
+
);
|
|
1778
|
+
}
|
|
1804
1779
|
config.data = option.data;
|
|
1805
1780
|
switch (option.data) {
|
|
1806
|
-
case
|
|
1781
|
+
case DataOptions.raw:
|
|
1807
1782
|
config.raw = true;
|
|
1808
1783
|
break;
|
|
1809
|
-
case
|
|
1784
|
+
case DataOptions.includeKeys:
|
|
1810
1785
|
config.includeKeys = true;
|
|
1811
1786
|
break;
|
|
1812
1787
|
}
|
|
@@ -1814,11 +1789,19 @@ class Entity {
|
|
|
1814
1789
|
|
|
1815
1790
|
if (option.count !== undefined) {
|
|
1816
1791
|
if (typeof option.count !== "number" || option.count < 1) {
|
|
1817
|
-
throw new e.ElectroError(
|
|
1792
|
+
throw new e.ElectroError(
|
|
1793
|
+
e.ErrorCodes.InvalidOptions,
|
|
1794
|
+
`Query option 'count' must be of type 'number' and greater than zero.`,
|
|
1795
|
+
);
|
|
1818
1796
|
}
|
|
1819
1797
|
config.count = option.count;
|
|
1820
1798
|
}
|
|
1821
1799
|
|
|
1800
|
+
if (option.consistent === true) {
|
|
1801
|
+
config.consistent = true;
|
|
1802
|
+
config.params.ConsistentRead = true;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1822
1805
|
if (option.limit !== undefined) {
|
|
1823
1806
|
config.limit = option.limit;
|
|
1824
1807
|
config.params.Limit = option.limit;
|
|
@@ -2064,7 +2047,7 @@ class Entity {
|
|
|
2064
2047
|
return parameters;
|
|
2065
2048
|
}
|
|
2066
2049
|
|
|
2067
|
-
const requiresRawResponse =
|
|
2050
|
+
const requiresRawResponse = config.data === DataOptions.raw;
|
|
2068
2051
|
const enforcesOwnership = !config.ignoreOwnership;
|
|
2069
2052
|
const requiresUserInvolvedPagination =
|
|
2070
2053
|
TerminalOperation[config.terminalOperation] === TerminalOperation.page;
|
|
@@ -2258,7 +2241,7 @@ class Entity {
|
|
|
2258
2241
|
let keys = this._makeParameterKey(indexBase, pk, ...sk);
|
|
2259
2242
|
// trim empty key values (this can occur when keys are defined by users)
|
|
2260
2243
|
for (let key in keys) {
|
|
2261
|
-
if (keys[key] === undefined || keys[key] ===
|
|
2244
|
+
if (keys[key] === undefined || keys[key] === "") {
|
|
2262
2245
|
delete keys[key];
|
|
2263
2246
|
}
|
|
2264
2247
|
}
|
|
@@ -2266,26 +2249,25 @@ class Entity {
|
|
|
2266
2249
|
let keyExpressions = this._expressionAttributeBuilder(keys);
|
|
2267
2250
|
|
|
2268
2251
|
const expressionAttributeNames = this._mergeExpressionsAttributes(
|
|
2269
|
-
|
|
2270
|
-
|
|
2252
|
+
filter.getNames(),
|
|
2253
|
+
keyExpressions.ExpressionAttributeNames,
|
|
2271
2254
|
);
|
|
2272
2255
|
|
|
2273
2256
|
const expressionAttributeValues = this._mergeExpressionsAttributes(
|
|
2274
|
-
|
|
2275
|
-
|
|
2257
|
+
filter.getValues(),
|
|
2258
|
+
keyExpressions.ExpressionAttributeValues,
|
|
2276
2259
|
);
|
|
2277
2260
|
|
|
2278
|
-
|
|
2279
2261
|
let params = {
|
|
2280
2262
|
TableName: this.getTableName(),
|
|
2281
2263
|
};
|
|
2282
2264
|
|
|
2283
2265
|
if (Object.keys(expressionAttributeNames).length) {
|
|
2284
|
-
params[
|
|
2266
|
+
params["ExpressionAttributeNames"] = expressionAttributeNames;
|
|
2285
2267
|
}
|
|
2286
2268
|
|
|
2287
2269
|
if (Object.keys(expressionAttributeValues).length) {
|
|
2288
|
-
params[
|
|
2270
|
+
params["ExpressionAttributeValues"] = expressionAttributeValues;
|
|
2289
2271
|
}
|
|
2290
2272
|
|
|
2291
2273
|
let filterExpressions = [];
|
|
@@ -2306,7 +2288,7 @@ class Entity {
|
|
|
2306
2288
|
}
|
|
2307
2289
|
|
|
2308
2290
|
if (filterExpressions.length) {
|
|
2309
|
-
params.FilterExpression = filterExpressions.join(
|
|
2291
|
+
params.FilterExpression = filterExpressions.join(" AND ");
|
|
2310
2292
|
}
|
|
2311
2293
|
|
|
2312
2294
|
return params;
|
|
@@ -2365,7 +2347,13 @@ class Entity {
|
|
|
2365
2347
|
indexKey,
|
|
2366
2348
|
updatedKeys,
|
|
2367
2349
|
deletedKeys = [],
|
|
2368
|
-
} = this._getUpdatedKeys(
|
|
2350
|
+
} = this._getUpdatedKeys(
|
|
2351
|
+
pk,
|
|
2352
|
+
sk,
|
|
2353
|
+
attributesAndComposites,
|
|
2354
|
+
removed,
|
|
2355
|
+
update.composites,
|
|
2356
|
+
);
|
|
2369
2357
|
const accessPattern =
|
|
2370
2358
|
this.model.translations.indexes.fromIndexToAccessPattern[TableIndex];
|
|
2371
2359
|
for (const path of Object.keys(preparedUpdateValues)) {
|
|
@@ -2645,7 +2633,7 @@ class Entity {
|
|
|
2645
2633
|
return expressions;
|
|
2646
2634
|
}
|
|
2647
2635
|
|
|
2648
|
-
_makeQueryKeys(state) {
|
|
2636
|
+
_makeQueryKeys(state, options) {
|
|
2649
2637
|
let consolidatedQueryFacets = this._consolidateQueryFacets(
|
|
2650
2638
|
state.query.keys.sk,
|
|
2651
2639
|
);
|
|
@@ -2660,13 +2648,17 @@ class Entity {
|
|
|
2660
2648
|
isCollection: state.query.options._isCollectionQuery,
|
|
2661
2649
|
});
|
|
2662
2650
|
default:
|
|
2663
|
-
return this._makeIndexKeysWithoutTail(
|
|
2651
|
+
return this._makeIndexKeysWithoutTail(
|
|
2652
|
+
state,
|
|
2653
|
+
consolidatedQueryFacets,
|
|
2654
|
+
options,
|
|
2655
|
+
);
|
|
2664
2656
|
}
|
|
2665
2657
|
}
|
|
2666
2658
|
|
|
2667
2659
|
/* istanbul ignore next */
|
|
2668
2660
|
_queryParams(state = {}, options = {}) {
|
|
2669
|
-
const indexKeys = this._makeQueryKeys(state);
|
|
2661
|
+
const indexKeys = this._makeQueryKeys(state, options);
|
|
2670
2662
|
let parameters = {};
|
|
2671
2663
|
switch (state.query.type) {
|
|
2672
2664
|
case QueryTypes.is:
|
|
@@ -2707,6 +2699,7 @@ class Entity {
|
|
|
2707
2699
|
break;
|
|
2708
2700
|
case QueryTypes.between:
|
|
2709
2701
|
parameters = this._makeBetweenQueryParams(
|
|
2702
|
+
state.query.options,
|
|
2710
2703
|
state.query.index,
|
|
2711
2704
|
state.query.filter[ExpressionTypes.FilterExpression],
|
|
2712
2705
|
indexKeys.pk,
|
|
@@ -2722,6 +2715,8 @@ class Entity {
|
|
|
2722
2715
|
state.query.type,
|
|
2723
2716
|
state.query.filter[ExpressionTypes.FilterExpression],
|
|
2724
2717
|
indexKeys,
|
|
2718
|
+
options,
|
|
2719
|
+
state.query.options,
|
|
2725
2720
|
);
|
|
2726
2721
|
break;
|
|
2727
2722
|
default:
|
|
@@ -2739,31 +2734,50 @@ class Entity {
|
|
|
2739
2734
|
});
|
|
2740
2735
|
}
|
|
2741
2736
|
|
|
2742
|
-
_makeBetweenQueryParams(index, filter, pk, ...sk) {
|
|
2737
|
+
_makeBetweenQueryParams(queryOptions, index, filter, pk, ...sk) {
|
|
2743
2738
|
let keyExpressions = this._queryKeyExpressionAttributeBuilder(
|
|
2744
2739
|
index,
|
|
2745
2740
|
pk,
|
|
2746
2741
|
...sk,
|
|
2747
2742
|
);
|
|
2743
|
+
|
|
2748
2744
|
delete keyExpressions.ExpressionAttributeNames["#sk2"];
|
|
2745
|
+
|
|
2746
|
+
const customExpressions = {
|
|
2747
|
+
names: (queryOptions.expressions && queryOptions.expressions.names) || {},
|
|
2748
|
+
values:
|
|
2749
|
+
(queryOptions.expressions && queryOptions.expressions.values) || {},
|
|
2750
|
+
expression:
|
|
2751
|
+
(queryOptions.expressions && queryOptions.expressions.expression) || "",
|
|
2752
|
+
};
|
|
2753
|
+
|
|
2749
2754
|
let params = {
|
|
2750
2755
|
TableName: this.getTableName(),
|
|
2751
2756
|
ExpressionAttributeNames: this._mergeExpressionsAttributes(
|
|
2752
2757
|
filter.getNames(),
|
|
2753
2758
|
keyExpressions.ExpressionAttributeNames,
|
|
2759
|
+
customExpressions.names,
|
|
2754
2760
|
),
|
|
2755
2761
|
ExpressionAttributeValues: this._mergeExpressionsAttributes(
|
|
2756
2762
|
filter.getValues(),
|
|
2757
2763
|
keyExpressions.ExpressionAttributeValues,
|
|
2764
|
+
customExpressions.values,
|
|
2758
2765
|
),
|
|
2759
2766
|
KeyConditionExpression: `#pk = :pk and #sk1 BETWEEN :sk1 AND :sk2`,
|
|
2760
2767
|
};
|
|
2768
|
+
|
|
2761
2769
|
if (index) {
|
|
2762
2770
|
params["IndexName"] = index;
|
|
2763
2771
|
}
|
|
2764
|
-
|
|
2765
|
-
|
|
2772
|
+
|
|
2773
|
+
let expressions = [customExpressions.expression, filter.build()]
|
|
2774
|
+
.filter(Boolean)
|
|
2775
|
+
.join(" AND ");
|
|
2776
|
+
|
|
2777
|
+
if (expressions.length) {
|
|
2778
|
+
params.FilterExpression = expressions;
|
|
2766
2779
|
}
|
|
2780
|
+
|
|
2767
2781
|
return params;
|
|
2768
2782
|
}
|
|
2769
2783
|
|
|
@@ -2881,28 +2895,51 @@ class Entity {
|
|
|
2881
2895
|
return merged;
|
|
2882
2896
|
}
|
|
2883
2897
|
|
|
2898
|
+
_getComparisonOperator(comparison, skType, comparisonType) {
|
|
2899
|
+
if (skType === "number") {
|
|
2900
|
+
return Comparisons[comparison];
|
|
2901
|
+
} else if (
|
|
2902
|
+
comparisonType === ComparisonTypes.v2
|
|
2903
|
+
) {
|
|
2904
|
+
return KeyAttributesComparisons[comparison];
|
|
2905
|
+
} else {
|
|
2906
|
+
return Comparisons[comparison];
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2884
2910
|
/* istanbul ignore next */
|
|
2885
2911
|
_makeComparisonQueryParams(
|
|
2886
2912
|
index = TableIndex,
|
|
2887
2913
|
comparison = "",
|
|
2888
2914
|
filter = {},
|
|
2889
2915
|
indexKeys = {},
|
|
2916
|
+
options = {},
|
|
2917
|
+
queryOptions = {},
|
|
2890
2918
|
) {
|
|
2891
2919
|
const { pk } = indexKeys;
|
|
2892
2920
|
const sk = indexKeys.sk[0];
|
|
2893
2921
|
|
|
2894
|
-
let operator =
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2922
|
+
let operator = this._getComparisonOperator(
|
|
2923
|
+
comparison,
|
|
2924
|
+
typeof sk,
|
|
2925
|
+
options.compare,
|
|
2926
|
+
);
|
|
2899
2927
|
if (!operator) {
|
|
2900
2928
|
throw new Error(
|
|
2901
2929
|
`Unexpected comparison operator "${comparison}", expected ${u.commaSeparatedString(
|
|
2902
|
-
Object.
|
|
2930
|
+
Object.keys(KeyAttributesComparisons),
|
|
2903
2931
|
)}`,
|
|
2904
2932
|
);
|
|
2905
2933
|
}
|
|
2934
|
+
|
|
2935
|
+
let customExpressions = {
|
|
2936
|
+
names: (queryOptions.expressions && queryOptions.expressions.names) || {},
|
|
2937
|
+
values:
|
|
2938
|
+
(queryOptions.expressions && queryOptions.expressions.values) || {},
|
|
2939
|
+
expression:
|
|
2940
|
+
(queryOptions.expressions && queryOptions.expressions.expression) || "",
|
|
2941
|
+
};
|
|
2942
|
+
|
|
2906
2943
|
let keyExpressions = this._queryKeyExpressionAttributeBuilder(
|
|
2907
2944
|
index,
|
|
2908
2945
|
pk,
|
|
@@ -2914,27 +2951,40 @@ class Entity {
|
|
|
2914
2951
|
ExpressionAttributeNames: this._mergeExpressionsAttributes(
|
|
2915
2952
|
filter.getNames(),
|
|
2916
2953
|
keyExpressions.ExpressionAttributeNames,
|
|
2954
|
+
customExpressions.names,
|
|
2917
2955
|
),
|
|
2918
2956
|
ExpressionAttributeValues: this._mergeExpressionsAttributes(
|
|
2919
2957
|
filter.getValues(),
|
|
2920
2958
|
keyExpressions.ExpressionAttributeValues,
|
|
2959
|
+
customExpressions.values,
|
|
2921
2960
|
),
|
|
2922
2961
|
KeyConditionExpression: `#pk = :pk and #sk1 ${operator} :sk1`,
|
|
2923
2962
|
};
|
|
2963
|
+
|
|
2924
2964
|
if (index) {
|
|
2925
2965
|
params["IndexName"] = index;
|
|
2926
2966
|
}
|
|
2927
|
-
|
|
2928
|
-
|
|
2967
|
+
|
|
2968
|
+
let expressions = [customExpressions.expression, filter.build()]
|
|
2969
|
+
.filter(Boolean)
|
|
2970
|
+
.join(" AND ");
|
|
2971
|
+
|
|
2972
|
+
if (expressions.length) {
|
|
2973
|
+
params.FilterExpression = expressions;
|
|
2929
2974
|
}
|
|
2975
|
+
|
|
2930
2976
|
return params;
|
|
2931
2977
|
}
|
|
2932
2978
|
|
|
2933
|
-
_expectIndexFacets(
|
|
2979
|
+
_expectIndexFacets(
|
|
2980
|
+
attributes,
|
|
2981
|
+
facets,
|
|
2982
|
+
{ utilizeIncludedOnlyIndexes, skipConditionCheck } = {},
|
|
2983
|
+
) {
|
|
2934
2984
|
let [isIncomplete, { incomplete, complete }] = this._getIndexImpact(
|
|
2935
2985
|
attributes,
|
|
2936
2986
|
facets,
|
|
2937
|
-
|
|
2987
|
+
{ utilizeIncludedOnlyIndexes, skipConditionCheck },
|
|
2938
2988
|
);
|
|
2939
2989
|
|
|
2940
2990
|
if (isIncomplete) {
|
|
@@ -2963,7 +3013,8 @@ class Entity {
|
|
|
2963
3013
|
_makeKeysFromAttributes(indexes, attributes, conditions) {
|
|
2964
3014
|
let indexKeys = {};
|
|
2965
3015
|
for (let [index, keyTypes] of Object.entries(indexes)) {
|
|
2966
|
-
const shouldMakeKeys =
|
|
3016
|
+
const shouldMakeKeys =
|
|
3017
|
+
!this._indexConditionIsDefined(index) || conditions[index];
|
|
2967
3018
|
if (!shouldMakeKeys && index !== TableIndex) {
|
|
2968
3019
|
continue;
|
|
2969
3020
|
}
|
|
@@ -2994,7 +3045,10 @@ class Entity {
|
|
|
2994
3045
|
_makePutKeysFromAttributes(indexes, attributes) {
|
|
2995
3046
|
let indexKeys = {};
|
|
2996
3047
|
for (let index of indexes) {
|
|
2997
|
-
const shouldMakeKeys =
|
|
3048
|
+
const shouldMakeKeys =
|
|
3049
|
+
this.model.indexes[
|
|
3050
|
+
this.model.translations.indexes.fromIndexToAccessPattern[index]
|
|
3051
|
+
].condition(attributes);
|
|
2998
3052
|
if (!shouldMakeKeys) {
|
|
2999
3053
|
continue;
|
|
3000
3054
|
}
|
|
@@ -3019,11 +3073,15 @@ class Entity {
|
|
|
3019
3073
|
);
|
|
3020
3074
|
|
|
3021
3075
|
let deletedKeys = [];
|
|
3022
|
-
for (const [indexName, condition] of Object.entries(
|
|
3076
|
+
for (const [indexName, condition] of Object.entries(
|
|
3077
|
+
completeFacets.conditions,
|
|
3078
|
+
)) {
|
|
3023
3079
|
if (!condition) {
|
|
3024
3080
|
deletedKeys.push(this.model.translations.keys[indexName][KeyTypes.pk]);
|
|
3025
3081
|
if (this.model.translations.keys[indexName][KeyTypes.sk]) {
|
|
3026
|
-
deletedKeys.push(
|
|
3082
|
+
deletedKeys.push(
|
|
3083
|
+
this.model.translations.keys[indexName][KeyTypes.sk],
|
|
3084
|
+
);
|
|
3027
3085
|
}
|
|
3028
3086
|
}
|
|
3029
3087
|
}
|
|
@@ -3071,7 +3129,7 @@ class Entity {
|
|
|
3071
3129
|
const removedKeyImpact = this._expectIndexFacets(
|
|
3072
3130
|
{ ...removed },
|
|
3073
3131
|
{ ...keyAttributes },
|
|
3074
|
-
{ skipConditionCheck: true }
|
|
3132
|
+
{ skipConditionCheck: true },
|
|
3075
3133
|
);
|
|
3076
3134
|
|
|
3077
3135
|
// complete facets, only includes impacted facets which likely does not include the updateIndex which then needs to be added here.
|
|
@@ -3091,11 +3149,15 @@ class Entity {
|
|
|
3091
3149
|
let updatedKeys = {};
|
|
3092
3150
|
let deletedKeys = [];
|
|
3093
3151
|
let indexKey = {};
|
|
3094
|
-
for (const [indexName, condition] of Object.entries(
|
|
3152
|
+
for (const [indexName, condition] of Object.entries(
|
|
3153
|
+
completeFacets.conditions,
|
|
3154
|
+
)) {
|
|
3095
3155
|
if (!condition) {
|
|
3096
3156
|
deletedKeys.push(this.model.translations.keys[indexName][KeyTypes.pk]);
|
|
3097
3157
|
if (this.model.translations.keys[indexName][KeyTypes.sk]) {
|
|
3098
|
-
|
|
3158
|
+
deletedKeys.push(
|
|
3159
|
+
this.model.translations.keys[indexName][KeyTypes.sk],
|
|
3160
|
+
);
|
|
3099
3161
|
}
|
|
3100
3162
|
}
|
|
3101
3163
|
}
|
|
@@ -3122,9 +3184,9 @@ class Entity {
|
|
|
3122
3184
|
let hasPrefix =
|
|
3123
3185
|
indexHasSk && this.model.prefixes[index].sk.prefix !== undefined;
|
|
3124
3186
|
let hasPostfix =
|
|
3125
|
-
|
|
3187
|
+
indexHasSk && this.model.prefixes[index].sk.prefix !== undefined;
|
|
3126
3188
|
if (noImpactSk && noAttributeSk) {
|
|
3127
|
-
let key = hasPrefix ? this.model.prefixes[index].sk.prefix :
|
|
3189
|
+
let key = hasPrefix ? this.model.prefixes[index].sk.prefix : "";
|
|
3128
3190
|
if (hasPostfix) {
|
|
3129
3191
|
key = `${key}${this.model.prefixes[index].sk.postfix}`;
|
|
3130
3192
|
}
|
|
@@ -3146,12 +3208,19 @@ class Entity {
|
|
|
3146
3208
|
}
|
|
3147
3209
|
|
|
3148
3210
|
_indexConditionIsDefined(index) {
|
|
3149
|
-
const definition =
|
|
3211
|
+
const definition =
|
|
3212
|
+
this.model.indexes[
|
|
3213
|
+
this.model.translations.indexes.fromIndexToAccessPattern[index]
|
|
3214
|
+
];
|
|
3150
3215
|
return definition && definition.conditionDefined;
|
|
3151
3216
|
}
|
|
3152
3217
|
|
|
3153
3218
|
/* istanbul ignore next */
|
|
3154
|
-
_getIndexImpact(
|
|
3219
|
+
_getIndexImpact(
|
|
3220
|
+
attributes = {},
|
|
3221
|
+
included = {},
|
|
3222
|
+
{ utilizeIncludedOnlyIndexes, skipConditionCheck } = {},
|
|
3223
|
+
) {
|
|
3155
3224
|
// beware: this entire algorithm stinks and needs to be completely refactored. It does redundant loops and fights
|
|
3156
3225
|
// itself the whole way through. I am sorry.
|
|
3157
3226
|
let includedFacets = Object.keys(included);
|
|
@@ -3166,21 +3235,26 @@ class Entity {
|
|
|
3166
3235
|
facets[attribute] = attributes[attribute];
|
|
3167
3236
|
indexes.forEach((definition) => {
|
|
3168
3237
|
const { index, type } = definition;
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3238
|
+
impactedIndexes[index] = impactedIndexes[index] || {};
|
|
3239
|
+
impactedIndexes[index][type] = impactedIndexes[index][type] || [];
|
|
3240
|
+
impactedIndexes[index][type].push(attribute);
|
|
3241
|
+
impactedIndexTypes[index] = impactedIndexTypes[index] || {};
|
|
3242
|
+
impactedIndexTypes[index][type] =
|
|
3243
|
+
this.model.translations.keys[index][type];
|
|
3174
3244
|
|
|
3175
|
-
|
|
3176
|
-
impactedIndexTypeSources[index]
|
|
3245
|
+
impactedIndexTypeSources[index] =
|
|
3246
|
+
impactedIndexTypeSources[index] || {};
|
|
3247
|
+
impactedIndexTypeSources[index][type] =
|
|
3248
|
+
ImpactedIndexTypeSource.provided;
|
|
3177
3249
|
});
|
|
3178
3250
|
}
|
|
3179
3251
|
}
|
|
3180
3252
|
|
|
3181
3253
|
// this function is used to determine key impact for update `set`, update `delete`, and `put`. This block is currently only used by update `set`
|
|
3182
3254
|
if (utilizeIncludedOnlyIndexes) {
|
|
3183
|
-
for (const [index, { pk, sk }] of Object.entries(
|
|
3255
|
+
for (const [index, { pk, sk }] of Object.entries(
|
|
3256
|
+
this.model.facets.byIndex,
|
|
3257
|
+
)) {
|
|
3184
3258
|
// The main table index is handled somewhere else (messy I know), and we only want to do this processing if an
|
|
3185
3259
|
// index condition is defined for backwards compatibility. Backwards compatibility is not required for this
|
|
3186
3260
|
// change, but I have paranoid concerns of breaking changes around sparse indexes.
|
|
@@ -3188,24 +3262,36 @@ class Entity {
|
|
|
3188
3262
|
continue;
|
|
3189
3263
|
}
|
|
3190
3264
|
|
|
3191
|
-
if (
|
|
3265
|
+
if (
|
|
3266
|
+
pk &&
|
|
3267
|
+
pk.length &&
|
|
3268
|
+
pk.every((attr) => included[attr] !== undefined)
|
|
3269
|
+
) {
|
|
3192
3270
|
pk.forEach((attr) => {
|
|
3193
3271
|
facets[attr] = included[attr];
|
|
3194
3272
|
});
|
|
3195
3273
|
impactedIndexes[index] = impactedIndexes[index] || {};
|
|
3196
3274
|
impactedIndexes[index][KeyTypes.pk] = [...pk];
|
|
3197
3275
|
impactedIndexTypes[index] = impactedIndexTypes[index] || {};
|
|
3198
|
-
impactedIndexTypes[index][KeyTypes.pk] =
|
|
3276
|
+
impactedIndexTypes[index][KeyTypes.pk] =
|
|
3277
|
+
this.model.translations.keys[index][KeyTypes.pk];
|
|
3199
3278
|
|
|
3200
3279
|
// flagging the impactedIndexTypeSource as `composite` means the entire key is only being impacted because
|
|
3201
3280
|
// all composites are in `included`. This will help us determine if we need to evaluate the `condition`
|
|
3202
3281
|
// callback for the index. If both the `sk` and `pk` were impacted because of `included` then we can skip
|
|
3203
3282
|
// the condition check because the index doesn't need to be recalculated;
|
|
3204
|
-
impactedIndexTypeSources[index] =
|
|
3205
|
-
|
|
3283
|
+
impactedIndexTypeSources[index] =
|
|
3284
|
+
impactedIndexTypeSources[index] || {};
|
|
3285
|
+
impactedIndexTypeSources[index][KeyTypes.pk] =
|
|
3286
|
+
impactedIndexTypeSources[index][KeyTypes.pk] ||
|
|
3287
|
+
ImpactedIndexTypeSource.composite;
|
|
3206
3288
|
}
|
|
3207
3289
|
|
|
3208
|
-
if (
|
|
3290
|
+
if (
|
|
3291
|
+
sk &&
|
|
3292
|
+
sk.length &&
|
|
3293
|
+
sk.every((attr) => included[attr] !== undefined)
|
|
3294
|
+
) {
|
|
3209
3295
|
if (this.model.translations.keys[index][KeyTypes.sk]) {
|
|
3210
3296
|
sk.forEach((attr) => {
|
|
3211
3297
|
facets[attr] = included[attr];
|
|
@@ -3213,61 +3299,66 @@ class Entity {
|
|
|
3213
3299
|
impactedIndexes[index] = impactedIndexes[index] || {};
|
|
3214
3300
|
impactedIndexes[index][KeyTypes.sk] = [...sk];
|
|
3215
3301
|
impactedIndexTypes[index] = impactedIndexTypes[index] || {};
|
|
3216
|
-
impactedIndexTypes[index][KeyTypes.sk] =
|
|
3302
|
+
impactedIndexTypes[index][KeyTypes.sk] =
|
|
3303
|
+
this.model.translations.keys[index][KeyTypes.sk];
|
|
3217
3304
|
|
|
3218
3305
|
// flagging the impactedIndexTypeSource as `composite` means the entire key is only being impacted because
|
|
3219
3306
|
// all composites are in `included`. This will help us determine if we need to evaluate the `condition`
|
|
3220
3307
|
// callback for the index. If both the `sk` and `pk` were impacted because of `included` then we can skip
|
|
3221
3308
|
// the condition check because the index doesn't need to be recalculated;
|
|
3222
|
-
impactedIndexTypeSources[index] =
|
|
3223
|
-
|
|
3309
|
+
impactedIndexTypeSources[index] =
|
|
3310
|
+
impactedIndexTypeSources[index] || {};
|
|
3311
|
+
impactedIndexTypeSources[index][KeyTypes.sk] =
|
|
3312
|
+
impactedIndexTypeSources[index][KeyTypes.sk] ||
|
|
3313
|
+
ImpactedIndexTypeSource.composite;
|
|
3224
3314
|
}
|
|
3225
3315
|
}
|
|
3226
3316
|
}
|
|
3227
3317
|
}
|
|
3228
3318
|
|
|
3229
|
-
let indexesWithMissingComposites = Object.entries(
|
|
3230
|
-
.
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
if (!missingPk && !missingSk) {
|
|
3265
|
-
completedIndexes.push(index);
|
|
3266
|
-
}
|
|
3319
|
+
let indexesWithMissingComposites = Object.entries(
|
|
3320
|
+
this.model.facets.byIndex,
|
|
3321
|
+
).map(([index, definition]) => {
|
|
3322
|
+
const { pk, sk } = definition;
|
|
3323
|
+
let impacted = impactedIndexes[index];
|
|
3324
|
+
let impact = {
|
|
3325
|
+
index,
|
|
3326
|
+
definition,
|
|
3327
|
+
missing: [],
|
|
3328
|
+
};
|
|
3329
|
+
if (impacted) {
|
|
3330
|
+
let missingPk =
|
|
3331
|
+
impacted[KeyTypes.pk] && impacted[KeyTypes.pk].length !== pk.length;
|
|
3332
|
+
let missingSk =
|
|
3333
|
+
impacted[KeyTypes.sk] && impacted[KeyTypes.sk].length !== sk.length;
|
|
3334
|
+
if (missingPk) {
|
|
3335
|
+
impact.missing = [
|
|
3336
|
+
...impact.missing,
|
|
3337
|
+
...pk.filter((attr) => {
|
|
3338
|
+
return (
|
|
3339
|
+
!impacted[KeyTypes.pk].includes(attr) &&
|
|
3340
|
+
!includedFacets.includes(attr)
|
|
3341
|
+
);
|
|
3342
|
+
}),
|
|
3343
|
+
];
|
|
3344
|
+
}
|
|
3345
|
+
if (missingSk) {
|
|
3346
|
+
impact.missing = [
|
|
3347
|
+
...impact.missing,
|
|
3348
|
+
...sk.filter(
|
|
3349
|
+
(attr) =>
|
|
3350
|
+
!impacted[KeyTypes.sk].includes(attr) &&
|
|
3351
|
+
!includedFacets.includes(attr),
|
|
3352
|
+
),
|
|
3353
|
+
];
|
|
3267
3354
|
}
|
|
3355
|
+
if (!missingPk && !missingSk) {
|
|
3356
|
+
completedIndexes.push(index);
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3268
3359
|
|
|
3269
|
-
|
|
3270
|
-
|
|
3360
|
+
return impact;
|
|
3361
|
+
});
|
|
3271
3362
|
|
|
3272
3363
|
let incomplete = [];
|
|
3273
3364
|
for (const { index, missing, definition } of indexesWithMissingComposites) {
|
|
@@ -3277,28 +3368,53 @@ class Entity {
|
|
|
3277
3368
|
// is meaningless and ElectroDB should uphold its obligation to keep keys and attributes in sync.
|
|
3278
3369
|
// `index === TableIndex` is a special case where we don't need to check the condition because the main table is immutable
|
|
3279
3370
|
// `!this._indexConditionIsDefined(index)` means the index doesn't have a condition defined, so we can skip the check
|
|
3280
|
-
if (
|
|
3371
|
+
if (
|
|
3372
|
+
skipConditionCheck ||
|
|
3373
|
+
index === TableIndex ||
|
|
3374
|
+
!indexConditionIsDefined
|
|
3375
|
+
) {
|
|
3281
3376
|
incomplete.push({ index, missing });
|
|
3282
3377
|
conditions[index] = true;
|
|
3283
3378
|
continue;
|
|
3284
3379
|
}
|
|
3285
3380
|
|
|
3286
|
-
const memberAttributeIsImpacted =
|
|
3287
|
-
|
|
3381
|
+
const memberAttributeIsImpacted =
|
|
3382
|
+
impactedIndexTypeSources[index] &&
|
|
3383
|
+
(impactedIndexTypeSources[index][KeyTypes.pk] ===
|
|
3384
|
+
ImpactedIndexTypeSource.provided ||
|
|
3385
|
+
impactedIndexTypeSources[index][KeyTypes.sk] ===
|
|
3386
|
+
ImpactedIndexTypeSource.provided);
|
|
3387
|
+
const allMemberAttributesAreIncluded = definition.all.every(
|
|
3388
|
+
({ name }) => included[name] !== undefined,
|
|
3389
|
+
);
|
|
3288
3390
|
|
|
3289
3391
|
if (memberAttributeIsImpacted || allMemberAttributesAreIncluded) {
|
|
3290
3392
|
// the `missing` array will contain indexes that are partially provided, but that leaves cases where the pk or
|
|
3291
3393
|
// sk of an index is complete but not both. Both cases are invalid if `indexConditionIsDefined=true`
|
|
3292
3394
|
const missingAttributes = definition.all
|
|
3293
|
-
|
|
3294
|
-
|
|
3395
|
+
.filter(
|
|
3396
|
+
({ name }) =>
|
|
3397
|
+
(attributes[name] === undefined &&
|
|
3398
|
+
included[name] === undefined) ||
|
|
3399
|
+
missing.includes(name),
|
|
3400
|
+
)
|
|
3401
|
+
.map(({ name }) => name);
|
|
3295
3402
|
|
|
3296
3403
|
if (missingAttributes.length) {
|
|
3297
|
-
throw new e.ElectroError(
|
|
3404
|
+
throw new e.ElectroError(
|
|
3405
|
+
e.ErrorCodes.IncompleteIndexCompositesAttributesProvided,
|
|
3406
|
+
`Incomplete composite attributes provided for index ${index}. Write operations that include composite attributes, for indexes with a condition callback defined, must always provide values for every index composite. This is to ensure consistency between index values and attribute values. Missing composite attributes identified: ${u.commaSeparatedString(
|
|
3407
|
+
missingAttributes,
|
|
3408
|
+
)}`,
|
|
3409
|
+
);
|
|
3298
3410
|
}
|
|
3299
3411
|
|
|
3300
|
-
const accessPattern =
|
|
3301
|
-
|
|
3412
|
+
const accessPattern =
|
|
3413
|
+
this.model.translations.indexes.fromIndexToAccessPattern[index];
|
|
3414
|
+
let shouldMakeKeys = !!this.model.indexes[accessPattern].condition({
|
|
3415
|
+
...attributes,
|
|
3416
|
+
...included,
|
|
3417
|
+
});
|
|
3302
3418
|
|
|
3303
3419
|
// this helps identify which conditions were checked (key is present) and what the result was (true/false)
|
|
3304
3420
|
conditions[index] = shouldMakeKeys;
|
|
@@ -3313,7 +3429,12 @@ class Entity {
|
|
|
3313
3429
|
incomplete = incomplete.filter(({ missing }) => missing.length);
|
|
3314
3430
|
|
|
3315
3431
|
let isIncomplete = !!incomplete.length;
|
|
3316
|
-
let complete = {
|
|
3432
|
+
let complete = {
|
|
3433
|
+
facets,
|
|
3434
|
+
indexes: completedIndexes,
|
|
3435
|
+
impactedIndexTypes,
|
|
3436
|
+
conditions,
|
|
3437
|
+
};
|
|
3317
3438
|
return [isIncomplete, { incomplete, complete }];
|
|
3318
3439
|
}
|
|
3319
3440
|
|
|
@@ -3555,34 +3676,32 @@ class Entity {
|
|
|
3555
3676
|
return prefix;
|
|
3556
3677
|
}
|
|
3557
3678
|
|
|
3558
|
-
_makeKeyTransforms(queryType) {
|
|
3679
|
+
_makeKeyTransforms(queryType, options = {}) {
|
|
3559
3680
|
const transforms = [];
|
|
3560
3681
|
const shiftUp = (val) => u.shiftSortOrder(val, 1);
|
|
3561
3682
|
const noop = (val) => val;
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
default:
|
|
3571
|
-
transforms.push(noop);
|
|
3572
|
-
break;
|
|
3683
|
+
if (options.compare !== ComparisonTypes.v2) {
|
|
3684
|
+
transforms.push(noop);
|
|
3685
|
+
} else if (queryType === QueryTypes.between) {
|
|
3686
|
+
transforms.push(noop, shiftUp);
|
|
3687
|
+
} else if (queryType === QueryTypes.lte || queryType === QueryTypes.gt) {
|
|
3688
|
+
transforms.push(shiftUp);
|
|
3689
|
+
} else {
|
|
3690
|
+
transforms.push(noop);
|
|
3573
3691
|
}
|
|
3692
|
+
|
|
3574
3693
|
return transforms;
|
|
3575
3694
|
}
|
|
3576
3695
|
|
|
3577
3696
|
/* istanbul ignore next */
|
|
3578
|
-
_makeIndexKeysWithoutTail(state = {}, skFacets = []) {
|
|
3697
|
+
_makeIndexKeysWithoutTail(state = {}, skFacets = [], options) {
|
|
3579
3698
|
const index = state.query.index || TableIndex;
|
|
3580
3699
|
this._validateIndex(index);
|
|
3581
3700
|
const pkFacets = state.query.keys.pk || {};
|
|
3582
3701
|
const excludePostfix =
|
|
3583
3702
|
state.query.options.indexType === IndexTypes.clustered &&
|
|
3584
3703
|
state.query.options._isCollectionQuery;
|
|
3585
|
-
const transforms = this._makeKeyTransforms(state.query.type);
|
|
3704
|
+
const transforms = this._makeKeyTransforms(state.query.type, options);
|
|
3586
3705
|
if (!skFacets.length) {
|
|
3587
3706
|
skFacets.push({});
|
|
3588
3707
|
}
|
|
@@ -4078,8 +4197,8 @@ class Entity {
|
|
|
4078
4197
|
let indexScope = index.scope || "";
|
|
4079
4198
|
if (index.index === undefined && v.isFunction(index.condition)) {
|
|
4080
4199
|
throw new e.ElectroError(
|
|
4081
|
-
|
|
4082
|
-
|
|
4200
|
+
e.ErrorCodes.InvalidIndexCondition,
|
|
4201
|
+
`The index option 'condition' is only allowed on secondary indexes`,
|
|
4083
4202
|
);
|
|
4084
4203
|
}
|
|
4085
4204
|
|
|
@@ -4184,7 +4303,7 @@ class Entity {
|
|
|
4184
4303
|
}
|
|
4185
4304
|
}
|
|
4186
4305
|
|
|
4187
|
-
let definition= {
|
|
4306
|
+
let definition = {
|
|
4188
4307
|
pk,
|
|
4189
4308
|
sk,
|
|
4190
4309
|
hasSk,
|