@exabugs/dynamodb-client 1.27.0 → 1.27.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/CHANGELOG.md +18 -0
- package/dist/__generated__/models/findResultPageInfo.d.ts +2 -0
- package/dist/__generated__/models/findResultPageInfo.d.ts.map +1 -1
- package/dist/server/handler.cjs +318 -307
- package/dist/server/handler.cjs.map +4 -4
- package/dist/server/media-handler.cjs +2 -2
- package/dist/server/media-process-handler-dlq.cjs +2 -2
- package/dist/server/media-process-handler.cjs +2 -2
- package/dist/server/operations/find/handler.d.ts.map +1 -1
- package/dist/server/operations/find/handler.js +4 -1
- package/dist/server/operations/find/handler.js.map +1 -1
- package/dist/server/operations/find/nearQuery.d.ts +4 -1
- package/dist/server/operations/find/nearQuery.d.ts.map +1 -1
- package/dist/server/operations/find/nearQuery.js +30 -42
- package/dist/server/operations/find/nearQuery.js.map +1 -1
- package/dist/server/operations/updateMany.d.ts.map +1 -1
- package/dist/server/operations/updateMany.js +17 -15
- package/dist/server/operations/updateMany.js.map +1 -1
- package/dist/server/query/nearSearch.d.ts +37 -25
- package/dist/server/query/nearSearch.d.ts.map +1 -1
- package/dist/server/query/nearSearch.js +87 -125
- package/dist/server/query/nearSearch.js.map +1 -1
- package/dist/server/utils/updateOperators.d.ts +12 -1
- package/dist/server/utils/updateOperators.d.ts.map +1 -1
- package/dist/server/utils/updateOperators.js +53 -6
- package/dist/server/utils/updateOperators.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,163 +1,123 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GeoHash近隣検索ロジック
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 周囲 9 マス(中心 + 隣接 8 方向)のシャドーだけを読み、位置の近似から、確定できる範囲で打ち切る。
|
|
5
|
+
* 本体レコードは、最後に、結果になりうる候補だけを 1 回読む。
|
|
6
|
+
*
|
|
7
|
+
* 保証できる半径 r(p, 緯度) = 精度 p のマスの短い辺。検索点から r 以内の点は、必ず周囲 9 マスの中にある
|
|
8
|
+
* (検索点は中央のマスのどこにでもあるので、9 マスの縁までの最短距離は、マス 1 つぶんの短い辺)。
|
|
5
9
|
*/
|
|
6
|
-
import { DEFAULT_GEOHASH_CONFIG, calculateDistance, encodeGeoHash, extractCoordinatesFromNearQuery, extractMaxDistanceFromNearQuery, extractMinDistanceFromNearQuery, getNeighborGeoHashes, } from '../../shared/geohash/index.js';
|
|
10
|
+
import { DEFAULT_GEOHASH_CONFIG, calculateDistance, decodeGeoHash, encodeGeoHash, extractCoordinatesFromNearQuery, extractMaxDistanceFromNearQuery, extractMinDistanceFromNearQuery, getNeighborGeoHashes, } from '../../shared/geohash/index.js';
|
|
11
|
+
/** 緯度 1 度の長さ(メートル)。経度 1 度の長さの上限(赤道)にも使う(実際は地球の半径から求めるより少し長い) */
|
|
12
|
+
const METERS_PER_DEGREE = 111320;
|
|
7
13
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
14
|
+
* 精度 p のマスの(緯度方向の高さ, 経度方向の幅)。メートル。経度は `cosLat` を掛ける
|
|
15
|
+
*
|
|
16
|
+
* geohash は経度から先に 1 ビットずつ割る。精度 p は 5p ビットで、経度が ceil(5p/2)、緯度が floor(5p/2)
|
|
10
17
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
18
|
+
function cellSizeMeters(precision, cosLat) {
|
|
19
|
+
const latBits = Math.floor((5 * precision) / 2);
|
|
20
|
+
const lonBits = Math.ceil((5 * precision) / 2);
|
|
21
|
+
const heightDeg = 180 / 2 ** latBits;
|
|
22
|
+
return {
|
|
23
|
+
height: heightDeg * METERS_PER_DEGREE,
|
|
24
|
+
width: (360 / 2 ** lonBits) * METERS_PER_DEGREE * cosLat,
|
|
25
|
+
heightDeg,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
20
28
|
/**
|
|
21
|
-
*
|
|
29
|
+
* 精度 p で保証できる半径(メートル)= マスの短い辺
|
|
30
|
+
*
|
|
31
|
+
* 経度方向の幅は、周囲 9 マスのうち最も高緯度の縁の cos を使う(高緯度ほど狭い)。
|
|
22
32
|
*/
|
|
23
|
-
function
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
export function guaranteedRadius(precision, latitude) {
|
|
34
|
+
const { heightDeg } = cellSizeMeters(precision, 1);
|
|
35
|
+
const worstLatitude = Math.min(89.9, Math.abs(latitude) + 2 * heightDeg);
|
|
36
|
+
const { height, width } = cellSizeMeters(precision, Math.cos((worstLatitude * Math.PI) / 180));
|
|
37
|
+
return Math.min(height, width);
|
|
38
|
+
}
|
|
39
|
+
/** シャドーの精度の位置の誤差 ε(メートル)= マスの対角線の半分(経度は赤道の長さで見て、上限にする) */
|
|
40
|
+
export function shadowPositionError(shadowPrecision) {
|
|
41
|
+
const { height, width } = cellSizeMeters(shadowPrecision, 1);
|
|
42
|
+
return Math.hypot(height, width) / 2;
|
|
43
|
+
}
|
|
44
|
+
/** 近似の距離の列から、k 番目に小さいもの(k 件に足りなければ undefined)。昇順に並べて渡すこと */
|
|
45
|
+
function kthOf(sortedAscending, k) {
|
|
46
|
+
return sortedAscending.length >= k ? sortedAscending[k - 1] : undefined;
|
|
29
47
|
}
|
|
30
48
|
/**
|
|
31
|
-
*
|
|
49
|
+
* 近隣検索を実行
|
|
32
50
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
51
|
+
* 1. 周囲 9 マスのシャドーを読み、近似の距離 d̃ を求める(重複は id で消える)
|
|
52
|
+
* 2. 上位 limit 件が 9 マスの中で確定する(d̃ₖ + ε ≤ r)か、円の全体を 9 マスが覆う(maxDistance + ε ≤ r)
|
|
53
|
+
* まで、精度を 1 つずつ下げて 1 に戻る(`minPrecision` まで)
|
|
54
|
+
* 3. d̃ が d̃ₖ + 2ε 以内の候補(maxDistance・minDistance があればその範囲)だけの本体を、1 回読む
|
|
35
55
|
*
|
|
36
56
|
* @param nearQuery - $nearクエリ
|
|
37
57
|
* @param fieldName - GeoHashフィールド名
|
|
38
58
|
* @param limit - 取得件数
|
|
39
|
-
* @param
|
|
59
|
+
* @param searchShadows - 1 マス(geohash の接頭辞)のシャドーを読む関数
|
|
60
|
+
* @param readBodies - id の本体を読む関数(ほかの条件で絞る場合は、ここで絞る)
|
|
40
61
|
* @param config - GeoHash設定
|
|
41
|
-
* @returns 距離付きドキュメントと検索メタデータ
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```typescript
|
|
45
|
-
* const result = await executeNearSearch(
|
|
46
|
-
* { latitude: 35.6812, longitude: 139.7671, maxDistance: 5000 },
|
|
47
|
-
* 'location',
|
|
48
|
-
* 10,
|
|
49
|
-
* async (geohashPrefix) => {
|
|
50
|
-
* // DynamoDBから検索
|
|
51
|
-
* return await collection.find({ geohash: { $regex: `^${geohashPrefix}` } }).toArray();
|
|
52
|
-
* }
|
|
53
|
-
* );
|
|
54
|
-
* ```
|
|
55
62
|
*/
|
|
56
|
-
export async function executeNearSearch(nearQuery, fieldName, limit,
|
|
57
|
-
// クエリから緯度・経度を抽出
|
|
63
|
+
export async function executeNearSearch(nearQuery, fieldName, limit, searchShadows, readBodies, config = DEFAULT_GEOHASH_CONFIG) {
|
|
58
64
|
const { latitude, longitude } = extractCoordinatesFromNearQuery(nearQuery);
|
|
59
65
|
const maxDistance = extractMaxDistanceFromNearQuery(nearQuery);
|
|
60
66
|
const minDistance = extractMinDistanceFromNearQuery(nearQuery);
|
|
67
|
+
const epsilon = shadowPositionError(config.shadowPrecision);
|
|
61
68
|
let precision = config.searchPrecision;
|
|
62
|
-
let allCandidates = [];
|
|
63
69
|
let iterations = 0;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
let exhaustive = false;
|
|
71
|
+
/** id → 近似の距離 d̃ */
|
|
72
|
+
let approx = new Map();
|
|
73
|
+
/** 上位 limit 件を確定できた d̃ₖ(確定できなければ undefined) */
|
|
74
|
+
let kth;
|
|
75
|
+
while (precision >= config.minPrecision && iterations < config.maxIterations) {
|
|
68
76
|
iterations++;
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// 重複を除外して追加
|
|
78
|
-
for (const candidate of candidates) {
|
|
79
|
-
if (!allCandidates.some((c) => c.id === candidate.id)) {
|
|
80
|
-
allCandidates.push(candidate);
|
|
81
|
-
}
|
|
77
|
+
const radius = guaranteedRadius(precision, latitude);
|
|
78
|
+
const neighbors = getNeighborGeoHashes(encodeGeoHash(latitude, longitude, precision));
|
|
79
|
+
const hits = (await Promise.all(neighbors.map((prefix) => searchShadows(prefix)))).flat();
|
|
80
|
+
// 精度を下げた回は、前の回の範囲を含むので、作り直す
|
|
81
|
+
approx = new Map();
|
|
82
|
+
for (const hit of hits) {
|
|
83
|
+
const center = decodeGeoHash(hit.geohash);
|
|
84
|
+
approx.set(hit.id, calculateDistance(latitude, longitude, center.latitude, center.longitude));
|
|
82
85
|
}
|
|
83
|
-
// maxDistance
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
candidatesWithinDistance++;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// 距離内の候補が十分に見つかったら終了
|
|
99
|
-
if (candidatesWithinDistance >= limit) {
|
|
100
|
-
console.log('[nearSearch] Early termination: found enough candidates within maxDistance', {
|
|
101
|
-
candidatesWithinDistance,
|
|
102
|
-
limit,
|
|
103
|
-
maxDistance,
|
|
104
|
-
precision,
|
|
105
|
-
iterations,
|
|
106
|
-
});
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
// 現在の精度での検索範囲がmaxDistanceを完全にカバーしている場合、
|
|
110
|
-
// これ以上精度を緩和しても意味がないので終了
|
|
111
|
-
if (coversMaxDistance(precision, maxDistance)) {
|
|
112
|
-
console.log('[nearSearch] Early termination: current precision covers maxDistance', {
|
|
113
|
-
precision,
|
|
114
|
-
maxDistance,
|
|
115
|
-
coverage: GEOHASH_COVERAGE[precision],
|
|
116
|
-
candidatesWithinDistance,
|
|
117
|
-
iterations,
|
|
118
|
-
});
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
86
|
+
// 結果になることが確かな候補(真の距離が minDistance 以上・maxDistance 以下)の中で、k 番目に近いもの
|
|
87
|
+
const sure = [...approx.values()]
|
|
88
|
+
.filter((d) => (minDistance === undefined || d - epsilon >= minDistance) &&
|
|
89
|
+
(maxDistance === undefined || d + epsilon <= maxDistance))
|
|
90
|
+
.sort((a, b) => a - b);
|
|
91
|
+
kth = kthOf(sure, limit);
|
|
92
|
+
const topKConfirmed = kth !== undefined && kth + epsilon <= radius;
|
|
93
|
+
const circleCovered = maxDistance !== undefined && maxDistance + epsilon <= radius;
|
|
94
|
+
if (topKConfirmed || circleCovered) {
|
|
95
|
+
exhaustive = true;
|
|
96
|
+
break;
|
|
121
97
|
}
|
|
122
|
-
// 精度を1文字減らす(検索範囲を広げる)
|
|
123
98
|
precision--;
|
|
124
99
|
}
|
|
125
|
-
//
|
|
126
|
-
const
|
|
100
|
+
// 本体を読む候補: 近似の距離が d̃ₖ + 2ε 以内(確定した上位 limit 件の真の距離は、d̃ₖ + ε 以内)
|
|
101
|
+
const cutoff = kth !== undefined ? kth + 2 * epsilon : undefined;
|
|
102
|
+
const wanted = [...approx.entries()]
|
|
103
|
+
.filter(([, d]) => (cutoff === undefined || d <= cutoff) &&
|
|
104
|
+
(maxDistance === undefined || d <= maxDistance + epsilon) &&
|
|
105
|
+
(minDistance === undefined || d >= minDistance - epsilon))
|
|
106
|
+
.map(([id]) => id);
|
|
107
|
+
const bodies = wanted.length > 0 ? await readBodies(wanted) : [];
|
|
108
|
+
const documents = bodies
|
|
127
109
|
.map((doc) => {
|
|
128
|
-
// フィールドから地理座標を取得
|
|
129
110
|
const location = doc[fieldName];
|
|
130
|
-
// デバッグログ: locationフィールドの内容を確認
|
|
131
|
-
if (!location) {
|
|
132
|
-
console.log('[nearSearch] Location field not found:', {
|
|
133
|
-
fieldName,
|
|
134
|
-
docKeys: Object.keys(doc),
|
|
135
|
-
doc: JSON.stringify(doc).substring(0, 200),
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
111
|
if (!location ||
|
|
139
112
|
typeof location.latitude !== 'number' ||
|
|
140
113
|
typeof location.longitude !== 'number') {
|
|
141
114
|
return null;
|
|
142
115
|
}
|
|
143
|
-
// 距離計算
|
|
144
116
|
const distance = calculateDistance(latitude, longitude, location.latitude, location.longitude);
|
|
145
|
-
|
|
146
|
-
console.log('[nearSearch] Distance calculated:', {
|
|
147
|
-
docId: doc.id,
|
|
148
|
-
distance,
|
|
149
|
-
maxDistance,
|
|
150
|
-
minDistance,
|
|
151
|
-
willBeFiltered: (maxDistance !== undefined && distance > maxDistance) ||
|
|
152
|
-
(minDistance !== undefined && distance < minDistance),
|
|
153
|
-
});
|
|
154
|
-
// 距離フィルタリング
|
|
155
|
-
if (maxDistance !== undefined && distance > maxDistance) {
|
|
117
|
+
if (maxDistance !== undefined && distance > maxDistance)
|
|
156
118
|
return null;
|
|
157
|
-
|
|
158
|
-
if (minDistance !== undefined && distance < minDistance) {
|
|
119
|
+
if (minDistance !== undefined && distance < minDistance)
|
|
159
120
|
return null;
|
|
160
|
-
}
|
|
161
121
|
return {
|
|
162
122
|
...doc,
|
|
163
123
|
__distance: distance,
|
|
@@ -168,11 +128,13 @@ export async function executeNearSearch(nearQuery, fieldName, limit, searchFunct
|
|
|
168
128
|
.sort((a, b) => (a.__distance ?? 0) - (b.__distance ?? 0))
|
|
169
129
|
.slice(0, limit);
|
|
170
130
|
return {
|
|
171
|
-
documents
|
|
131
|
+
documents,
|
|
172
132
|
metadata: {
|
|
173
133
|
iterations,
|
|
174
|
-
candidatesFound:
|
|
134
|
+
candidatesFound: approx.size,
|
|
135
|
+
bodiesRead: wanted.length,
|
|
175
136
|
requestedLimit: limit,
|
|
137
|
+
exhaustive,
|
|
176
138
|
},
|
|
177
139
|
};
|
|
178
140
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nearSearch.js","sourceRoot":"","sources":["../../../src/server/query/nearSearch.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"nearSearch.js","sourceRoot":"","sources":["../../../src/server/query/nearSearch.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,14 +3,25 @@ export interface ResolvedUpdate {
|
|
|
3
3
|
patch: Record<string, unknown>;
|
|
4
4
|
/** 新規作成のときだけ入れる値($setOnInsert) */
|
|
5
5
|
setOnInsert: Record<string, unknown>;
|
|
6
|
+
/** 足す値($inc。項目名(ドット記法可)→ 有限の数値)。今の値の読み取りと加算は updateMany が行う */
|
|
7
|
+
inc: Record<string, number>;
|
|
6
8
|
/** レスポンスの items に返す値($set の値。演算子が無ければパッチそのもの) */
|
|
7
9
|
returned: Record<string, unknown>;
|
|
8
10
|
}
|
|
11
|
+
/** ドット記法のパスで、今の値を読む(途中がオブジェクトでなければ undefined) */
|
|
12
|
+
export declare function readByPath(data: Record<string, unknown>, path: string): unknown;
|
|
13
|
+
/**
|
|
14
|
+
* $inc を当てる(今の値に足した値を $set として書くパッチを作る)
|
|
15
|
+
*
|
|
16
|
+
* 項目が無い・null のときは 0 から足す。数値でないときは 400。
|
|
17
|
+
* 返す `added` は、足した後の値(レスポンスの items 用)。
|
|
18
|
+
*/
|
|
19
|
+
export declare function applyInc(data: Record<string, unknown>, inc: Record<string, number>): Record<string, number>;
|
|
9
20
|
/**
|
|
10
21
|
* 更新のデータを解釈する
|
|
11
22
|
*
|
|
12
23
|
* - 演算子(`$` で始まるキー)が無ければ、全体を JSON Merge Patch として扱う
|
|
13
|
-
* - 演算子があれば、$set / $setOnInsert / $unset だけを受け付ける(ほかは 400)
|
|
24
|
+
* - 演算子があれば、$set / $setOnInsert / $unset / $inc だけを受け付ける(ほかは 400)
|
|
14
25
|
*
|
|
15
26
|
* @throws AppError VALIDATION_ERROR(400)
|
|
16
27
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateOperators.d.ts","sourceRoot":"","sources":["../../../src/server/utils/updateOperators.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;
|
|
1
|
+
{"version":3,"file":"updateOperators.d.ts","sourceRoot":"","sources":["../../../src/server/utils/updateOperators.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAsCD,iDAAiD;AACjD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAO/E;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcxB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,CA6BpF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzF"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 更新演算子($set / $setOnInsert / $unset)の解釈
|
|
2
|
+
* 更新演算子($set / $setOnInsert / $unset / $inc)の解釈
|
|
3
3
|
*
|
|
4
|
-
* 対応していない演算子($
|
|
4
|
+
* 対応していない演算子($push など)を黙って無視しない。無視すると、呼び出し側は更新できたと
|
|
5
5
|
* 思い込む($unset が無視され、項目を空にする操作が保存されない不具合が実際に起きた)。
|
|
6
6
|
*/
|
|
7
7
|
import { AppError, ErrorCode } from '../../shared/index.js';
|
|
8
|
-
const SUPPORTED_OPERATORS = ['$set', '$setOnInsert', '$unset'];
|
|
8
|
+
const SUPPORTED_OPERATORS = ['$set', '$setOnInsert', '$unset', '$inc'];
|
|
9
9
|
function invalid(message) {
|
|
10
10
|
return new AppError(ErrorCode.VALIDATION_ERROR, message, 400);
|
|
11
11
|
}
|
|
@@ -29,18 +29,58 @@ function unsetFields(value) {
|
|
|
29
29
|
}
|
|
30
30
|
return Object.keys(asObject(value, '$unset'));
|
|
31
31
|
}
|
|
32
|
+
/** $inc は、項目名 → 有限の数値。それ以外は 400 */
|
|
33
|
+
function incFields(value) {
|
|
34
|
+
const inc = {};
|
|
35
|
+
for (const [field, amount] of Object.entries(asObject(value, '$inc'))) {
|
|
36
|
+
if (typeof amount !== 'number' || !Number.isFinite(amount)) {
|
|
37
|
+
throw invalid(`$inc value for "${field}" must be a finite number`);
|
|
38
|
+
}
|
|
39
|
+
inc[field] = amount;
|
|
40
|
+
}
|
|
41
|
+
return inc;
|
|
42
|
+
}
|
|
43
|
+
/** ドット記法のパスで、今の値を読む(途中がオブジェクトでなければ undefined) */
|
|
44
|
+
export function readByPath(data, path) {
|
|
45
|
+
let current = data;
|
|
46
|
+
for (const key of path.split('.')) {
|
|
47
|
+
if (typeof current !== 'object' || current === null)
|
|
48
|
+
return undefined;
|
|
49
|
+
current = current[key];
|
|
50
|
+
}
|
|
51
|
+
return current;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* $inc を当てる(今の値に足した値を $set として書くパッチを作る)
|
|
55
|
+
*
|
|
56
|
+
* 項目が無い・null のときは 0 から足す。数値でないときは 400。
|
|
57
|
+
* 返す `added` は、足した後の値(レスポンスの items 用)。
|
|
58
|
+
*/
|
|
59
|
+
export function applyInc(data, inc) {
|
|
60
|
+
const added = {};
|
|
61
|
+
for (const [field, amount] of Object.entries(inc)) {
|
|
62
|
+
const current = readByPath(data, field);
|
|
63
|
+
if (current !== undefined &&
|
|
64
|
+
current !== null &&
|
|
65
|
+
(typeof current !== 'number' || !Number.isFinite(current))) {
|
|
66
|
+
throw invalid(`Field "${field}" is not a number, $inc cannot be applied`);
|
|
67
|
+
}
|
|
68
|
+
added[field] = (current ?? 0) + amount;
|
|
69
|
+
}
|
|
70
|
+
return added;
|
|
71
|
+
}
|
|
32
72
|
/**
|
|
33
73
|
* 更新のデータを解釈する
|
|
34
74
|
*
|
|
35
75
|
* - 演算子(`$` で始まるキー)が無ければ、全体を JSON Merge Patch として扱う
|
|
36
|
-
* - 演算子があれば、$set / $setOnInsert / $unset だけを受け付ける(ほかは 400)
|
|
76
|
+
* - 演算子があれば、$set / $setOnInsert / $unset / $inc だけを受け付ける(ほかは 400)
|
|
37
77
|
*
|
|
38
78
|
* @throws AppError VALIDATION_ERROR(400)
|
|
39
79
|
*/
|
|
40
80
|
export function resolveUpdateOperators(data) {
|
|
41
81
|
const operators = Object.keys(data).filter((key) => key.startsWith('$'));
|
|
42
82
|
if (operators.length === 0) {
|
|
43
|
-
return { patch: data, setOnInsert: {}, returned: data };
|
|
83
|
+
return { patch: data, setOnInsert: {}, inc: {}, returned: data };
|
|
44
84
|
}
|
|
45
85
|
const unsupported = operators.filter((op) => !SUPPORTED_OPERATORS.includes(op));
|
|
46
86
|
if (unsupported.length > 0) {
|
|
@@ -54,7 +94,14 @@ export function resolveUpdateOperators(data) {
|
|
|
54
94
|
}
|
|
55
95
|
patch[field] = null;
|
|
56
96
|
}
|
|
57
|
-
|
|
97
|
+
const inc = incFields(data.$inc);
|
|
98
|
+
for (const field of Object.keys(inc)) {
|
|
99
|
+
if (field in set)
|
|
100
|
+
throw invalid(`Field "${field}" is in both $set and $inc`);
|
|
101
|
+
if (field in patch)
|
|
102
|
+
throw invalid(`Field "${field}" is in both $unset and $inc`);
|
|
103
|
+
}
|
|
104
|
+
return { patch, setOnInsert: asObject(data.$setOnInsert, '$setOnInsert'), inc, returned: set };
|
|
58
105
|
}
|
|
59
106
|
/**
|
|
60
107
|
* フィルタから、新規作成(upsert)のレコードに入れる値を取り出す
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateOperators.js","sourceRoot":"","sources":["../../../src/server/utils/updateOperators.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"updateOperators.js","sourceRoot":"","sources":["../../../src/server/utils/updateOperators.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exabugs/dynamodb-client",
|
|
3
|
-
"version": "1.27.
|
|
3
|
+
"version": "1.27.1",
|
|
4
4
|
"description": "DynamoDB Single-Table Client SDK with MongoDB-like API, Shadow Records, and Lambda implementation for serverless applications",
|
|
5
5
|
"author": "exabugs",
|
|
6
6
|
"license": "MIT",
|