@exabugs/dynamodb-client 1.4.8 → 1.4.10
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/dist/server/handler.cjs +138 -39
- package/dist/server/handler.cjs.map +3 -3
- package/dist/server/operations/find/shadowQuery.d.ts.map +1 -1
- package/dist/server/operations/find/shadowQuery.js +114 -44
- package/dist/server/operations/find/shadowQuery.js.map +1 -1
- package/dist/server/utils/pagination.d.ts +9 -0
- package/dist/server/utils/pagination.d.ts.map +1 -1
- package/dist/server/utils/pagination.js +28 -0
- package/dist/server/utils/pagination.js.map +1 -1
- package/package.json +1 -1
package/dist/server/handler.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// @exabugs/dynamodb-client v1.4.
|
|
2
|
-
// Built: 2026-04-
|
|
1
|
+
// @exabugs/dynamodb-client v1.4.10
|
|
2
|
+
// Built: 2026-04-05T13:26:07.424Z
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
@@ -30324,6 +30324,25 @@ function encodeNextToken(pk, sk) {
|
|
|
30324
30324
|
const base64url = base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
30325
30325
|
return base64url;
|
|
30326
30326
|
}
|
|
30327
|
+
function encodeOffsetToken(offset) {
|
|
30328
|
+
const payload2 = { offset };
|
|
30329
|
+
const json = JSON.stringify(payload2);
|
|
30330
|
+
const base64 = Buffer.from(json, "utf-8").toString("base64");
|
|
30331
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
30332
|
+
}
|
|
30333
|
+
function decodeOffsetToken(token) {
|
|
30334
|
+
try {
|
|
30335
|
+
let base64 = token.replace(/-/g, "+").replace(/_/g, "/");
|
|
30336
|
+
while (base64.length % 4 !== 0) base64 += "=";
|
|
30337
|
+
const payload2 = JSON.parse(Buffer.from(base64, "base64").toString("utf-8"));
|
|
30338
|
+
if (typeof payload2.offset === "number" && !payload2.PK) {
|
|
30339
|
+
return payload2.offset;
|
|
30340
|
+
}
|
|
30341
|
+
return null;
|
|
30342
|
+
} catch {
|
|
30343
|
+
return null;
|
|
30344
|
+
}
|
|
30345
|
+
}
|
|
30327
30346
|
function decodeNextToken(token) {
|
|
30328
30347
|
try {
|
|
30329
30348
|
let base64 = token.replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -30349,6 +30368,8 @@ var init_pagination = __esm({
|
|
|
30349
30368
|
init_index2();
|
|
30350
30369
|
init_validation3();
|
|
30351
30370
|
__name(encodeNextToken, "encodeNextToken");
|
|
30371
|
+
__name(encodeOffsetToken, "encodeOffsetToken");
|
|
30372
|
+
__name(decodeOffsetToken, "decodeOffsetToken");
|
|
30352
30373
|
__name(decodeNextToken, "decodeNextToken");
|
|
30353
30374
|
}
|
|
30354
30375
|
});
|
|
@@ -30819,20 +30840,26 @@ async function executeShadowQuery(resource, normalizedParams, requestId) {
|
|
|
30819
30840
|
const costTracker = new CostTracker();
|
|
30820
30841
|
const filterFirstCandidate = selectFilterFirstCandidate(parsedFilters, sort.field, normalizedParams.schema, perPage);
|
|
30821
30842
|
const isFilterFirst = filterFirstCandidate !== void 0;
|
|
30822
|
-
|
|
30823
|
-
|
|
30824
|
-
|
|
30843
|
+
if (isFilterFirst) {
|
|
30844
|
+
return executeFilterFirstQuery(
|
|
30845
|
+
resource,
|
|
30846
|
+
normalizedParams,
|
|
30847
|
+
filterFirstCandidate,
|
|
30848
|
+
costTracker,
|
|
30849
|
+
requestId
|
|
30850
|
+
);
|
|
30851
|
+
}
|
|
30852
|
+
const optimizableFilter = findOptimizableFilter(sort.field, parsedFilters);
|
|
30825
30853
|
logger8.debug("Executing shadow query", {
|
|
30826
30854
|
requestId,
|
|
30827
30855
|
resource,
|
|
30828
30856
|
sortField: sort.field,
|
|
30829
|
-
isFilterFirst,
|
|
30830
|
-
queryField: querySort.field,
|
|
30857
|
+
isFilterFirst: false,
|
|
30831
30858
|
hasFilters: parsedFilters.length > 0
|
|
30832
30859
|
});
|
|
30833
30860
|
const shadowRecords = await executeShadowRecordQuery(
|
|
30834
30861
|
resource,
|
|
30835
|
-
|
|
30862
|
+
sort,
|
|
30836
30863
|
perPage,
|
|
30837
30864
|
nextToken,
|
|
30838
30865
|
optimizableFilter,
|
|
@@ -30863,11 +30890,8 @@ async function executeShadowQuery(resource, normalizedParams, requestId) {
|
|
|
30863
30890
|
seenIds.add(id);
|
|
30864
30891
|
return true;
|
|
30865
30892
|
}).map((id) => recordMap.get(id)).filter((record) => record !== void 0);
|
|
30866
|
-
if (
|
|
30867
|
-
items = items.filter((record) => matchesAllFilters(record,
|
|
30868
|
-
}
|
|
30869
|
-
if (isFilterFirst) {
|
|
30870
|
-
items = sortInMemory(items, sort);
|
|
30893
|
+
if (parsedFilters.length > 0) {
|
|
30894
|
+
items = items.filter((record) => matchesAllFilters(record, parsedFilters));
|
|
30871
30895
|
}
|
|
30872
30896
|
const hasNextPage = (shadowRecords.Items?.length || 0) < perPage ? false : shadowRecords.LastEvaluatedKey !== void 0;
|
|
30873
30897
|
const nextTokenValue = hasNextPage && shadowRecords.LastEvaluatedKey ? encodeNextToken(
|
|
@@ -30878,7 +30902,7 @@ async function executeShadowQuery(resource, normalizedParams, requestId) {
|
|
|
30878
30902
|
requestId,
|
|
30879
30903
|
resource,
|
|
30880
30904
|
sortField: sort.field,
|
|
30881
|
-
isFilterFirst,
|
|
30905
|
+
isFilterFirst: false,
|
|
30882
30906
|
shadowCount: shadowRecords.Items?.length || 0,
|
|
30883
30907
|
mainCount: items.length,
|
|
30884
30908
|
hasNextPage
|
|
@@ -30893,6 +30917,75 @@ async function executeShadowQuery(resource, normalizedParams, requestId) {
|
|
|
30893
30917
|
consumedCapacity: costTracker.getAggregated()
|
|
30894
30918
|
};
|
|
30895
30919
|
}
|
|
30920
|
+
async function executeFilterFirstQuery(resource, normalizedParams, filterCandidate, costTracker, requestId) {
|
|
30921
|
+
const { sort, pagination, parsedFilters } = normalizedParams;
|
|
30922
|
+
const { perPage, nextToken } = pagination;
|
|
30923
|
+
const offset = nextToken ? decodeOffsetToken(nextToken) ?? 0 : 0;
|
|
30924
|
+
const filterSort = { field: filterCandidate.parsed.field, order: "ASC" };
|
|
30925
|
+
const remainingFilters = parsedFilters.filter((f4) => f4 !== filterCandidate);
|
|
30926
|
+
logger8.debug("Executing filter-first query", {
|
|
30927
|
+
requestId,
|
|
30928
|
+
resource,
|
|
30929
|
+
filterField: filterCandidate.parsed.field,
|
|
30930
|
+
sortField: sort.field,
|
|
30931
|
+
offset
|
|
30932
|
+
});
|
|
30933
|
+
const allShadowItems = [];
|
|
30934
|
+
let lastKey;
|
|
30935
|
+
do {
|
|
30936
|
+
const result = await executeShadowRecordQuery(
|
|
30937
|
+
resource,
|
|
30938
|
+
filterSort,
|
|
30939
|
+
1e3,
|
|
30940
|
+
// 大きめの Limit でループ回数を最小化
|
|
30941
|
+
lastKey ? encodeNextToken(lastKey.PK, lastKey.SK) : void 0,
|
|
30942
|
+
filterCandidate,
|
|
30943
|
+
costTracker,
|
|
30944
|
+
requestId
|
|
30945
|
+
);
|
|
30946
|
+
allShadowItems.push(...result.Items || []);
|
|
30947
|
+
lastKey = result.LastEvaluatedKey;
|
|
30948
|
+
} while (lastKey);
|
|
30949
|
+
const allIds = Array.from(new Set(extractRecordIds(allShadowItems)));
|
|
30950
|
+
if (allIds.length === 0) {
|
|
30951
|
+
return {
|
|
30952
|
+
items: [],
|
|
30953
|
+
pageInfo: { hasNextPage: false, hasPreviousPage: offset > 0 },
|
|
30954
|
+
consumedCapacity: costTracker.getAggregated()
|
|
30955
|
+
};
|
|
30956
|
+
}
|
|
30957
|
+
const mainRecords = await fetchMainRecords(resource, allIds, costTracker, requestId);
|
|
30958
|
+
const recordMap = new Map(
|
|
30959
|
+
mainRecords.map((item) => {
|
|
30960
|
+
const data2 = item.data;
|
|
30961
|
+
return [data2.id, extractCleanRecord(item)];
|
|
30962
|
+
})
|
|
30963
|
+
);
|
|
30964
|
+
let items = allIds.map((id) => recordMap.get(id)).filter((r4) => r4 !== void 0);
|
|
30965
|
+
if (remainingFilters.length > 0) {
|
|
30966
|
+
items = items.filter((r4) => matchesAllFilters(r4, remainingFilters));
|
|
30967
|
+
}
|
|
30968
|
+
items = sortInMemory(items, sort);
|
|
30969
|
+
const page = items.slice(offset, offset + perPage);
|
|
30970
|
+
const hasNextPage = offset + perPage < items.length;
|
|
30971
|
+
const nextTokenValue = hasNextPage ? encodeOffsetToken(offset + perPage) : void 0;
|
|
30972
|
+
logger8.info("Filter-first query succeeded", {
|
|
30973
|
+
requestId,
|
|
30974
|
+
resource,
|
|
30975
|
+
filterField: filterCandidate.parsed.field,
|
|
30976
|
+
sortField: sort.field,
|
|
30977
|
+
totalMatched: items.length,
|
|
30978
|
+
offset,
|
|
30979
|
+
returned: page.length,
|
|
30980
|
+
hasNextPage
|
|
30981
|
+
});
|
|
30982
|
+
return {
|
|
30983
|
+
items: page,
|
|
30984
|
+
pageInfo: { hasNextPage, hasPreviousPage: offset > 0 },
|
|
30985
|
+
...nextTokenValue && { nextToken: nextTokenValue },
|
|
30986
|
+
consumedCapacity: costTracker.getAggregated()
|
|
30987
|
+
};
|
|
30988
|
+
}
|
|
30896
30989
|
function selectFilterFirstCandidate(parsedFilters, sortField, schema, perPage) {
|
|
30897
30990
|
const eqFilters = parsedFilters.filter(
|
|
30898
30991
|
(f4) => f4.parsed.operator === "$eq" && f4.parsed.field !== sortField
|
|
@@ -30900,7 +30993,7 @@ function selectFilterFirstCandidate(parsedFilters, sortField, schema, perPage) {
|
|
|
30900
30993
|
if (eqFilters.length === 0) return void 0;
|
|
30901
30994
|
const cardinality = schema?.cardinality;
|
|
30902
30995
|
if (!cardinality) {
|
|
30903
|
-
return
|
|
30996
|
+
return void 0;
|
|
30904
30997
|
}
|
|
30905
30998
|
const threshold = perPage / 2;
|
|
30906
30999
|
const scored = eqFilters.map((f4) => ({ f: f4, score: cardinality[f4.parsed.field] ?? 0 })).filter(({ score }) => score > threshold).sort((a4, b4) => b4.score - a4.score);
|
|
@@ -31053,36 +31146,41 @@ async function fetchMainRecords(resource, recordIds, costTracker, requestId) {
|
|
|
31053
31146
|
const dbClient2 = getDBClient();
|
|
31054
31147
|
const tableName = getTableName();
|
|
31055
31148
|
const uniqueRecordIds = Array.from(new Set(recordIds));
|
|
31056
|
-
const
|
|
31057
|
-
|
|
31058
|
-
|
|
31059
|
-
|
|
31060
|
-
|
|
31061
|
-
|
|
31062
|
-
|
|
31063
|
-
|
|
31064
|
-
|
|
31065
|
-
|
|
31066
|
-
|
|
31067
|
-
|
|
31068
|
-
|
|
31069
|
-
|
|
31070
|
-
|
|
31071
|
-
|
|
31072
|
-
|
|
31073
|
-
|
|
31074
|
-
|
|
31075
|
-
|
|
31149
|
+
const BATCH_SIZE = 100;
|
|
31150
|
+
const allRecords = [];
|
|
31151
|
+
for (let i4 = 0; i4 < uniqueRecordIds.length; i4 += BATCH_SIZE) {
|
|
31152
|
+
const chunk = uniqueRecordIds.slice(i4, i4 + BATCH_SIZE);
|
|
31153
|
+
const batchGetResult = await executeDynamoDBOperation(
|
|
31154
|
+
() => dbClient2.send(
|
|
31155
|
+
new import_lib_dynamodb4.BatchGetCommand({
|
|
31156
|
+
RequestItems: {
|
|
31157
|
+
[tableName]: {
|
|
31158
|
+
Keys: chunk.map((id) => ({
|
|
31159
|
+
PK: resource,
|
|
31160
|
+
SK: `id#${id}`
|
|
31161
|
+
})),
|
|
31162
|
+
ConsistentRead: true
|
|
31163
|
+
}
|
|
31164
|
+
},
|
|
31165
|
+
ReturnConsumedCapacity: "TOTAL"
|
|
31166
|
+
})
|
|
31167
|
+
),
|
|
31168
|
+
"BatchGetItem"
|
|
31169
|
+
);
|
|
31170
|
+
if (batchGetResult.ConsumedCapacity) {
|
|
31171
|
+
for (const capacity of batchGetResult.ConsumedCapacity) {
|
|
31172
|
+
costTracker.add(capacity);
|
|
31173
|
+
}
|
|
31076
31174
|
}
|
|
31175
|
+
allRecords.push(...batchGetResult.Responses?.[tableName] || []);
|
|
31077
31176
|
}
|
|
31078
|
-
const mainRecords = batchGetResult.Responses?.[tableName] || [];
|
|
31079
31177
|
logger8.debug("Main records fetched", {
|
|
31080
31178
|
requestId,
|
|
31081
31179
|
resource,
|
|
31082
31180
|
requestedCount: uniqueRecordIds.length,
|
|
31083
|
-
fetchedCount:
|
|
31181
|
+
fetchedCount: allRecords.length
|
|
31084
31182
|
});
|
|
31085
|
-
return
|
|
31183
|
+
return allRecords;
|
|
31086
31184
|
}
|
|
31087
31185
|
var import_lib_dynamodb4, logger8;
|
|
31088
31186
|
var init_shadowQuery = __esm({
|
|
@@ -31100,6 +31198,7 @@ var init_shadowQuery = __esm({
|
|
|
31100
31198
|
level: process.env.LOG_LEVEL || "info"
|
|
31101
31199
|
});
|
|
31102
31200
|
__name(executeShadowQuery, "executeShadowQuery");
|
|
31201
|
+
__name(executeFilterFirstQuery, "executeFilterFirstQuery");
|
|
31103
31202
|
__name(selectFilterFirstCandidate, "selectFilterFirstCandidate");
|
|
31104
31203
|
__name(sortInMemory, "sortInMemory");
|
|
31105
31204
|
__name(executeShadowRecordQuery, "executeShadowRecordQuery");
|
|
@@ -34401,7 +34500,7 @@ async function handler(event) {
|
|
|
34401
34500
|
return createCorsResponse(HTTP_STATUS.OK);
|
|
34402
34501
|
}
|
|
34403
34502
|
if (event.requestContext.http.method === "GET" && event.requestContext.http.path === "/version") {
|
|
34404
|
-
const version = "1.4.
|
|
34503
|
+
const version = "1.4.10";
|
|
34405
34504
|
return createSuccessResponse({ version, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, requestId);
|
|
34406
34505
|
}
|
|
34407
34506
|
if (event.requestContext.http.method !== "POST") {
|