electrodb 2.1.2 → 2.2.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/.travis.yml +1 -1
- package/README.md +40 -12
- package/index.d.ts +369 -6
- package/output +2299 -0
- package/package.json +9 -7
- package/src/clauses.js +148 -32
- package/src/client.js +2 -2
- package/src/entity.js +258 -90
- package/src/schema.js +45 -17
- package/src/service.js +145 -29
- package/src/types.js +56 -2
- package/src/util.js +15 -1
- package/src/validations.js +5 -0
- package/src/where.js +14 -18
package/src/entity.js
CHANGED
|
@@ -20,7 +20,9 @@ const { AllPages,
|
|
|
20
20
|
MaxBatchItems,
|
|
21
21
|
TerminalOperation,
|
|
22
22
|
ResultOrderOption,
|
|
23
|
-
ResultOrderParam
|
|
23
|
+
ResultOrderParam,
|
|
24
|
+
IndexTypes,
|
|
25
|
+
PartialComparisons,
|
|
24
26
|
} = require("./types");
|
|
25
27
|
const { FilterFactory } = require("./filters");
|
|
26
28
|
const { FilterOperations } = require("./operations");
|
|
@@ -57,7 +59,10 @@ class Entity {
|
|
|
57
59
|
for (let accessPattern in this.model.indexes) {
|
|
58
60
|
let index = this.model.indexes[accessPattern].index;
|
|
59
61
|
this.query[accessPattern] = (...values) => {
|
|
60
|
-
|
|
62
|
+
const options = {
|
|
63
|
+
indexType: this.model.indexes[accessPattern].type || IndexTypes.isolated,
|
|
64
|
+
}
|
|
65
|
+
return this._makeChain(index, this._clausesWithFilters, clauses.index, options).query(...values);
|
|
61
66
|
};
|
|
62
67
|
}
|
|
63
68
|
this.config.identifiers = config.identifiers || {};
|
|
@@ -191,14 +196,9 @@ class Entity {
|
|
|
191
196
|
}
|
|
192
197
|
}
|
|
193
198
|
|
|
194
|
-
collection(collection = "", clauses = {}, facets = {},
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
expressions: {
|
|
198
|
-
names: expressions.names || {},
|
|
199
|
-
values: expressions.values || {},
|
|
200
|
-
expression: expressions.expression || ""
|
|
201
|
-
},
|
|
199
|
+
collection(collection = "", clauses = {}, facets = {}, options = {}) {
|
|
200
|
+
const chainOptions = {
|
|
201
|
+
...options,
|
|
202
202
|
_isCollectionQuery: true,
|
|
203
203
|
};
|
|
204
204
|
|
|
@@ -206,10 +206,18 @@ class Entity {
|
|
|
206
206
|
if (index === undefined) {
|
|
207
207
|
throw new Error(`Invalid collection: ${collection}`);
|
|
208
208
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
209
|
+
const chain = this._makeChain(index, clauses, clauses.index, chainOptions);
|
|
210
|
+
if (options.indexType === IndexTypes.clustered) {
|
|
211
|
+
return chain.clusteredCollection(
|
|
212
|
+
collection,
|
|
213
|
+
facets,
|
|
214
|
+
);
|
|
215
|
+
} else {
|
|
216
|
+
return chain.collection(
|
|
217
|
+
collection,
|
|
218
|
+
facets,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
213
221
|
}
|
|
214
222
|
|
|
215
223
|
_validateModel(model) {
|
|
@@ -510,7 +518,7 @@ class Entity {
|
|
|
510
518
|
if (!response || !response.UnprocessedItems) {
|
|
511
519
|
return response;
|
|
512
520
|
}
|
|
513
|
-
const table = config.table || this.
|
|
521
|
+
const table = config.table || this.getTableName();
|
|
514
522
|
const index = TableIndex;
|
|
515
523
|
let unprocessed = response.UnprocessedItems[table];
|
|
516
524
|
if (Array.isArray(unprocessed) && unprocessed.length) {
|
|
@@ -541,7 +549,7 @@ class Entity {
|
|
|
541
549
|
response = {},
|
|
542
550
|
config = {},
|
|
543
551
|
}) {
|
|
544
|
-
const table = config.table || this.
|
|
552
|
+
const table = config.table || this.getTableName();
|
|
545
553
|
const index = TableIndex;
|
|
546
554
|
|
|
547
555
|
if (!response.UnprocessedKeys || !response.Responses) {
|
|
@@ -667,18 +675,22 @@ class Entity {
|
|
|
667
675
|
return config.formatCursor.deserialize(exclusiveStartKey) || null;
|
|
668
676
|
}
|
|
669
677
|
|
|
670
|
-
|
|
671
|
-
|
|
678
|
+
setClient(client) {
|
|
679
|
+
if (client) {
|
|
680
|
+
this.client = c.normalizeClient(client);
|
|
681
|
+
}
|
|
672
682
|
}
|
|
673
683
|
|
|
674
|
-
|
|
675
|
-
this.config.table =
|
|
684
|
+
setTableName(tableName) {
|
|
685
|
+
this.config.table = tableName;
|
|
676
686
|
}
|
|
677
687
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
688
|
+
getTableName() {
|
|
689
|
+
return this.config.table;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
getTableName() {
|
|
693
|
+
return this.config.table;
|
|
682
694
|
}
|
|
683
695
|
|
|
684
696
|
_chain(state, clauses, clause) {
|
|
@@ -702,9 +714,9 @@ class Entity {
|
|
|
702
714
|
let state = new ChainState({
|
|
703
715
|
index,
|
|
704
716
|
options,
|
|
705
|
-
attributes: this.model.schema.attributes,
|
|
706
|
-
hasSortKey: this.model.lookup.indexHasSortKeys[index],
|
|
707
|
-
compositeAttributes: this.model.facets.byIndex[index]
|
|
717
|
+
attributes: options.attributes || this.model.schema.attributes,
|
|
718
|
+
hasSortKey: options.hasSortKey || this.model.lookup.indexHasSortKeys[index],
|
|
719
|
+
compositeAttributes: options.compositeAttributes || this.model.facets.byIndex[index],
|
|
708
720
|
});
|
|
709
721
|
return state.init(this, clauses, rootClause);
|
|
710
722
|
}
|
|
@@ -837,9 +849,13 @@ class Entity {
|
|
|
837
849
|
}
|
|
838
850
|
|
|
839
851
|
_constructPagerIndex(index = TableIndex, item) {
|
|
840
|
-
let
|
|
841
|
-
let
|
|
842
|
-
let keys = this._makeIndexKeys(
|
|
852
|
+
let pkAttributes = this._expectFacets(item, this.model.facets.byIndex[index].pk);
|
|
853
|
+
let skAttributes = this._expectFacets(item, this.model.facets.byIndex[index].sk);
|
|
854
|
+
let keys = this._makeIndexKeys({
|
|
855
|
+
index,
|
|
856
|
+
pkAttributes,
|
|
857
|
+
skAttributes: [skAttributes],
|
|
858
|
+
});
|
|
843
859
|
return this._makeParameterKey(index, keys.pk, ...keys.sk);
|
|
844
860
|
}
|
|
845
861
|
|
|
@@ -908,8 +924,8 @@ class Entity {
|
|
|
908
924
|
|
|
909
925
|
if (option.formatCursor) {
|
|
910
926
|
const isValid = ['serialize', 'deserialize'].every(method =>
|
|
911
|
-
method in option.formatCursor &&
|
|
912
|
-
|
|
927
|
+
method in option.formatCursor &&
|
|
928
|
+
validations.isFunction(option.formatCursor[method])
|
|
913
929
|
);
|
|
914
930
|
if (isValid) {
|
|
915
931
|
config.formatCursor = option.formatCursor;
|
|
@@ -1219,7 +1235,7 @@ class Entity {
|
|
|
1219
1235
|
}
|
|
1220
1236
|
|
|
1221
1237
|
_batchGetParams(state, config = {}) {
|
|
1222
|
-
let table = config.table || this.
|
|
1238
|
+
let table = config.table || this.getTableName();
|
|
1223
1239
|
let userDefinedParams = config.params || {};
|
|
1224
1240
|
let records = [];
|
|
1225
1241
|
for (let itemState of state.subStates) {
|
|
@@ -1244,7 +1260,7 @@ class Entity {
|
|
|
1244
1260
|
}
|
|
1245
1261
|
|
|
1246
1262
|
_batchWriteParams(state, config = {}) {
|
|
1247
|
-
let table = config.table || this.
|
|
1263
|
+
let table = config.table || this.getTableName();
|
|
1248
1264
|
let records = [];
|
|
1249
1265
|
for (let itemState of state.subStates) {
|
|
1250
1266
|
let method = itemState.query.method;
|
|
@@ -1292,14 +1308,14 @@ class Entity {
|
|
|
1292
1308
|
let version = this.getVersion();
|
|
1293
1309
|
return {
|
|
1294
1310
|
names: {
|
|
1295
|
-
[`#${this.identifiers.entity}
|
|
1296
|
-
[`#${this.identifiers.version}
|
|
1311
|
+
[`#${this.identifiers.entity}`]: this.identifiers.entity,
|
|
1312
|
+
[`#${this.identifiers.version}`]: this.identifiers.version,
|
|
1297
1313
|
},
|
|
1298
1314
|
values: {
|
|
1299
1315
|
[`:${this.identifiers.entity}_${alias}`]: name,
|
|
1300
1316
|
[`:${this.identifiers.version}_${alias}`]: version,
|
|
1301
1317
|
},
|
|
1302
|
-
expression: `(#${this.identifiers.entity}
|
|
1318
|
+
expression: `(#${this.identifiers.entity} = :${this.identifiers.entity}_${alias} AND #${this.identifiers.version} = :${this.identifiers.version}_${alias})`
|
|
1303
1319
|
}
|
|
1304
1320
|
}
|
|
1305
1321
|
|
|
@@ -1309,11 +1325,13 @@ class Entity {
|
|
|
1309
1325
|
let hasSortKey = this.model.lookup.indexHasSortKeys[indexBase];
|
|
1310
1326
|
let accessPattern = this.model.translations.indexes.fromIndexToAccessPattern[indexBase];
|
|
1311
1327
|
let pkField = this.model.indexes[accessPattern].pk.field;
|
|
1312
|
-
let {pk, sk} = this._makeIndexKeys(
|
|
1328
|
+
let {pk, sk} = this._makeIndexKeys({
|
|
1329
|
+
index: indexBase,
|
|
1330
|
+
});
|
|
1313
1331
|
let keys = this._makeParameterKey(indexBase, pk, ...sk);
|
|
1314
1332
|
let keyExpressions = this._expressionAttributeBuilder(keys);
|
|
1315
1333
|
let params = {
|
|
1316
|
-
TableName: this.
|
|
1334
|
+
TableName: this.getTableName(),
|
|
1317
1335
|
ExpressionAttributeNames: this._mergeExpressionsAttributes(
|
|
1318
1336
|
filter.getNames(),
|
|
1319
1337
|
keyExpressions.ExpressionAttributeNames
|
|
@@ -1341,9 +1359,13 @@ class Entity {
|
|
|
1341
1359
|
|
|
1342
1360
|
_makeSimpleIndexParams(partition, sort) {
|
|
1343
1361
|
let index = TableIndex;
|
|
1344
|
-
let keys = this._makeIndexKeys(
|
|
1362
|
+
let keys = this._makeIndexKeys({
|
|
1363
|
+
index,
|
|
1364
|
+
pkAttributes: partition,
|
|
1365
|
+
skAttributes: [sort],
|
|
1366
|
+
});
|
|
1345
1367
|
let Key = this._makeParameterKey(index, keys.pk, ...keys.sk);
|
|
1346
|
-
let TableName = this.
|
|
1368
|
+
let TableName = this.getTableName();
|
|
1347
1369
|
return {Key, TableName};
|
|
1348
1370
|
}
|
|
1349
1371
|
|
|
@@ -1430,7 +1452,7 @@ class Entity {
|
|
|
1430
1452
|
UpdateExpression: update.build(),
|
|
1431
1453
|
ExpressionAttributeNames: update.getNames(),
|
|
1432
1454
|
ExpressionAttributeValues: update.getValues(),
|
|
1433
|
-
TableName: this.
|
|
1455
|
+
TableName: this.getTableName(),
|
|
1434
1456
|
Key: indexKey,
|
|
1435
1457
|
};
|
|
1436
1458
|
}
|
|
@@ -1447,7 +1469,7 @@ class Entity {
|
|
|
1447
1469
|
[this.identifiers.entity]: this.getName(),
|
|
1448
1470
|
[this.identifiers.version]: this.getVersion(),
|
|
1449
1471
|
},
|
|
1450
|
-
TableName: this.
|
|
1472
|
+
TableName: this.getTableName(),
|
|
1451
1473
|
};
|
|
1452
1474
|
}
|
|
1453
1475
|
|
|
@@ -1551,17 +1573,28 @@ class Entity {
|
|
|
1551
1573
|
return expressions;
|
|
1552
1574
|
}
|
|
1553
1575
|
|
|
1554
|
-
|
|
1555
|
-
_queryParams(state = {}, options = {}) {
|
|
1576
|
+
_makeQueryKeys(state) {
|
|
1556
1577
|
let consolidatedQueryFacets = this._consolidateQueryFacets(
|
|
1557
1578
|
state.query.keys.sk,
|
|
1558
1579
|
);
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1580
|
+
switch (state.query.type) {
|
|
1581
|
+
case QueryTypes.is:
|
|
1582
|
+
return this._makeIndexKeys({
|
|
1583
|
+
index: state.query.index,
|
|
1584
|
+
pkAttributes: state.query.keys.pk,
|
|
1585
|
+
skAttributes: consolidatedQueryFacets,
|
|
1586
|
+
indexType: state.query.options.indexType,
|
|
1587
|
+
queryType: state.query.type,
|
|
1588
|
+
isCollection: state.query.options._isCollectionQuery,
|
|
1589
|
+
});
|
|
1590
|
+
default:
|
|
1591
|
+
return this._makeIndexKeysWithoutTail(state, consolidatedQueryFacets);
|
|
1564
1592
|
}
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
/* istanbul ignore next */
|
|
1596
|
+
_queryParams(state = {}, options = {}) {
|
|
1597
|
+
const indexKeys = this._makeQueryKeys(state);
|
|
1565
1598
|
let parameters = {};
|
|
1566
1599
|
switch (state.query.type) {
|
|
1567
1600
|
case QueryTypes.is:
|
|
@@ -1591,6 +1624,15 @@ class Entity {
|
|
|
1591
1624
|
this._getCollectionSk(state.query.collection),
|
|
1592
1625
|
);
|
|
1593
1626
|
break;
|
|
1627
|
+
case QueryTypes.clustered_collection:
|
|
1628
|
+
parameters = this._makeBeginsWithQueryParams(
|
|
1629
|
+
state.query.options,
|
|
1630
|
+
state.query.index,
|
|
1631
|
+
state.query.filter[ExpressionTypes.FilterExpression],
|
|
1632
|
+
indexKeys.pk,
|
|
1633
|
+
...indexKeys.sk,
|
|
1634
|
+
);
|
|
1635
|
+
break;
|
|
1594
1636
|
case QueryTypes.between:
|
|
1595
1637
|
parameters = this._makeBetweenQueryParams(
|
|
1596
1638
|
state.query.index,
|
|
@@ -1607,8 +1649,7 @@ class Entity {
|
|
|
1607
1649
|
state.query.index,
|
|
1608
1650
|
state.query.type,
|
|
1609
1651
|
state.query.filter[ExpressionTypes.FilterExpression],
|
|
1610
|
-
indexKeys
|
|
1611
|
-
...indexKeys.sk,
|
|
1652
|
+
indexKeys,
|
|
1612
1653
|
);
|
|
1613
1654
|
break;
|
|
1614
1655
|
default:
|
|
@@ -1626,7 +1667,7 @@ class Entity {
|
|
|
1626
1667
|
);
|
|
1627
1668
|
delete keyExpressions.ExpressionAttributeNames["#sk2"];
|
|
1628
1669
|
let params = {
|
|
1629
|
-
TableName: this.
|
|
1670
|
+
TableName: this.getTableName(),
|
|
1630
1671
|
ExpressionAttributeNames: this._mergeExpressionsAttributes(
|
|
1631
1672
|
filter.getNames(),
|
|
1632
1673
|
keyExpressions.ExpressionAttributeNames,
|
|
@@ -1669,7 +1710,7 @@ class Entity {
|
|
|
1669
1710
|
|
|
1670
1711
|
let params = {
|
|
1671
1712
|
KeyConditionExpression,
|
|
1672
|
-
TableName: this.
|
|
1713
|
+
TableName: this.getTableName(),
|
|
1673
1714
|
ExpressionAttributeNames: this._mergeExpressionsAttributes(filter.getNames(), keyExpressions.ExpressionAttributeNames, customExpressions.names),
|
|
1674
1715
|
ExpressionAttributeValues: this._mergeExpressionsAttributes(filter.getValues(), keyExpressions.ExpressionAttributeValues, customExpressions.values),
|
|
1675
1716
|
};
|
|
@@ -1727,8 +1768,14 @@ class Entity {
|
|
|
1727
1768
|
}
|
|
1728
1769
|
|
|
1729
1770
|
/* istanbul ignore next */
|
|
1730
|
-
_makeComparisonQueryParams(index = TableIndex, comparison = "", filter = {},
|
|
1731
|
-
|
|
1771
|
+
_makeComparisonQueryParams(index = TableIndex, comparison = "", filter = {}, indexKeys = {}) {
|
|
1772
|
+
const {pk, fulfilled} = indexKeys;
|
|
1773
|
+
const sk = indexKeys.sk[0];
|
|
1774
|
+
let operator = PartialComparisons[comparison];
|
|
1775
|
+
// fulfilled
|
|
1776
|
+
// ? Comparisons[comparison]
|
|
1777
|
+
// : PartialComparisons[comparison];
|
|
1778
|
+
|
|
1732
1779
|
if (!operator) {
|
|
1733
1780
|
throw new Error(`Unexpected comparison operator "${comparison}", expected ${u.commaSeparatedString(Object.values(Comparisons))}`);
|
|
1734
1781
|
}
|
|
@@ -1738,7 +1785,7 @@ class Entity {
|
|
|
1738
1785
|
sk,
|
|
1739
1786
|
);
|
|
1740
1787
|
let params = {
|
|
1741
|
-
TableName: this.
|
|
1788
|
+
TableName: this.getTableName(),
|
|
1742
1789
|
ExpressionAttributeNames: this._mergeExpressionsAttributes(
|
|
1743
1790
|
filter.getNames(),
|
|
1744
1791
|
keyExpressions.ExpressionAttributeNames,
|
|
@@ -1776,7 +1823,11 @@ class Entity {
|
|
|
1776
1823
|
_makeKeysFromAttributes(indexes, attributes) {
|
|
1777
1824
|
let indexKeys = {};
|
|
1778
1825
|
for (let [index, keyTypes] of Object.entries(indexes)) {
|
|
1779
|
-
let keys = this._makeIndexKeys(
|
|
1826
|
+
let keys = this._makeIndexKeys({
|
|
1827
|
+
index,
|
|
1828
|
+
pkAttributes: attributes,
|
|
1829
|
+
skAttributes: [attributes],
|
|
1830
|
+
});
|
|
1780
1831
|
if (keyTypes.pk || keyTypes.sk) {
|
|
1781
1832
|
indexKeys[index] = {};
|
|
1782
1833
|
}
|
|
@@ -1798,7 +1849,11 @@ class Entity {
|
|
|
1798
1849
|
_makePutKeysFromAttributes(indexes, attributes) {
|
|
1799
1850
|
let indexKeys = {};
|
|
1800
1851
|
for (let index of indexes) {
|
|
1801
|
-
indexKeys[index] = this._makeIndexKeys(
|
|
1852
|
+
indexKeys[index] = this._makeIndexKeys({
|
|
1853
|
+
index,
|
|
1854
|
+
pkAttributes: attributes,
|
|
1855
|
+
skAttributes: [attributes],
|
|
1856
|
+
});
|
|
1802
1857
|
}
|
|
1803
1858
|
return indexKeys;
|
|
1804
1859
|
}
|
|
@@ -2009,7 +2064,14 @@ class Entity {
|
|
|
2009
2064
|
return [!!missing.length, missing, matching];
|
|
2010
2065
|
}
|
|
2011
2066
|
|
|
2012
|
-
|
|
2067
|
+
_makeKeyFixings({
|
|
2068
|
+
service,
|
|
2069
|
+
entity,
|
|
2070
|
+
version = "1",
|
|
2071
|
+
tableIndex,
|
|
2072
|
+
modelVersion,
|
|
2073
|
+
isClustered
|
|
2074
|
+
}) {
|
|
2013
2075
|
/*
|
|
2014
2076
|
Collections will prefix the sort key so they can be queried with
|
|
2015
2077
|
a "begins_with" operator when crossing entities. It is also possible
|
|
@@ -2035,34 +2097,47 @@ class Entity {
|
|
|
2035
2097
|
|
|
2036
2098
|
let pk = `$${service}`;
|
|
2037
2099
|
let sk = "";
|
|
2038
|
-
|
|
2100
|
+
let entityKeys = "";
|
|
2101
|
+
let postfix = "";
|
|
2039
2102
|
// If the index is in a collections, prepend the sk;
|
|
2040
2103
|
let collectionPrefix = this._makeCollectionPrefix(tableIndex.collection);
|
|
2041
2104
|
if (validations.isStringHasLength(collectionPrefix)) {
|
|
2042
|
-
sk = `${collectionPrefix}
|
|
2105
|
+
sk = `${collectionPrefix}`;
|
|
2106
|
+
entityKeys += `#${entity}`;
|
|
2043
2107
|
} else {
|
|
2044
|
-
|
|
2108
|
+
entityKeys += `$${entity}`;
|
|
2045
2109
|
}
|
|
2046
2110
|
|
|
2047
2111
|
/** start beta/v1 condition **/
|
|
2048
2112
|
if (modelVersion === ModelVersions.beta) {
|
|
2049
2113
|
pk = `${pk}_${version}`;
|
|
2050
2114
|
} else {
|
|
2051
|
-
|
|
2115
|
+
entityKeys = `${entityKeys}_${version}`;
|
|
2052
2116
|
}
|
|
2053
2117
|
/** end beta/v1 condition **/
|
|
2054
2118
|
|
|
2119
|
+
if (isClustered) {
|
|
2120
|
+
postfix = entityKeys;
|
|
2121
|
+
} else {
|
|
2122
|
+
sk = `${sk}${entityKeys}`
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2055
2125
|
// If no sk, append the sk properties to the pk
|
|
2056
2126
|
if (Object.keys(tableIndex.sk).length === 0) {
|
|
2057
2127
|
pk += sk;
|
|
2128
|
+
if (isClustered) {
|
|
2129
|
+
pk += postfix;
|
|
2130
|
+
}
|
|
2058
2131
|
}
|
|
2059
2132
|
|
|
2060
2133
|
// If keys arent custom, set the prefixes
|
|
2061
2134
|
if (!keys.pk.isCustom) {
|
|
2062
2135
|
keys.pk.prefix = u.formatKeyCasing(pk, tableIndex.pk.casing);
|
|
2063
2136
|
}
|
|
2137
|
+
|
|
2064
2138
|
if (!keys.sk.isCustom) {
|
|
2065
2139
|
keys.sk.prefix = u.formatKeyCasing(sk, tableIndex.sk.casing);
|
|
2140
|
+
keys.sk.postfix = u.formatKeyCasing(postfix, tableIndex.sk.casing);
|
|
2066
2141
|
}
|
|
2067
2142
|
|
|
2068
2143
|
return keys;
|
|
@@ -2107,9 +2182,32 @@ class Entity {
|
|
|
2107
2182
|
return prefix;
|
|
2108
2183
|
}
|
|
2109
2184
|
|
|
2185
|
+
_makeKeyTransforms(queryType) {
|
|
2186
|
+
const transforms = [];
|
|
2187
|
+
const shiftUp = (val) => u.shiftSortOrder(val, 1);
|
|
2188
|
+
const noop = (val) => val;
|
|
2189
|
+
switch (queryType) {
|
|
2190
|
+
case QueryTypes.between:
|
|
2191
|
+
transforms.push(noop, shiftUp);
|
|
2192
|
+
break;
|
|
2193
|
+
case QueryTypes.lte:
|
|
2194
|
+
case QueryTypes.gt:
|
|
2195
|
+
transforms.push(shiftUp);
|
|
2196
|
+
break;
|
|
2197
|
+
default:
|
|
2198
|
+
transforms.push(noop);
|
|
2199
|
+
break;
|
|
2200
|
+
}
|
|
2201
|
+
return transforms;
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2110
2204
|
/* istanbul ignore next */
|
|
2111
|
-
_makeIndexKeysWithoutTail(
|
|
2205
|
+
_makeIndexKeysWithoutTail(state = {}, skFacets = []) {
|
|
2206
|
+
const index = state.query.index || TableIndex;
|
|
2112
2207
|
this._validateIndex(index);
|
|
2208
|
+
const pkFacets = state.query.keys.pk || {};
|
|
2209
|
+
const excludePostfix = state.query.options.indexType === IndexTypes.clustered && state.query.options._isCollectionQuery;
|
|
2210
|
+
const transforms = this._makeKeyTransforms(state.query.type);
|
|
2113
2211
|
if (!skFacets.length) {
|
|
2114
2212
|
skFacets.push({});
|
|
2115
2213
|
}
|
|
@@ -2118,49 +2216,84 @@ class Entity {
|
|
|
2118
2216
|
if (!prefixes) {
|
|
2119
2217
|
throw new Error(`Invalid index: ${index}`);
|
|
2120
2218
|
}
|
|
2121
|
-
let
|
|
2219
|
+
let partitionKey = this._makeKey(prefixes.pk, facets.pk, pkFacets, this.model.facets.labels[index].pk, {excludeLabelTail: true});
|
|
2220
|
+
let pk = partitionKey.key;
|
|
2122
2221
|
let sk = [];
|
|
2222
|
+
let fulfilled = false;
|
|
2123
2223
|
if (this.model.lookup.indexHasSortKeys[index]) {
|
|
2124
|
-
for (let
|
|
2224
|
+
for (let i = 0; i < skFacets.length; i++) {
|
|
2225
|
+
const skFacet = skFacets[i];
|
|
2226
|
+
const transform = transforms[i];
|
|
2125
2227
|
let hasLabels = this.model.facets.labels[index] && Array.isArray(this.model.facets.labels[index].sk);
|
|
2126
2228
|
let labels = hasLabels
|
|
2127
2229
|
? this.model.facets.labels[index].sk
|
|
2128
|
-
: []
|
|
2129
|
-
let sortKey = this._makeKey(prefixes.sk, facets.sk, skFacet, labels, {
|
|
2130
|
-
|
|
2131
|
-
|
|
2230
|
+
: [];
|
|
2231
|
+
let sortKey = this._makeKey(prefixes.sk, facets.sk, skFacet, labels, {
|
|
2232
|
+
excludeLabelTail: true,
|
|
2233
|
+
excludePostfix,
|
|
2234
|
+
transform,
|
|
2235
|
+
});
|
|
2236
|
+
if (sortKey.key !== undefined) {
|
|
2237
|
+
sk.push(sortKey.key);
|
|
2238
|
+
}
|
|
2239
|
+
if (sortKey.fulfilled) {
|
|
2240
|
+
fulfilled = true;
|
|
2132
2241
|
}
|
|
2133
2242
|
}
|
|
2134
2243
|
}
|
|
2135
|
-
return {
|
|
2244
|
+
return {
|
|
2245
|
+
pk,
|
|
2246
|
+
sk,
|
|
2247
|
+
fulfilled,
|
|
2248
|
+
};
|
|
2136
2249
|
}
|
|
2137
2250
|
|
|
2138
2251
|
/* istanbul ignore next */
|
|
2139
|
-
_makeIndexKeys(
|
|
2252
|
+
_makeIndexKeys({
|
|
2253
|
+
index = TableIndex,
|
|
2254
|
+
pkAttributes = {},
|
|
2255
|
+
skAttributes = [],
|
|
2256
|
+
queryType,
|
|
2257
|
+
indexType,
|
|
2258
|
+
isCollection = false,
|
|
2259
|
+
}) {
|
|
2260
|
+
|
|
2140
2261
|
this._validateIndex(index);
|
|
2141
|
-
|
|
2142
|
-
|
|
2262
|
+
const excludePostfix = indexType === IndexTypes.clustered && isCollection;
|
|
2263
|
+
const transforms = this._makeKeyTransforms(queryType);
|
|
2264
|
+
if (!skAttributes.length) {
|
|
2265
|
+
skAttributes.push({});
|
|
2143
2266
|
}
|
|
2144
2267
|
let facets = this.model.facets.byIndex[index];
|
|
2145
2268
|
let prefixes = this.model.prefixes[index];
|
|
2146
2269
|
if (!prefixes) {
|
|
2147
2270
|
throw new Error(`Invalid index: ${index}`);
|
|
2148
2271
|
}
|
|
2149
|
-
let pk = this._makeKey(prefixes.pk, facets.pk,
|
|
2272
|
+
let pk = this._makeKey(prefixes.pk, facets.pk, pkAttributes, this.model.facets.labels[index].pk);
|
|
2150
2273
|
let sk = [];
|
|
2274
|
+
let fulfilled = false;
|
|
2151
2275
|
if (this.model.lookup.indexHasSortKeys[index]) {
|
|
2152
|
-
for (let
|
|
2276
|
+
for (let i = 0; i < skAttributes.length; i++) {
|
|
2277
|
+
const skFacet = skAttributes[i];
|
|
2278
|
+
const transform = transforms[i];
|
|
2153
2279
|
let hasLabels = this.model.facets.labels[index] && Array.isArray(this.model.facets.labels[index].sk);
|
|
2154
2280
|
let labels = hasLabels
|
|
2155
2281
|
? this.model.facets.labels[index].sk
|
|
2156
2282
|
: []
|
|
2157
|
-
let sortKey = this._makeKey(prefixes.sk, facets.sk, skFacet, labels);
|
|
2158
|
-
if (sortKey !== undefined) {
|
|
2159
|
-
sk.push(sortKey);
|
|
2283
|
+
let sortKey = this._makeKey(prefixes.sk, facets.sk, skFacet, labels, {excludePostfix, transform});
|
|
2284
|
+
if (sortKey.key !== undefined) {
|
|
2285
|
+
sk.push(sortKey.key);
|
|
2286
|
+
}
|
|
2287
|
+
if (sortKey.fulfilled) {
|
|
2288
|
+
fulfilled = true;
|
|
2160
2289
|
}
|
|
2161
2290
|
}
|
|
2162
2291
|
}
|
|
2163
|
-
return {
|
|
2292
|
+
return {
|
|
2293
|
+
pk: pk.key,
|
|
2294
|
+
sk,
|
|
2295
|
+
fulfilled
|
|
2296
|
+
};
|
|
2164
2297
|
}
|
|
2165
2298
|
|
|
2166
2299
|
_isNumericKey(isCustom, facets = [], labels = []) {
|
|
@@ -2172,11 +2305,15 @@ class Entity {
|
|
|
2172
2305
|
}
|
|
2173
2306
|
|
|
2174
2307
|
/* istanbul ignore next */
|
|
2175
|
-
_makeKey({prefix, isCustom, casing} = {}, facets = [], supplied = {}, labels = [], {excludeLabelTail} = {}) {
|
|
2308
|
+
_makeKey({prefix, isCustom, casing, postfix} = {}, facets = [], supplied = {}, labels = [], {excludeLabelTail, excludePostfix, transform = (val) => val} = {}) {
|
|
2176
2309
|
if (this._isNumericKey(isCustom, facets, labels)) {
|
|
2177
|
-
return
|
|
2310
|
+
return {
|
|
2311
|
+
fulfilled: supplied[facets[0]] !== undefined,
|
|
2312
|
+
key: supplied[facets[0]],
|
|
2313
|
+
};
|
|
2178
2314
|
}
|
|
2179
2315
|
let key = prefix;
|
|
2316
|
+
let foundCount = 0;
|
|
2180
2317
|
for (let i = 0; i < labels.length; i++) {
|
|
2181
2318
|
const { name, label } = labels[i];
|
|
2182
2319
|
const attribute = this.model.schema.getAttribute(name);
|
|
@@ -2198,11 +2335,24 @@ class Entity {
|
|
|
2198
2335
|
if (supplied[name] === undefined) {
|
|
2199
2336
|
break;
|
|
2200
2337
|
}
|
|
2201
|
-
|
|
2338
|
+
foundCount++;
|
|
2202
2339
|
key = `${key}${value}`;
|
|
2203
2340
|
}
|
|
2204
2341
|
|
|
2205
|
-
|
|
2342
|
+
|
|
2343
|
+
|
|
2344
|
+
// when sort keys are fulfilled we need to add the entity postfix
|
|
2345
|
+
// this is used for cluster indexes
|
|
2346
|
+
const fulfilled = foundCount === labels.length;
|
|
2347
|
+
const shouldApplyPostfix = typeof postfix === 'string' && !excludePostfix;
|
|
2348
|
+
if (fulfilled && shouldApplyPostfix) {
|
|
2349
|
+
key += postfix;
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
return {
|
|
2353
|
+
fulfilled,
|
|
2354
|
+
key: transform(u.formatKeyCasing(key, casing))
|
|
2355
|
+
};
|
|
2206
2356
|
}
|
|
2207
2357
|
|
|
2208
2358
|
_findBestIndexKeyMatch(attributes = {}) {
|
|
@@ -2450,6 +2600,7 @@ class Entity {
|
|
|
2450
2600
|
let indexFieldTranslation = {};
|
|
2451
2601
|
let indexHasSortKeys = {};
|
|
2452
2602
|
let indexHasSubCollections = {};
|
|
2603
|
+
let clusteredIndexes = new Set();
|
|
2453
2604
|
let indexAccessPatternTransaction = {
|
|
2454
2605
|
fromAccessPatternToIndex: {},
|
|
2455
2606
|
fromIndexToAccessPattern: {},
|
|
@@ -2480,6 +2631,10 @@ class Entity {
|
|
|
2480
2631
|
let accessPattern = accessPatterns[i];
|
|
2481
2632
|
let index = indexes[accessPattern];
|
|
2482
2633
|
let indexName = index.index || TableIndex;
|
|
2634
|
+
let indexType = typeof index.type === 'string' ? index.type : IndexTypes.isolated;
|
|
2635
|
+
if (indexType === 'clustered') {
|
|
2636
|
+
clusteredIndexes.add(accessPattern);
|
|
2637
|
+
}
|
|
2483
2638
|
if (seenIndexes[indexName] !== undefined) {
|
|
2484
2639
|
if (indexName === TableIndex) {
|
|
2485
2640
|
throw new e.ElectroError(e.ErrorCodes.DuplicateIndexes, `Duplicate index defined in model found in Access Pattern '${accessPattern}': '${u.formatIndexNameForDisplay(indexName)}'. This could be because you forgot to specify the index name of a secondary index defined in your model.`);
|
|
@@ -2554,8 +2709,10 @@ class Entity {
|
|
|
2554
2709
|
pk,
|
|
2555
2710
|
sk,
|
|
2556
2711
|
collection,
|
|
2712
|
+
hasSk,
|
|
2557
2713
|
customFacets,
|
|
2558
2714
|
index: indexName,
|
|
2715
|
+
type: indexType,
|
|
2559
2716
|
};
|
|
2560
2717
|
|
|
2561
2718
|
indexHasSubCollections[indexName] = inCollection && Array.isArray(collection);
|
|
@@ -2699,6 +2856,7 @@ class Entity {
|
|
|
2699
2856
|
facets,
|
|
2700
2857
|
subCollections,
|
|
2701
2858
|
indexHasSortKeys,
|
|
2859
|
+
clusteredIndexes,
|
|
2702
2860
|
indexHasSubCollections,
|
|
2703
2861
|
indexes: normalized,
|
|
2704
2862
|
indexField: indexFieldTranslation,
|
|
@@ -2723,11 +2881,18 @@ class Entity {
|
|
|
2723
2881
|
return normalized;
|
|
2724
2882
|
}
|
|
2725
2883
|
|
|
2726
|
-
|
|
2884
|
+
_normalizeKeyFixings({service, entity, version, indexes, modelVersion, clusteredIndexes}) {
|
|
2727
2885
|
let prefixes = {};
|
|
2728
2886
|
for (let accessPattern of Object.keys(indexes)) {
|
|
2729
|
-
let
|
|
2730
|
-
prefixes[
|
|
2887
|
+
let tableIndex = indexes[accessPattern];
|
|
2888
|
+
prefixes[tableIndex.index] = this._makeKeyFixings({
|
|
2889
|
+
service,
|
|
2890
|
+
entity,
|
|
2891
|
+
version,
|
|
2892
|
+
tableIndex,
|
|
2893
|
+
modelVersion,
|
|
2894
|
+
isClustered: clusteredIndexes.has(accessPattern),
|
|
2895
|
+
});
|
|
2731
2896
|
}
|
|
2732
2897
|
return prefixes;
|
|
2733
2898
|
}
|
|
@@ -2867,13 +3032,15 @@ class Entity {
|
|
|
2867
3032
|
collections,
|
|
2868
3033
|
subCollections,
|
|
2869
3034
|
indexCollection,
|
|
3035
|
+
clusteredIndexes,
|
|
2870
3036
|
indexHasSortKeys,
|
|
2871
3037
|
indexAccessPattern,
|
|
2872
3038
|
indexHasSubCollections,
|
|
2873
3039
|
} = this._normalizeIndexes(model.indexes);
|
|
2874
|
-
let schema = new Schema(model.attributes, facets, {client});
|
|
3040
|
+
let schema = new Schema(model.attributes, facets, {client, isRoot: true});
|
|
2875
3041
|
let filters = this._normalizeFilters(model.filters);
|
|
2876
|
-
|
|
3042
|
+
// todo: consider a rename
|
|
3043
|
+
let prefixes = this._normalizeKeyFixings({service, entity, version, indexes, modelVersion, clusteredIndexes});
|
|
2877
3044
|
|
|
2878
3045
|
// apply model defined labels
|
|
2879
3046
|
let schemaDefinedLabels = schema.getLabels();
|
|
@@ -2898,6 +3065,7 @@ class Entity {
|
|
|
2898
3065
|
modelVersion,
|
|
2899
3066
|
subCollections,
|
|
2900
3067
|
lookup: {
|
|
3068
|
+
clusteredIndexes,
|
|
2901
3069
|
indexHasSortKeys,
|
|
2902
3070
|
indexHasSubCollections
|
|
2903
3071
|
},
|