w-orm-lmdb 1.0.12 → 1.0.14
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 +45 -12
- package/dist/req-mingo.umd.js +1 -1
- package/dist/w-orm-lmdb.umd.js +2 -2
- package/dist/w-orm-lmdb.umd.js.map +1 -1
- package/docs/WOrmLmdb.html +18 -17
- package/docs/WOrmLmdb.mjs.html +815 -767
- package/docs/index.html +2 -2
- package/g-basic.mjs +12 -12
- package/package.json +1 -1
- package/src/WOrmLmdb.mjs +813 -765
- package/test/unit-basic.test.mjs +27 -27
- package/test/unit-cache.test.mjs +2 -2
- package/test/unit-del.test.mjs +127 -0
- package/test/unit-delall.test.mjs +121 -0
- package/test/unit-insert.test.mjs +11 -10
- package/test/unit-save.test.mjs +64 -13
- package//350/263/207/346/226/231/345/272/253/345/207/275/346/225/270/345/233/236/345/202/263/345/256/232/347/276/251/350/250/210/347/256/227.md +317 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# w-orm 系列套件統一規格
|
|
2
|
+
|
|
3
|
+
本文為 `w-orm-*` 系列套件之**規格**,非現況描述。凡新增或修改此系列之任一套件,皆須以本文為準。
|
|
4
|
+
|
|
5
|
+
規格與各套件之儲存後端無關:後端可為 key-value store、document database、關聯式資料庫或其他形式。規格只規範**對外行為**——函數名稱、參數、回傳結構、各欄位語義、錯誤處置與原子性要求;**不規範達成手段**,各套件依其後端能力自行實作。
|
|
6
|
+
|
|
7
|
+
適用函數共六個:`select`、`selectByPk`、`insert`、`save`、`del`、`delAll`。套件得另有後端專屬函數(如建表、檔案儲存),不在本規格範圍內,惟不得與六函數之語義衝突。
|
|
8
|
+
|
|
9
|
+
專屬函數若與六函數之概念對應(插入/單筆直讀/刪除/條件刪除),則**參數形狀亦須比照**,不只回傳結構:收「帶主鍵之數據物件或陣列」者回等長陣列,收單一主鍵者回單一物件。僅對齊回傳值而參數各行其是,會讓同一概念出現兩種呼叫方式,與本規格「呼叫端不須為個別套件寫特例」之目的相違。若某概念在該後端無法滿足本規格之要求(如無法原子取代內容而不能提供 `save`),則不提供該函數,並於 README 說明原因與替代作法;不得提供一個語義較弱而同名的版本。
|
|
10
|
+
|
|
11
|
+
只有當無法支援併發時才於各套件之README.md宣告。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 通則
|
|
16
|
+
|
|
17
|
+
### T1 主鍵稱謂與單筆直讀函數
|
|
18
|
+
|
|
19
|
+
各套件之主鍵欄位名不同(如 `id`、`time`),本文以「主鍵」統稱,實作時代換為各套件之欄位名。
|
|
20
|
+
|
|
21
|
+
單筆直讀函數一律名為 **`selectByPk`**,參數為主鍵值。名稱只表達「依主鍵直讀」這件事,不編入欄位名,故各套件不因主鍵欄位不同而異名,亦不須另掛別名。
|
|
22
|
+
|
|
23
|
+
**同名不表示可無條件互換。** 主鍵之性質仍隨套件而異——是否得由套件自動補值(見 T6)、是否承載業務語義、型別與值域為何,各不相同。`selectByPk` 之統一僅保證「呼叫方式與回傳結構一致」,不保證「換一個套件即可原樣運作」;上述差異須由呼叫端知悉並自行處理,各套件之主鍵欄位名與性質須於「各套件符合狀態」載明。
|
|
24
|
+
|
|
25
|
+
主鍵欄位是否可由呼叫端指定,本規格不作要求。固定為某一欄位者,須於函數註解與「各套件符合狀態」載明該欄位名,以及是否已支援指定。
|
|
26
|
+
|
|
27
|
+
### T2 回傳型別與鍵集合固定
|
|
28
|
+
|
|
29
|
+
同一函數不論走哪條路徑,回傳之**型別與鍵集合完全相同**。呼叫端不得需要先判斷某個鍵是否存在。
|
|
30
|
+
|
|
31
|
+
- 計數欄位(`n`、`nInserted`、`nModified`、`nDeleted`)在該函數的規格表中一旦列出即**恆出現**,無對應行為時填 `0`,不得省略。
|
|
32
|
+
- 逐筆函數(`save`、`del`)恆回傳與輸入**等長**之陣列,即使輸入為單一物件亦回傳長度為 `1` 之陣列。
|
|
33
|
+
- 整批函數(`insert`、`delAll`)恆回傳單一物件。
|
|
34
|
+
- 唯一的例外是 `err`:僅在該筆 `ok` 為 `0` 時出現,`ok` 為 `1` 時不得出現。
|
|
35
|
+
|
|
36
|
+
### T3 `n` 之定義
|
|
37
|
+
|
|
38
|
+
`n` 為**本次操作於資料庫端所命中或涉及之筆數**。逐函數定義如下,跨套件不得有第二種解讀:
|
|
39
|
+
|
|
40
|
+
| 函數 | `n` |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `insert` | 輸入筆數(陣列化後之長度),代表本次嘗試插入之基準 |
|
|
43
|
+
| `save`(逐筆) | 主鍵命中筆數,`0` 或 `1`。命中(不論內容有無變更)或經插入而產生皆為 `1` |
|
|
44
|
+
| `del`(逐筆) | 主鍵命中筆數,`0` 或 `1` |
|
|
45
|
+
| `delAll` | 實際刪除筆數,恆等於 `nDeleted` |
|
|
46
|
+
|
|
47
|
+
`n` 不得取全表筆數,不得為與結果無關之常數。
|
|
48
|
+
|
|
49
|
+
### T4 `ok` 與錯誤處置
|
|
50
|
+
|
|
51
|
+
`ok` 僅有兩值:`1` 成功、`0` 該筆失敗。
|
|
52
|
+
|
|
53
|
+
- 成功路徑一律 `ok: 1`。**不得**由驅動層之確認旗標(如 MongoDB 之 `acknowledged`、SQL driver 之連線狀態)直接推導——該類旗標會產生沒有錯誤訊息的 `ok: 0`,呼叫端無從處理。若確實需要反映驅動層的未確認狀態,須將其視為該筆失敗,回 `ok: 0` 並附 `err`。
|
|
54
|
+
- `ok: 0` 僅出現於逐筆函數(`save`、`del`),且**必附 `err` 字串**說明原因。
|
|
55
|
+
- 單筆失敗**不中斷整批**:其餘筆數照常處理,整批仍 resolve,該筆以 `ok: 0` 回報。
|
|
56
|
+
- 整批性錯誤(連線失敗、參數型別錯誤、資料表不存在、權限不足等)以 `Promise.reject` 拋出,不進入逐筆結果。
|
|
57
|
+
|
|
58
|
+
判別「該筆失敗」與「整批性錯誤」的原則:錯誤只影響該筆資料者為前者,影響後續所有筆數者為後者。
|
|
59
|
+
|
|
60
|
+
### T5 輸入無效之處置
|
|
61
|
+
|
|
62
|
+
「輸入無效」指傳入之 `data` 既非有效物件亦非有效陣列。此時不視為錯誤,回傳空結果:
|
|
63
|
+
|
|
64
|
+
| 函數 | 回傳 |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `insert` | `{ n: 0, nInserted: 0, ok: 1 }` |
|
|
67
|
+
| `save` | `[]` |
|
|
68
|
+
| `del` | `[]` |
|
|
69
|
+
|
|
70
|
+
主鍵值無效(未給、型別不符)之處置見各函數說明。
|
|
71
|
+
|
|
72
|
+
### T6 主鍵補值
|
|
73
|
+
|
|
74
|
+
`insert` 與 `save` 於輸入未帶有效主鍵時,自動產生主鍵值後再寫入。
|
|
75
|
+
|
|
76
|
+
**例外:主鍵具業務語義者不補值。** 主鍵若非無語義之識別碼,而是承載業務意義之欄位(如時序資料以觀測時間為主鍵),則不得自動補值——補值等同替呼叫端決定一筆資料的業務內容,會讓「呼叫端漏給主鍵」靜默變成「以當下之值寫入一筆」,其錯誤比直接失敗更難察覺。此類套件於輸入未帶有效主鍵時,以 `Promise.reject` 拋出,屬 T4 之整批性錯誤。
|
|
77
|
+
|
|
78
|
+
採用本例外之套件,須於下方「各套件符合狀態」載明其主鍵欄位與採用理由;未載明者一律適用補值規定。
|
|
79
|
+
|
|
80
|
+
`del` **不補值**——未帶有效主鍵者視為該筆無法處理,回 `ok: 0` 並附 `err`,且不得將無效主鍵送進查詢條件(部分後端會把 `undefined` 轉為 `null` 而誤中其他資料)。此規定不因上述例外而改變:`del` 於任一套件皆不補值,且未帶有效主鍵屬該筆問題而非整批性錯誤。
|
|
81
|
+
|
|
82
|
+
### T7 原子性要求
|
|
83
|
+
|
|
84
|
+
以下兩項判斷與寫入,必須由資料庫在**單一原子操作**內完成,不得以「先讀出、再依讀到的結果決定寫入」的方式實作:
|
|
85
|
+
|
|
86
|
+
| 函數 | 須原子完成之內容 |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `insert` | 「檢查主鍵不存在」與「寫入」 |
|
|
89
|
+
| `save` | 「查找主鍵」與「更新或插入」 |
|
|
90
|
+
|
|
91
|
+
達成手段不限,由各套件依後端能力選擇(唯一索引、`ON CONFLICT`、條件寫入、寫交易、比較並交換等)。
|
|
92
|
+
|
|
93
|
+
**若某後端須開啟特定設定才能達成本要求,該設定必須預設開啟。** 不得以「預設關閉、要正確性請自行開啟」的形式交付——`insert` 的「已存在則跳過」與 `save` 的「不遺失更新」是本規格定義的核心語義,不是選配。若開啟該設定對既有資料有前提(例如建立唯一索引前須先清除重複資料),須於 README 載明升級步驟。
|
|
94
|
+
|
|
95
|
+
`del` 不在本要求內:刪除為冪等操作,重複刪除不產生額外副作用。惟 `nDeleted` 在併發下可能不精確,套件得選擇以原子操作取得精確值。
|
|
96
|
+
|
|
97
|
+
### T8 併發保證之宣告義務
|
|
98
|
+
|
|
99
|
+
各套件須於 README 明確宣告其原子性保證的**適用範圍**,至少區分:
|
|
100
|
+
|
|
101
|
+
- **單一行程內併發**:同一行程內多個並行呼叫。
|
|
102
|
+
- **跨行程併發**:多個行程操作同一資料庫。
|
|
103
|
+
|
|
104
|
+
若某一範圍無法保證(受限於後端或其驅動層),須明確標示,並說明:失效時的**具體後果**(哪些欄位會失準、資料是否會損毀)、**實測依據**(平台、版本、條件、發生率),以及**建議的迴避方式**(單一寫入者、跨行程鎖等)。
|
|
105
|
+
|
|
106
|
+
不得只寫「支援併發」而不界定範圍。呼叫端據以決定是否需要自行加鎖,此資訊缺漏會直接導致誤用。
|
|
107
|
+
|
|
108
|
+
### T9 讀取函數不得有副作用
|
|
109
|
+
|
|
110
|
+
`select` 與 `selectByPk` 不得寫入資料、不得建立索引或資料表、不得改變任何可被觀察的狀態。需要初始化的動作應於建構時或寫入函數內完成。
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 各函數規格
|
|
115
|
+
|
|
116
|
+
### select(find) → `Promise<Array<Object>>`
|
|
117
|
+
|
|
118
|
+
依查詢條件取回多筆數據。
|
|
119
|
+
|
|
120
|
+
- 恆回傳陣列。無符合數據回 `[]`,不得回 `null` 或 `undefined`。
|
|
121
|
+
- `find` 未給或為空物件時回傳全部數據。
|
|
122
|
+
- 陣列元素為數據物件,**不得含資料庫內部欄位**(如 MongoDB 之 `_id`、SQL 之自增序號),呼叫端看到的欄位須與寫入時給的一致。
|
|
123
|
+
- 錯誤時 `reject`。
|
|
124
|
+
|
|
125
|
+
### selectByPk(pk) → `Promise<Object | null>`
|
|
126
|
+
|
|
127
|
+
依主鍵直讀單筆數據,不經 `select` 之全表提取與過濾。
|
|
128
|
+
|
|
129
|
+
| 情境 | 回傳 |
|
|
130
|
+
|---|---|
|
|
131
|
+
| 主鍵命中 | 該筆數據物件,內容與 `select({主鍵})[0]` 相同 |
|
|
132
|
+
| 主鍵未命中 | `null` |
|
|
133
|
+
| 主鍵值無效(未給、型別不符) | `null`,不 `reject` |
|
|
134
|
+
| 錯誤 | `reject` |
|
|
135
|
+
|
|
136
|
+
「命中」之判定基準須與 `insert`、`save`、`del` 內對既有數據之認定一致,不得出現 `selectByPk` 回傳物件而 `insert` 仍視為不存在之矛盾。
|
|
137
|
+
|
|
138
|
+
### insert(data) → `Promise<{ n, nInserted, ok }>`
|
|
139
|
+
|
|
140
|
+
**僅於主鍵不存在時寫入,已存在者跳過且不覆寫。**
|
|
141
|
+
|
|
142
|
+
| 欄位 | 值 |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `n` | 輸入筆數 |
|
|
145
|
+
| `nInserted` | 實際插入筆數,`0 ≤ nInserted ≤ n` |
|
|
146
|
+
| `ok` | `1` |
|
|
147
|
+
|
|
148
|
+
- 全數已存在而 `nInserted` 為 `0` 屬**正常結果**,不視為錯誤,不得 `reject`。
|
|
149
|
+
- 同批含重複主鍵時僅首筆計入 `nInserted`,其餘視為已存在。
|
|
150
|
+
- 須符合 T7 之原子性要求。
|
|
151
|
+
- 輸入無效見 T5。
|
|
152
|
+
|
|
153
|
+
### save(data, option) → `Promise<Array<{ n, nInserted, nModified, ok }>>`
|
|
154
|
+
|
|
155
|
+
以主鍵為準更新既有數據,未給之欄位保留;主鍵不存在且 `option.autoInsert` 為 `true`(預設)時改為插入。
|
|
156
|
+
|
|
157
|
+
逐筆結果:
|
|
158
|
+
|
|
159
|
+
| 情境 | `n` | `nInserted` | `nModified` | `ok` |
|
|
160
|
+
|---|---|---|---|---|
|
|
161
|
+
| 主鍵存在,合併後內容有變更 | `1` | `0` | `1` | `1` |
|
|
162
|
+
| 主鍵存在,合併後內容與現值相同而未寫入 | `1` | `0` | `0` | `1` |
|
|
163
|
+
| 主鍵不存在,`autoInsert: true` | `1` | `1` | `0` | `1` |
|
|
164
|
+
| 主鍵不存在,`autoInsert: false` | `0` | `0` | `0` | `1` |
|
|
165
|
+
| 該筆執行失敗 | `1` | `0` | `0` | `0` + `err` |
|
|
166
|
+
|
|
167
|
+
**「內容相同」之判定基準為:把待寫入物件合併進現值之後,結果與現值相同。** 相同則不寫入,`nModified` 為 `0`。
|
|
168
|
+
|
|
169
|
+
此基準的用意是讓 `nModified` 忠實反映「資料庫端是否真的寫入」。只給部份欄位且該些欄位值皆與現值相同時,合併結果等於現值,故 `nModified` 為 `0`——不得因為「傳入物件與現值不全等」就回報已修改,那會讓呼叫端把沒發生的變更當成發生了。
|
|
170
|
+
|
|
171
|
+
- `nInserted` 與 `nModified` **同時存在**,依 T2。
|
|
172
|
+
- 得於原子操作前先行預讀以判斷內容相同而略過寫入(快速路徑)。預讀值過期不影響正確性,因內容相同時該筆等價於無操作,可視為於預讀當下即已完成。**預讀結果不得用於決定寫入內容**——寫入內容一律由原子操作內讀到的現值決定。
|
|
173
|
+
- 須符合 T7 之原子性要求。
|
|
174
|
+
- 輸入無效見 T5。
|
|
175
|
+
|
|
176
|
+
### del(data) → `Promise<Array<{ n, nDeleted, ok }>>`
|
|
177
|
+
|
|
178
|
+
依主鍵刪除數據。
|
|
179
|
+
|
|
180
|
+
逐筆結果:
|
|
181
|
+
|
|
182
|
+
| 情境 | `n` | `nDeleted` | `ok` |
|
|
183
|
+
|---|---|---|---|
|
|
184
|
+
| 主鍵命中並刪除 | `1` | `1` | `1` |
|
|
185
|
+
| 主鍵未命中 | `0` | `0` | `1` |
|
|
186
|
+
| 該筆未帶有效主鍵 | `0` | `0` | `0` + `err` |
|
|
187
|
+
| 該筆執行失敗 | `1` | `0` | `0` + `err` |
|
|
188
|
+
|
|
189
|
+
- 「未帶有效主鍵」與「主鍵未命中」須以 `ok` 分辨:前者為輸入問題(`ok: 0`),後者為正常結果(`ok: 1`)。
|
|
190
|
+
- 判斷某筆是否真的被刪除,一律以 `nDeleted` 為準。
|
|
191
|
+
- 輸入無效見 T5。
|
|
192
|
+
|
|
193
|
+
### delAll(find) → `Promise<{ n, nDeleted, ok }>`
|
|
194
|
+
|
|
195
|
+
依條件刪除多筆數據。與 `del` 分開,避免未傳數據而誤刪全表。
|
|
196
|
+
|
|
197
|
+
| 欄位 | 值 |
|
|
198
|
+
|---|---|
|
|
199
|
+
| `n` | 實際刪除筆數 |
|
|
200
|
+
| `nDeleted` | 實際刪除筆數,恆等於 `n` |
|
|
201
|
+
| `ok` | `1` |
|
|
202
|
+
|
|
203
|
+
- `find` 未給或為空物件時刪除全部數據。
|
|
204
|
+
- 條件無命中時回 `{ n: 0, nDeleted: 0, ok: 1 }`,不視為錯誤。
|
|
205
|
+
- `n` **不得**取全表筆數。
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 回傳形狀速查
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
select(find) → [ {...}, {...} ] 無符合為 []
|
|
213
|
+
selectByPk(pk) → {...} | null
|
|
214
|
+
|
|
215
|
+
insert(data) → { n, nInserted, ok }
|
|
216
|
+
save(data, option) → [ { n, nInserted, nModified, ok }, ... ]
|
|
217
|
+
del(data) → [ { n, nDeleted, ok }, ... ]
|
|
218
|
+
delAll(find) → { n, nDeleted, ok }
|
|
219
|
+
|
|
220
|
+
單筆失敗 → { ..., ok: 0, err: '...' } 僅 save、del
|
|
221
|
+
整批失敗 → Promise.reject(err)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## 呼叫端判讀準則
|
|
227
|
+
|
|
228
|
+
| 要判斷什麼 | 看什麼 | 不要看什麼 |
|
|
229
|
+
|---|---|---|
|
|
230
|
+
| 這批有幾筆是新資料 | `insert` 之 `nInserted` | `n`(那是輸入筆數) |
|
|
231
|
+
| 這筆是不是新資料 | `save` 之 `nInserted === 1` | `n`(命中即為 1,插入與更新皆是) |
|
|
232
|
+
| 這筆內容有沒有實際寫入 | `save` 之 `nModified === 1` | `n` |
|
|
233
|
+
| 這筆主鍵原本存不存在 | `save` 之 `n === 1` 且 `nInserted === 0` | 單看 `n` |
|
|
234
|
+
| 這筆有沒有真的被刪 | `nDeleted` | `n` |
|
|
235
|
+
| 整批有沒有失敗 | Promise 是否 `reject` | — |
|
|
236
|
+
| 個別筆有沒有失敗 | 逐筆之 `ok === 0`,訊息取 `err` | — |
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 新增套件之檢查表
|
|
241
|
+
|
|
242
|
+
新套件納入 `w-orm-*` 系列前,逐項確認:
|
|
243
|
+
|
|
244
|
+
1. 六個函數皆存在,單筆直讀函數依 T1 名為 `selectByPk`,主鍵欄位名已於函數註解與「各套件符合狀態」載明。
|
|
245
|
+
2. `select` 恆回陣列且不含資料庫內部欄位,`selectByPk` 未命中回 `null`。
|
|
246
|
+
3. 每個函數之計數欄位恆出現,無對應行為時填 `0`;`err` 僅隨 `ok: 0` 出現。
|
|
247
|
+
4. `n` 依 T3 定義,四個函數各自的基準寫進函數註解。
|
|
248
|
+
5. `insert` 與 `save` 符合 T7 原子性要求;若倚賴某項設定達成,該設定預設開啟,且 README 載明升級前提。
|
|
249
|
+
6. 成功路徑 `ok` 恆為 `1`,不由驅動層旗標推導;`ok: 0` 必附 `err`;單筆失敗不中斷整批。
|
|
250
|
+
7. `save` 之「內容相同」採**合併後比對**,基準寫進註解。
|
|
251
|
+
8. `del` 對未帶有效主鍵者不送查詢,直接回 `ok: 0` + `err`。
|
|
252
|
+
9. 輸入無效之回傳依 T5;讀取函數無副作用(T9)。
|
|
253
|
+
10. README 依 T8 宣告併發保證範圍,無法保證者載明後果、實測依據與迴避方式。
|
|
254
|
+
11. 測試須涵蓋:同批重複主鍵、主鍵不存在、合併後內容相同、只給部份欄位且值相同、`autoInsert` 兩種取值、單筆失敗、未帶有效主鍵、`delAll` 帶條件且僅部份命中。
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## 各套件符合狀態
|
|
259
|
+
|
|
260
|
+
### w-orm-lmdb
|
|
261
|
+
|
|
262
|
+
主鍵欄位為 `id`,為無業務語義之識別碼,適用 T6 之補值規定。主鍵欄位目前**固定為 `id`,尚未支援由呼叫端指定**。
|
|
263
|
+
|
|
264
|
+
已符合 T1–T9 與六函數全部規格,無待處理項目。
|
|
265
|
+
|
|
266
|
+
`save` 之「內容相同」判定採合併後比對:將待寫入物件深層合併進現值後與現值比對,相同則不寫入。快速路徑之預讀僅用於判斷是否略過寫入,寫入內容一律由寫交易內讀到之現值決定。
|
|
267
|
+
|
|
268
|
+
T7 之原子性以 LMDB 之條件寫入與寫交易達成:`insert` 為 `ifNoExists`,`save` 為 `transaction` 包住讀取、合併與寫入,兩者皆無關閉選項。
|
|
269
|
+
|
|
270
|
+
T8 已完成:README 已宣告單一行程內保證成立、跨行程因 `lmdb-js` 綁定層限制而不成立,並附實測依據(平台、版本、行程數、回合數、發生率)與迴避方式。
|
|
271
|
+
|
|
272
|
+
### w-orm-mongodb
|
|
273
|
+
|
|
274
|
+
規格有更新,套件待確認與更新
|
|
275
|
+
|
|
276
|
+
<!--
|
|
277
|
+
主鍵欄位為 `id`,為無業務語義之識別碼,適用 T6 之補值規定。
|
|
278
|
+
|
|
279
|
+
待處理:
|
|
280
|
+
|
|
281
|
+
| 項目 | 現況 | 規格 |
|
|
282
|
+
|---|---|---|
|
|
283
|
+
| 單筆直讀函數名稱 | `selectById` | 依 T1 改名為 `selectByPk` |
|
|
284
|
+
| GridFS 單筆直讀函數名稱 | `selectByIdGfs` | 比照改名為 `selectByPkGfs` |
|
|
285
|
+
|
|
286
|
+
除上述命名外,已符合 T2–T9 與六函數全部規格。
|
|
287
|
+
|
|
288
|
+
`save` 之「內容相同」判定即本規格之基準來源:由 MongoDB 於伺服器端將 `$set` 之待寫入物件合併進現值後與現值比對,未寫入即回 `modifiedCount` 為 `0`,比對與寫入於同一原子操作內完成,故不須預讀。
|
|
289
|
+
|
|
290
|
+
T7 之原子性以主鍵之唯一索引達成,索引一律建立且無關閉選項;README 已載明既有資料表尚存重複主鍵時之清除步驟。
|
|
291
|
+
|
|
292
|
+
T8 已完成:README 已宣告單一行程內與跨行程併發皆成立,並附實測依據(含 2 個獨立行程對相同 20 個主鍵併發 `insert`、`nInserted` 總和為 20,以及 2 個行程對同一主鍵各寫入 20 個不同欄位、40 欄位全數保留)。
|
|
293
|
+
|
|
294
|
+
本套件另有 GridFS 專屬函數(現名 `selectByIdGfs`、`insertGfs`、`delGfs`、`delAllGfs`),依 §7 不在本規格範圍內,惟參數形狀與回傳結構皆已比照對應之六函數:數據物件為 `{ id, u8a }`,`insertGfs(data)` 收物件或陣列並回 `{ n, nInserted, ok }`、具「已存在則跳過」語義(以 `<cl>.files` 之 `filename` 唯一索引達成),單筆直讀查無回 `null`,`delGfs(data)` 收物件或陣列並回等長陣列,`delAllGfs(find)` 回 `{ n, nDeleted, ok }`。
|
|
295
|
+
|
|
296
|
+
`save` 無 GridFS 對應函數:GridFS 無法於單一原子操作內取代既有內容,提供 `saveGfs` 將違反 T7,故不提供,更新以 `delGfs` 後再 `insertGfs` 完成。 -->
|
|
297
|
+
|
|
298
|
+
### w-orm-postgresql
|
|
299
|
+
|
|
300
|
+
規格有更新,套件待確認與更新
|
|
301
|
+
|
|
302
|
+
<!--
|
|
303
|
+
主鍵欄位為 `time`,承載觀測時間之業務意義,故採 T6 例外:`insert` 與 `save` 於輸入未帶有效 `time` 時**不補值**,以 `Promise.reject` 拋出整批性錯誤。理由為此類時序資料若自動補入當下時間,將使「呼叫端漏給 `time`」靜默變成「多寫入一筆現在時刻之資料」,且該筆無從與正常資料區辨。`del` 不適用本例外,未帶有效 `time` 者仍依 T6 回該筆 `ok: 0` + `err`。
|
|
304
|
+
|
|
305
|
+
待處理:
|
|
306
|
+
|
|
307
|
+
| 項目 | 現況 | 規格 |
|
|
308
|
+
|---|---|---|
|
|
309
|
+
| 單筆直讀函數名稱 | `selectByTime` | 依 T1 改名為 `selectByPk`(主鍵欄位仍為 `time`,僅函數名統一) |
|
|
310
|
+
|
|
311
|
+
除上述命名外,已符合 T2–T9 與六函數全部規格。
|
|
312
|
+
|
|
313
|
+
`save` 之「內容相同」判定採合併後比對:以待寫入物件之非主鍵欄位淺層覆蓋現值後與現值比對。合併取淺層而非深層,係為與後端以 `EXCLUDED` 整欄取代之寫入行為一致——判定基準與實際寫入行為若不一致,`nModified` 即無法忠實反映是否真的寫入。
|
|
314
|
+
|
|
315
|
+
T7 之原子性以 `ON CONFLICT` 達成:`insert` 為單一 `INSERT ... ON CONFLICT (time) DO NOTHING`,取 `rowCount` 為 `nInserted`;`save` 於 `autoInsert` 開啟時為單一 `INSERT ... ON CONFLICT (time) DO UPDATE ... RETURNING (xmax = 0)`,以 `xmax` 區辨本語句插入與衝突後更新,關閉時為單一 `UPDATE ... WHERE time` 以免無中生有。其前提為主鍵 `time` 具唯一約束,`createTable` 一律以 `PRIMARY KEY` 建立且無關閉選項;README 已載明既有資料表缺該約束時之補建步驟,以及補建前須先清除重複 `time` 值。
|
|
316
|
+
|
|
317
|
+
T8 已完成:已測試單一行程內與跨行程併發皆成立,並附實測依據(含 2 個獨立行程對相同 20 個主鍵併發 `insert`、`nInserted` 總和為 20 且資料表僅 20 筆,以及 2 個行程對同一主鍵各寫入 5 個不同欄位、10 欄位全數保留)。本套件不保有行程內狀態且每次呼叫各自開啟連線,故單一行程內併發與跨行程併發於後端為同一情形。 -->
|