@exabugs/dynamodb-client 1.4.8 → 1.4.9

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.
@@ -1,5 +1,5 @@
1
- // @exabugs/dynamodb-client v1.4.8
2
- // Built: 2026-04-05T11:02:55.391Z
1
+ // @exabugs/dynamodb-client v1.4.9
2
+ // Built: 2026-04-05T13:06:32.256Z
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
- const querySort = isFilterFirst ? { field: filterFirstCandidate.parsed.field, order: "ASC" } : sort;
30823
- const optimizableFilter = isFilterFirst ? filterFirstCandidate : findOptimizableFilter(sort.field, parsedFilters);
30824
- const remainingFilters = isFilterFirst ? parsedFilters.filter((f4) => f4 !== filterFirstCandidate) : parsedFilters;
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
- querySort,
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 (remainingFilters.length > 0) {
30867
- items = items.filter((record) => matchesAllFilters(record, remainingFilters));
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 eqFilters[0];
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);
@@ -31100,6 +31193,7 @@ var init_shadowQuery = __esm({
31100
31193
  level: process.env.LOG_LEVEL || "info"
31101
31194
  });
31102
31195
  __name(executeShadowQuery, "executeShadowQuery");
31196
+ __name(executeFilterFirstQuery, "executeFilterFirstQuery");
31103
31197
  __name(selectFilterFirstCandidate, "selectFilterFirstCandidate");
31104
31198
  __name(sortInMemory, "sortInMemory");
31105
31199
  __name(executeShadowRecordQuery, "executeShadowRecordQuery");
@@ -34401,7 +34495,7 @@ async function handler(event) {
34401
34495
  return createCorsResponse(HTTP_STATUS.OK);
34402
34496
  }
34403
34497
  if (event.requestContext.http.method === "GET" && event.requestContext.http.path === "/version") {
34404
- const version = "1.4.8";
34498
+ const version = "1.4.9";
34405
34499
  return createSuccessResponse({ version, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, requestId);
34406
34500
  }
34407
34501
  if (event.requestContext.http.method !== "POST") {