electrodb 2.15.0 → 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/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 +332 -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,52 @@ 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.attributes ||
|
|
2903
|
+
comparisonType === ComparisonTypes.v2
|
|
2904
|
+
) {
|
|
2905
|
+
return KeyAttributesComparisons[comparison];
|
|
2906
|
+
} else {
|
|
2907
|
+
return Comparisons[comparison];
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2884
2911
|
/* istanbul ignore next */
|
|
2885
2912
|
_makeComparisonQueryParams(
|
|
2886
2913
|
index = TableIndex,
|
|
2887
2914
|
comparison = "",
|
|
2888
2915
|
filter = {},
|
|
2889
2916
|
indexKeys = {},
|
|
2917
|
+
options = {},
|
|
2918
|
+
queryOptions = {},
|
|
2890
2919
|
) {
|
|
2891
2920
|
const { pk } = indexKeys;
|
|
2892
2921
|
const sk = indexKeys.sk[0];
|
|
2893
2922
|
|
|
2894
|
-
let operator =
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2923
|
+
let operator = this._getComparisonOperator(
|
|
2924
|
+
comparison,
|
|
2925
|
+
typeof sk,
|
|
2926
|
+
options.compare,
|
|
2927
|
+
);
|
|
2899
2928
|
if (!operator) {
|
|
2900
2929
|
throw new Error(
|
|
2901
2930
|
`Unexpected comparison operator "${comparison}", expected ${u.commaSeparatedString(
|
|
2902
|
-
Object.
|
|
2931
|
+
Object.keys(KeyAttributesComparisons),
|
|
2903
2932
|
)}`,
|
|
2904
2933
|
);
|
|
2905
2934
|
}
|
|
2935
|
+
|
|
2936
|
+
let customExpressions = {
|
|
2937
|
+
names: (queryOptions.expressions && queryOptions.expressions.names) || {},
|
|
2938
|
+
values:
|
|
2939
|
+
(queryOptions.expressions && queryOptions.expressions.values) || {},
|
|
2940
|
+
expression:
|
|
2941
|
+
(queryOptions.expressions && queryOptions.expressions.expression) || "",
|
|
2942
|
+
};
|
|
2943
|
+
|
|
2906
2944
|
let keyExpressions = this._queryKeyExpressionAttributeBuilder(
|
|
2907
2945
|
index,
|
|
2908
2946
|
pk,
|
|
@@ -2914,27 +2952,40 @@ class Entity {
|
|
|
2914
2952
|
ExpressionAttributeNames: this._mergeExpressionsAttributes(
|
|
2915
2953
|
filter.getNames(),
|
|
2916
2954
|
keyExpressions.ExpressionAttributeNames,
|
|
2955
|
+
customExpressions.names,
|
|
2917
2956
|
),
|
|
2918
2957
|
ExpressionAttributeValues: this._mergeExpressionsAttributes(
|
|
2919
2958
|
filter.getValues(),
|
|
2920
2959
|
keyExpressions.ExpressionAttributeValues,
|
|
2960
|
+
customExpressions.values,
|
|
2921
2961
|
),
|
|
2922
2962
|
KeyConditionExpression: `#pk = :pk and #sk1 ${operator} :sk1`,
|
|
2923
2963
|
};
|
|
2964
|
+
|
|
2924
2965
|
if (index) {
|
|
2925
2966
|
params["IndexName"] = index;
|
|
2926
2967
|
}
|
|
2927
|
-
|
|
2928
|
-
|
|
2968
|
+
|
|
2969
|
+
let expressions = [customExpressions.expression, filter.build()]
|
|
2970
|
+
.filter(Boolean)
|
|
2971
|
+
.join(" AND ");
|
|
2972
|
+
|
|
2973
|
+
if (expressions.length) {
|
|
2974
|
+
params.FilterExpression = expressions;
|
|
2929
2975
|
}
|
|
2976
|
+
|
|
2930
2977
|
return params;
|
|
2931
2978
|
}
|
|
2932
2979
|
|
|
2933
|
-
_expectIndexFacets(
|
|
2980
|
+
_expectIndexFacets(
|
|
2981
|
+
attributes,
|
|
2982
|
+
facets,
|
|
2983
|
+
{ utilizeIncludedOnlyIndexes, skipConditionCheck } = {},
|
|
2984
|
+
) {
|
|
2934
2985
|
let [isIncomplete, { incomplete, complete }] = this._getIndexImpact(
|
|
2935
2986
|
attributes,
|
|
2936
2987
|
facets,
|
|
2937
|
-
|
|
2988
|
+
{ utilizeIncludedOnlyIndexes, skipConditionCheck },
|
|
2938
2989
|
);
|
|
2939
2990
|
|
|
2940
2991
|
if (isIncomplete) {
|
|
@@ -2963,7 +3014,8 @@ class Entity {
|
|
|
2963
3014
|
_makeKeysFromAttributes(indexes, attributes, conditions) {
|
|
2964
3015
|
let indexKeys = {};
|
|
2965
3016
|
for (let [index, keyTypes] of Object.entries(indexes)) {
|
|
2966
|
-
const shouldMakeKeys =
|
|
3017
|
+
const shouldMakeKeys =
|
|
3018
|
+
!this._indexConditionIsDefined(index) || conditions[index];
|
|
2967
3019
|
if (!shouldMakeKeys && index !== TableIndex) {
|
|
2968
3020
|
continue;
|
|
2969
3021
|
}
|
|
@@ -2994,7 +3046,10 @@ class Entity {
|
|
|
2994
3046
|
_makePutKeysFromAttributes(indexes, attributes) {
|
|
2995
3047
|
let indexKeys = {};
|
|
2996
3048
|
for (let index of indexes) {
|
|
2997
|
-
const shouldMakeKeys =
|
|
3049
|
+
const shouldMakeKeys =
|
|
3050
|
+
this.model.indexes[
|
|
3051
|
+
this.model.translations.indexes.fromIndexToAccessPattern[index]
|
|
3052
|
+
].condition(attributes);
|
|
2998
3053
|
if (!shouldMakeKeys) {
|
|
2999
3054
|
continue;
|
|
3000
3055
|
}
|
|
@@ -3019,11 +3074,15 @@ class Entity {
|
|
|
3019
3074
|
);
|
|
3020
3075
|
|
|
3021
3076
|
let deletedKeys = [];
|
|
3022
|
-
for (const [indexName, condition] of Object.entries(
|
|
3077
|
+
for (const [indexName, condition] of Object.entries(
|
|
3078
|
+
completeFacets.conditions,
|
|
3079
|
+
)) {
|
|
3023
3080
|
if (!condition) {
|
|
3024
3081
|
deletedKeys.push(this.model.translations.keys[indexName][KeyTypes.pk]);
|
|
3025
3082
|
if (this.model.translations.keys[indexName][KeyTypes.sk]) {
|
|
3026
|
-
deletedKeys.push(
|
|
3083
|
+
deletedKeys.push(
|
|
3084
|
+
this.model.translations.keys[indexName][KeyTypes.sk],
|
|
3085
|
+
);
|
|
3027
3086
|
}
|
|
3028
3087
|
}
|
|
3029
3088
|
}
|
|
@@ -3071,7 +3130,7 @@ class Entity {
|
|
|
3071
3130
|
const removedKeyImpact = this._expectIndexFacets(
|
|
3072
3131
|
{ ...removed },
|
|
3073
3132
|
{ ...keyAttributes },
|
|
3074
|
-
{ skipConditionCheck: true }
|
|
3133
|
+
{ skipConditionCheck: true },
|
|
3075
3134
|
);
|
|
3076
3135
|
|
|
3077
3136
|
// complete facets, only includes impacted facets which likely does not include the updateIndex which then needs to be added here.
|
|
@@ -3091,11 +3150,15 @@ class Entity {
|
|
|
3091
3150
|
let updatedKeys = {};
|
|
3092
3151
|
let deletedKeys = [];
|
|
3093
3152
|
let indexKey = {};
|
|
3094
|
-
for (const [indexName, condition] of Object.entries(
|
|
3153
|
+
for (const [indexName, condition] of Object.entries(
|
|
3154
|
+
completeFacets.conditions,
|
|
3155
|
+
)) {
|
|
3095
3156
|
if (!condition) {
|
|
3096
3157
|
deletedKeys.push(this.model.translations.keys[indexName][KeyTypes.pk]);
|
|
3097
3158
|
if (this.model.translations.keys[indexName][KeyTypes.sk]) {
|
|
3098
|
-
|
|
3159
|
+
deletedKeys.push(
|
|
3160
|
+
this.model.translations.keys[indexName][KeyTypes.sk],
|
|
3161
|
+
);
|
|
3099
3162
|
}
|
|
3100
3163
|
}
|
|
3101
3164
|
}
|
|
@@ -3122,9 +3185,9 @@ class Entity {
|
|
|
3122
3185
|
let hasPrefix =
|
|
3123
3186
|
indexHasSk && this.model.prefixes[index].sk.prefix !== undefined;
|
|
3124
3187
|
let hasPostfix =
|
|
3125
|
-
|
|
3188
|
+
indexHasSk && this.model.prefixes[index].sk.prefix !== undefined;
|
|
3126
3189
|
if (noImpactSk && noAttributeSk) {
|
|
3127
|
-
let key = hasPrefix ? this.model.prefixes[index].sk.prefix :
|
|
3190
|
+
let key = hasPrefix ? this.model.prefixes[index].sk.prefix : "";
|
|
3128
3191
|
if (hasPostfix) {
|
|
3129
3192
|
key = `${key}${this.model.prefixes[index].sk.postfix}`;
|
|
3130
3193
|
}
|
|
@@ -3146,12 +3209,19 @@ class Entity {
|
|
|
3146
3209
|
}
|
|
3147
3210
|
|
|
3148
3211
|
_indexConditionIsDefined(index) {
|
|
3149
|
-
const definition =
|
|
3212
|
+
const definition =
|
|
3213
|
+
this.model.indexes[
|
|
3214
|
+
this.model.translations.indexes.fromIndexToAccessPattern[index]
|
|
3215
|
+
];
|
|
3150
3216
|
return definition && definition.conditionDefined;
|
|
3151
3217
|
}
|
|
3152
3218
|
|
|
3153
3219
|
/* istanbul ignore next */
|
|
3154
|
-
_getIndexImpact(
|
|
3220
|
+
_getIndexImpact(
|
|
3221
|
+
attributes = {},
|
|
3222
|
+
included = {},
|
|
3223
|
+
{ utilizeIncludedOnlyIndexes, skipConditionCheck } = {},
|
|
3224
|
+
) {
|
|
3155
3225
|
// beware: this entire algorithm stinks and needs to be completely refactored. It does redundant loops and fights
|
|
3156
3226
|
// itself the whole way through. I am sorry.
|
|
3157
3227
|
let includedFacets = Object.keys(included);
|
|
@@ -3166,21 +3236,26 @@ class Entity {
|
|
|
3166
3236
|
facets[attribute] = attributes[attribute];
|
|
3167
3237
|
indexes.forEach((definition) => {
|
|
3168
3238
|
const { index, type } = definition;
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3239
|
+
impactedIndexes[index] = impactedIndexes[index] || {};
|
|
3240
|
+
impactedIndexes[index][type] = impactedIndexes[index][type] || [];
|
|
3241
|
+
impactedIndexes[index][type].push(attribute);
|
|
3242
|
+
impactedIndexTypes[index] = impactedIndexTypes[index] || {};
|
|
3243
|
+
impactedIndexTypes[index][type] =
|
|
3244
|
+
this.model.translations.keys[index][type];
|
|
3174
3245
|
|
|
3175
|
-
|
|
3176
|
-
impactedIndexTypeSources[index]
|
|
3246
|
+
impactedIndexTypeSources[index] =
|
|
3247
|
+
impactedIndexTypeSources[index] || {};
|
|
3248
|
+
impactedIndexTypeSources[index][type] =
|
|
3249
|
+
ImpactedIndexTypeSource.provided;
|
|
3177
3250
|
});
|
|
3178
3251
|
}
|
|
3179
3252
|
}
|
|
3180
3253
|
|
|
3181
3254
|
// 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
3255
|
if (utilizeIncludedOnlyIndexes) {
|
|
3183
|
-
for (const [index, { pk, sk }] of Object.entries(
|
|
3256
|
+
for (const [index, { pk, sk }] of Object.entries(
|
|
3257
|
+
this.model.facets.byIndex,
|
|
3258
|
+
)) {
|
|
3184
3259
|
// The main table index is handled somewhere else (messy I know), and we only want to do this processing if an
|
|
3185
3260
|
// index condition is defined for backwards compatibility. Backwards compatibility is not required for this
|
|
3186
3261
|
// change, but I have paranoid concerns of breaking changes around sparse indexes.
|
|
@@ -3188,24 +3263,36 @@ class Entity {
|
|
|
3188
3263
|
continue;
|
|
3189
3264
|
}
|
|
3190
3265
|
|
|
3191
|
-
if (
|
|
3266
|
+
if (
|
|
3267
|
+
pk &&
|
|
3268
|
+
pk.length &&
|
|
3269
|
+
pk.every((attr) => included[attr] !== undefined)
|
|
3270
|
+
) {
|
|
3192
3271
|
pk.forEach((attr) => {
|
|
3193
3272
|
facets[attr] = included[attr];
|
|
3194
3273
|
});
|
|
3195
3274
|
impactedIndexes[index] = impactedIndexes[index] || {};
|
|
3196
3275
|
impactedIndexes[index][KeyTypes.pk] = [...pk];
|
|
3197
3276
|
impactedIndexTypes[index] = impactedIndexTypes[index] || {};
|
|
3198
|
-
impactedIndexTypes[index][KeyTypes.pk] =
|
|
3277
|
+
impactedIndexTypes[index][KeyTypes.pk] =
|
|
3278
|
+
this.model.translations.keys[index][KeyTypes.pk];
|
|
3199
3279
|
|
|
3200
3280
|
// flagging the impactedIndexTypeSource as `composite` means the entire key is only being impacted because
|
|
3201
3281
|
// all composites are in `included`. This will help us determine if we need to evaluate the `condition`
|
|
3202
3282
|
// callback for the index. If both the `sk` and `pk` were impacted because of `included` then we can skip
|
|
3203
3283
|
// the condition check because the index doesn't need to be recalculated;
|
|
3204
|
-
impactedIndexTypeSources[index] =
|
|
3205
|
-
|
|
3284
|
+
impactedIndexTypeSources[index] =
|
|
3285
|
+
impactedIndexTypeSources[index] || {};
|
|
3286
|
+
impactedIndexTypeSources[index][KeyTypes.pk] =
|
|
3287
|
+
impactedIndexTypeSources[index][KeyTypes.pk] ||
|
|
3288
|
+
ImpactedIndexTypeSource.composite;
|
|
3206
3289
|
}
|
|
3207
3290
|
|
|
3208
|
-
if (
|
|
3291
|
+
if (
|
|
3292
|
+
sk &&
|
|
3293
|
+
sk.length &&
|
|
3294
|
+
sk.every((attr) => included[attr] !== undefined)
|
|
3295
|
+
) {
|
|
3209
3296
|
if (this.model.translations.keys[index][KeyTypes.sk]) {
|
|
3210
3297
|
sk.forEach((attr) => {
|
|
3211
3298
|
facets[attr] = included[attr];
|
|
@@ -3213,61 +3300,66 @@ class Entity {
|
|
|
3213
3300
|
impactedIndexes[index] = impactedIndexes[index] || {};
|
|
3214
3301
|
impactedIndexes[index][KeyTypes.sk] = [...sk];
|
|
3215
3302
|
impactedIndexTypes[index] = impactedIndexTypes[index] || {};
|
|
3216
|
-
impactedIndexTypes[index][KeyTypes.sk] =
|
|
3303
|
+
impactedIndexTypes[index][KeyTypes.sk] =
|
|
3304
|
+
this.model.translations.keys[index][KeyTypes.sk];
|
|
3217
3305
|
|
|
3218
3306
|
// flagging the impactedIndexTypeSource as `composite` means the entire key is only being impacted because
|
|
3219
3307
|
// all composites are in `included`. This will help us determine if we need to evaluate the `condition`
|
|
3220
3308
|
// callback for the index. If both the `sk` and `pk` were impacted because of `included` then we can skip
|
|
3221
3309
|
// the condition check because the index doesn't need to be recalculated;
|
|
3222
|
-
impactedIndexTypeSources[index] =
|
|
3223
|
-
|
|
3310
|
+
impactedIndexTypeSources[index] =
|
|
3311
|
+
impactedIndexTypeSources[index] || {};
|
|
3312
|
+
impactedIndexTypeSources[index][KeyTypes.sk] =
|
|
3313
|
+
impactedIndexTypeSources[index][KeyTypes.sk] ||
|
|
3314
|
+
ImpactedIndexTypeSource.composite;
|
|
3224
3315
|
}
|
|
3225
3316
|
}
|
|
3226
3317
|
}
|
|
3227
3318
|
}
|
|
3228
3319
|
|
|
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
|
-
}
|
|
3320
|
+
let indexesWithMissingComposites = Object.entries(
|
|
3321
|
+
this.model.facets.byIndex,
|
|
3322
|
+
).map(([index, definition]) => {
|
|
3323
|
+
const { pk, sk } = definition;
|
|
3324
|
+
let impacted = impactedIndexes[index];
|
|
3325
|
+
let impact = {
|
|
3326
|
+
index,
|
|
3327
|
+
definition,
|
|
3328
|
+
missing: [],
|
|
3329
|
+
};
|
|
3330
|
+
if (impacted) {
|
|
3331
|
+
let missingPk =
|
|
3332
|
+
impacted[KeyTypes.pk] && impacted[KeyTypes.pk].length !== pk.length;
|
|
3333
|
+
let missingSk =
|
|
3334
|
+
impacted[KeyTypes.sk] && impacted[KeyTypes.sk].length !== sk.length;
|
|
3335
|
+
if (missingPk) {
|
|
3336
|
+
impact.missing = [
|
|
3337
|
+
...impact.missing,
|
|
3338
|
+
...pk.filter((attr) => {
|
|
3339
|
+
return (
|
|
3340
|
+
!impacted[KeyTypes.pk].includes(attr) &&
|
|
3341
|
+
!includedFacets.includes(attr)
|
|
3342
|
+
);
|
|
3343
|
+
}),
|
|
3344
|
+
];
|
|
3345
|
+
}
|
|
3346
|
+
if (missingSk) {
|
|
3347
|
+
impact.missing = [
|
|
3348
|
+
...impact.missing,
|
|
3349
|
+
...sk.filter(
|
|
3350
|
+
(attr) =>
|
|
3351
|
+
!impacted[KeyTypes.sk].includes(attr) &&
|
|
3352
|
+
!includedFacets.includes(attr),
|
|
3353
|
+
),
|
|
3354
|
+
];
|
|
3267
3355
|
}
|
|
3356
|
+
if (!missingPk && !missingSk) {
|
|
3357
|
+
completedIndexes.push(index);
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3268
3360
|
|
|
3269
|
-
|
|
3270
|
-
|
|
3361
|
+
return impact;
|
|
3362
|
+
});
|
|
3271
3363
|
|
|
3272
3364
|
let incomplete = [];
|
|
3273
3365
|
for (const { index, missing, definition } of indexesWithMissingComposites) {
|
|
@@ -3277,28 +3369,53 @@ class Entity {
|
|
|
3277
3369
|
// is meaningless and ElectroDB should uphold its obligation to keep keys and attributes in sync.
|
|
3278
3370
|
// `index === TableIndex` is a special case where we don't need to check the condition because the main table is immutable
|
|
3279
3371
|
// `!this._indexConditionIsDefined(index)` means the index doesn't have a condition defined, so we can skip the check
|
|
3280
|
-
if (
|
|
3372
|
+
if (
|
|
3373
|
+
skipConditionCheck ||
|
|
3374
|
+
index === TableIndex ||
|
|
3375
|
+
!indexConditionIsDefined
|
|
3376
|
+
) {
|
|
3281
3377
|
incomplete.push({ index, missing });
|
|
3282
3378
|
conditions[index] = true;
|
|
3283
3379
|
continue;
|
|
3284
3380
|
}
|
|
3285
3381
|
|
|
3286
|
-
const memberAttributeIsImpacted =
|
|
3287
|
-
|
|
3382
|
+
const memberAttributeIsImpacted =
|
|
3383
|
+
impactedIndexTypeSources[index] &&
|
|
3384
|
+
(impactedIndexTypeSources[index][KeyTypes.pk] ===
|
|
3385
|
+
ImpactedIndexTypeSource.provided ||
|
|
3386
|
+
impactedIndexTypeSources[index][KeyTypes.sk] ===
|
|
3387
|
+
ImpactedIndexTypeSource.provided);
|
|
3388
|
+
const allMemberAttributesAreIncluded = definition.all.every(
|
|
3389
|
+
({ name }) => included[name] !== undefined,
|
|
3390
|
+
);
|
|
3288
3391
|
|
|
3289
3392
|
if (memberAttributeIsImpacted || allMemberAttributesAreIncluded) {
|
|
3290
3393
|
// the `missing` array will contain indexes that are partially provided, but that leaves cases where the pk or
|
|
3291
3394
|
// sk of an index is complete but not both. Both cases are invalid if `indexConditionIsDefined=true`
|
|
3292
3395
|
const missingAttributes = definition.all
|
|
3293
|
-
|
|
3294
|
-
|
|
3396
|
+
.filter(
|
|
3397
|
+
({ name }) =>
|
|
3398
|
+
(attributes[name] === undefined &&
|
|
3399
|
+
included[name] === undefined) ||
|
|
3400
|
+
missing.includes(name),
|
|
3401
|
+
)
|
|
3402
|
+
.map(({ name }) => name);
|
|
3295
3403
|
|
|
3296
3404
|
if (missingAttributes.length) {
|
|
3297
|
-
throw new e.ElectroError(
|
|
3405
|
+
throw new e.ElectroError(
|
|
3406
|
+
e.ErrorCodes.IncompleteIndexCompositesAttributesProvided,
|
|
3407
|
+
`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(
|
|
3408
|
+
missingAttributes,
|
|
3409
|
+
)}`,
|
|
3410
|
+
);
|
|
3298
3411
|
}
|
|
3299
3412
|
|
|
3300
|
-
const accessPattern =
|
|
3301
|
-
|
|
3413
|
+
const accessPattern =
|
|
3414
|
+
this.model.translations.indexes.fromIndexToAccessPattern[index];
|
|
3415
|
+
let shouldMakeKeys = !!this.model.indexes[accessPattern].condition({
|
|
3416
|
+
...attributes,
|
|
3417
|
+
...included,
|
|
3418
|
+
});
|
|
3302
3419
|
|
|
3303
3420
|
// this helps identify which conditions were checked (key is present) and what the result was (true/false)
|
|
3304
3421
|
conditions[index] = shouldMakeKeys;
|
|
@@ -3313,7 +3430,12 @@ class Entity {
|
|
|
3313
3430
|
incomplete = incomplete.filter(({ missing }) => missing.length);
|
|
3314
3431
|
|
|
3315
3432
|
let isIncomplete = !!incomplete.length;
|
|
3316
|
-
let complete = {
|
|
3433
|
+
let complete = {
|
|
3434
|
+
facets,
|
|
3435
|
+
indexes: completedIndexes,
|
|
3436
|
+
impactedIndexTypes,
|
|
3437
|
+
conditions,
|
|
3438
|
+
};
|
|
3317
3439
|
return [isIncomplete, { incomplete, complete }];
|
|
3318
3440
|
}
|
|
3319
3441
|
|
|
@@ -3555,34 +3677,32 @@ class Entity {
|
|
|
3555
3677
|
return prefix;
|
|
3556
3678
|
}
|
|
3557
3679
|
|
|
3558
|
-
_makeKeyTransforms(queryType) {
|
|
3680
|
+
_makeKeyTransforms(queryType, options = {}) {
|
|
3559
3681
|
const transforms = [];
|
|
3560
3682
|
const shiftUp = (val) => u.shiftSortOrder(val, 1);
|
|
3561
3683
|
const noop = (val) => val;
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
default:
|
|
3571
|
-
transforms.push(noop);
|
|
3572
|
-
break;
|
|
3684
|
+
if (options.compare !== ComparisonTypes.v2) {
|
|
3685
|
+
transforms.push(noop);
|
|
3686
|
+
} else if (queryType === QueryTypes.between) {
|
|
3687
|
+
transforms.push(noop, shiftUp);
|
|
3688
|
+
} else if (queryType === QueryTypes.lte || queryType === QueryTypes.gt) {
|
|
3689
|
+
transforms.push(shiftUp);
|
|
3690
|
+
} else {
|
|
3691
|
+
transforms.push(noop);
|
|
3573
3692
|
}
|
|
3693
|
+
|
|
3574
3694
|
return transforms;
|
|
3575
3695
|
}
|
|
3576
3696
|
|
|
3577
3697
|
/* istanbul ignore next */
|
|
3578
|
-
_makeIndexKeysWithoutTail(state = {}, skFacets = []) {
|
|
3698
|
+
_makeIndexKeysWithoutTail(state = {}, skFacets = [], options) {
|
|
3579
3699
|
const index = state.query.index || TableIndex;
|
|
3580
3700
|
this._validateIndex(index);
|
|
3581
3701
|
const pkFacets = state.query.keys.pk || {};
|
|
3582
3702
|
const excludePostfix =
|
|
3583
3703
|
state.query.options.indexType === IndexTypes.clustered &&
|
|
3584
3704
|
state.query.options._isCollectionQuery;
|
|
3585
|
-
const transforms = this._makeKeyTransforms(state.query.type);
|
|
3705
|
+
const transforms = this._makeKeyTransforms(state.query.type, options);
|
|
3586
3706
|
if (!skFacets.length) {
|
|
3587
3707
|
skFacets.push({});
|
|
3588
3708
|
}
|
|
@@ -4078,8 +4198,8 @@ class Entity {
|
|
|
4078
4198
|
let indexScope = index.scope || "";
|
|
4079
4199
|
if (index.index === undefined && v.isFunction(index.condition)) {
|
|
4080
4200
|
throw new e.ElectroError(
|
|
4081
|
-
|
|
4082
|
-
|
|
4201
|
+
e.ErrorCodes.InvalidIndexCondition,
|
|
4202
|
+
`The index option 'condition' is only allowed on secondary indexes`,
|
|
4083
4203
|
);
|
|
4084
4204
|
}
|
|
4085
4205
|
|
|
@@ -4184,7 +4304,7 @@ class Entity {
|
|
|
4184
4304
|
}
|
|
4185
4305
|
}
|
|
4186
4306
|
|
|
4187
|
-
let definition= {
|
|
4307
|
+
let definition = {
|
|
4188
4308
|
pk,
|
|
4189
4309
|
sk,
|
|
4190
4310
|
hasSk,
|