w-orm-lowdb 1.0.31 → 1.0.33

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.
@@ -66,6 +66,7 @@ import isestr from 'wsemi/src/isestr.mjs'
66
66
  import iseobj from 'wsemi/src/iseobj.mjs'
67
67
  import isearr from 'wsemi/src/isearr.mjs'
68
68
  import haskey from 'wsemi/src/haskey.mjs'
69
+ import cvOrmFind from 'wsemi/src/cvOrmFind.mjs'
69
70
 
70
71
 
71
72
  //kpGqueue, 以資料庫檔案路徑為鍵之全域序列化佇列, 詳見下方serialize說明
@@ -189,6 +190,93 @@ function WOrmLowdb(opt = {}) {
189
190
  return pm
190
191
  }
191
192
 
193
+ //reIsoDate, 西元0000-9999年之toISOString()恆為24字元YYYY-MM-DDTHH:mm:ss.sssZ, 字典序即時間序;
194
+ //年份<0或>9999者帶-yyyyyy-/+yyyyyy-前綴(27字元), 且'+'<'-'<數字, 字典序與時間序顛倒, 故不符此形者視為範圍外
195
+ let reIsoDate = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/
196
+
197
+ //pathKey, 路徑段(欄名或運算子名,皆已通過cvOrmFind之文法): 過長者截短, 規則同cvOrmFind之訊息(單鍵>30字元取前30加...,
198
+ //截斷處切開代理對而成孤立之高代理碼元者, cvOrmFind以?取代)
199
+ let pathKey = (k) => {
200
+ if (k.length <= 30) {
201
+ return k
202
+ }
203
+ let t = k.slice(0, 30)
204
+ let c = t.charCodeAt(29)
205
+ if (c >= 0xD800 && c <= 0xDBFF) {
206
+ t = `${t.slice(0, 29)}?`
207
+ }
208
+ return `${t}...`
209
+ }
210
+
211
+ //pathStr, 路徑字串化: 全路徑>120字元取...加末100字元, 規則同cvOrmFind之訊息
212
+ let pathStr = (s) => {
213
+ return s.length > 120 ? `...${s.slice(-100)}` : s
214
+ }
215
+
216
+ //isoFind, 把cvOrmFind所回傳複本內之Date就地轉為ISO字串(轉譯層): 數據以JSON存檔, Date於寫入時已成toISOString()之字串,
217
+ //條件內之Date不轉則恆不命中; 複本僅含null原型物件、密集陣列、純量與有效Date(無共享參照、無環), 就地轉換不影響呼叫端之find
218
+ //轉換所得之字串須為reIsoDate之形(檢查之值即使用之值), 範圍外(年份<0或>9999)者fail-closed: 拋出invalid find(帳本R-FIND-02), 不回顯值, p為路徑
219
+ let isoFind = (v, p = 'find') => {
220
+ if (v instanceof Date) {
221
+ let s = v.toISOString()
222
+ if (!reIsoDate.test(s)) {
223
+ throw new Error(`invalid find: Date year is out of range 0000-9999 at [${pathStr(p)}]`)
224
+ }
225
+ return s
226
+ }
227
+ if (v !== null && typeof v === 'object') {
228
+
229
+ //物件與陣列皆以Object.keys走訪(陣列為密集, 鍵即索引)
230
+ let isArr = Array.isArray(v)
231
+ for (let k of Object.keys(v)) {
232
+ v[k] = isoFind(v[k], isArr ? `${p}[${k}]` : `${p}.${pathKey(k)}`)
233
+ }
234
+ }
235
+ return v
236
+ }
237
+
238
+ //hasProtoName, 檢核後複本之欄名是否同於Object.prototype之成員(constructor、toString、valueOf、hasOwnProperty等):
239
+ //此類欄名屬白名單內之合法欄名, 但mingo以屬性存取取值, 數據無此自有欄位時會取到繼承之成員(函數)而非缺欄,
240
+ //使{constructor:null}無命中、{constructor:{$ne:null}}命中全部(delAll因而刪光); 欄名只會出現於複本之鍵(含$and/$or內之子條件)
241
+ let hasProtoName = (fc) => {
242
+ for (let k of Object.keys(fc)) {
243
+ if (k === '$and' || k === '$or') {
244
+ for (let sub of fc[k]) {
245
+ if (hasProtoName(sub)) {
246
+ return true
247
+ }
248
+ }
249
+ }
250
+ else if (k in Object.prototype) {
251
+ return true
252
+ }
253
+ }
254
+ return false
255
+ }
256
+
257
+ //mkMatch, 依檢核後複本建立逐筆命中判定, select與delAll共用同一判定(系列最低要求①: select命中之數據即delAll刪除之數據);
258
+ //欄名同於Object.prototype成員者, 改以null原型之淺複本(只含自有可列舉欄位)判定, 令其與一般不存在之欄位同為缺欄語意, 命中者仍回傳原數據
259
+ let mkMatch = (fc) => {
260
+ let q = new Query(fc)
261
+ if (!hasProtoName(fc)) {
262
+ return (v) => q.test(v)
263
+ }
264
+ return (v) => q.test(v !== null && typeof v === 'object' ? Object.assign(Object.create(null), v) : v)
265
+ }
266
+
267
+ //procFind, 條件之入口檢核(帳本R-FIND-01), 供select與delAll於serialize佇列之外、讀取之前呼叫:
268
+ //不合白名單者發出error事件(於reject之前)並回{err}, 由呼叫端reject, 不占用佇列; 合法者回{fc}為全新複本(null原型, 與呼叫端之find脫勾),
269
+ //其後一律只用此複本; 複本無自有鍵(未給、undefined、{})為全部
270
+ let procFind = (mode, find) => {
271
+ try {
272
+ return { fc: isoFind(cvOrmFind(find)) }
273
+ }
274
+ catch (err) {
275
+ emitError(mode, null, err)
276
+ return { err }
277
+ }
278
+ }
279
+
192
280
  //emitChange, 資料實際異動成功後發出, 事件僅為附加通知不承擔回傳義務
193
281
  //一律包try/catch, 令訂閱函數自身拋錯不影響本次操作之結果
194
282
  let emitChange = (mode, data, res) => {
@@ -300,11 +388,16 @@ function WOrmLowdb(opt = {}) {
300
388
  /**
301
389
  * 查詢數據
302
390
  *
391
+ * 條件find僅支援系列白名單(規則同delAll之第1-3點,見delAll),與delAll共用同一檢核,select(find)命中之數據即delAll(find)刪除之數據
392
+ * 條件內之Date年份須介於0000-9999,範圍外整批reject;數據側已存入之範圍外Date無法以Date條件正確比較(規則同delAll之第6點,見README)
393
+ *
303
394
  * @memberOf WOrmLowdb
304
- * @param {Object} [find={}] 輸入查詢條件物件
395
+ * @param {Object} [find={}] 輸入查詢條件物件(系列白名單,規則同delAll之第1-3點); 未給、undefined或{}回傳全部數據; 非條件物件或白名單外運算子整批reject
305
396
  * @returns {Promise} 回傳Promise,resolve回傳數據陣列,無符合數據回傳空陣列,reject回傳錯誤訊息
306
397
  */
307
- async function selectCore(find = {}) {
398
+ async function selectCore(find) {
399
+
400
+ //find, 入口檢核後之複本(procFind), 不再讀取呼叫端之條件
308
401
  let isErr = false
309
402
 
310
403
  //res
@@ -314,15 +407,11 @@ function WOrmLowdb(opt = {}) {
314
407
  //ltdt
315
408
  let ltdt = await getData()
316
409
 
317
- //filter
318
- if (iseobj(find)) {
410
+ //filter, 複本無自有鍵(未給、undefined、{})為全部
411
+ if (Reflect.ownKeys(find).length > 0) {
319
412
 
320
- //q
321
- let q = new Query(find)
322
- // console.log('q', q)
323
-
324
- //find
325
- res = q.find(ltdt).all()
413
+ //find, 與delAll共用同一逐筆判定
414
+ res = ltdt.filter(mkMatch(find))
326
415
  // console.log('res', res)
327
416
 
328
417
  }
@@ -354,7 +443,14 @@ function WOrmLowdb(opt = {}) {
354
443
  return res
355
444
  }
356
445
  async function select(find = {}) {
357
- return serialize(() => selectCore(find))
446
+
447
+ //check find, 置於serialize佇列之外, 錯誤不占佇列(R-FIND-01)
448
+ let pf = procFind('select', find)
449
+ if (pf.err) {
450
+ return Promise.reject(pf.err)
451
+ }
452
+
453
+ return serialize(() => selectCore(pf.fc))
358
454
  }
359
455
 
360
456
  /**
@@ -1009,13 +1105,39 @@ function WOrmLowdb(opt = {}) {
1009
1105
  }
1010
1106
 
1011
1107
  /**
1012
- * 刪除全部數據,需與del分開,避免未傳數據導致直接刪除全表
1108
+ * 依條件刪除多筆數據(破壞性操作,須審慎使用,非必要不用;刪除單筆請用del)
1109
+ *
1110
+ * 使用須知:
1111
+ * 1. 不給條件即刪除全部數據: find未給、為undefined或為{}時,刪除資料表內全部數據,不另行確認。
1112
+ * 給了條件才是部分刪除。
1113
+ * 2. 傳了值卻不是條件物件(null、陣列含[]與[{...}]、字串、數值、布林、函數、Date、RegExp、Map、Set、類別實例等)
1114
+ * 一律整批reject,不視為{},不刪除任何數據。
1115
+ * 3. 條件僅支援系列白名單: 欄位等值{欄:純量}(多欄為AND)、欄位運算子$eq $ne $gt $gte $lt $lte(值為純量)與
1116
+ * $in $nin(值為純量陣列)、文件層$and $or(值為條件物件之非空陣列)。純量為字串(不含NUL字元與孤立代理字元)、有限數字、布林、有效Date(年份須介於0000-9999,範圍外整批reject,見第6點);
1117
+ * null僅可用於欄位等值、$eq、$ne。白名單外之運算子($not $nor $regex $options $exists $where $expr $like $ilike等)、
1118
+ * 欄位層$and/$or、子查詢、函數值、Symbol鍵、undefined值、空的$nin/$and/$or等一律整批reject,
1119
+ * 訊息為invalid find: 原因 at [路徑],且不刪除任何數據。
1120
+ * 4. 與select共用同一檢核與同一條件,select(find)命中之數據即本函數刪除之數據;
1121
+ * 建議先以select(find)確認命中範圍再刪除(useCache開啟且他行程已寫入時,select可能回傳快取,而本函數一律直讀資料庫)。
1122
+ * 5. 條件值若來自可能為undefined、null或空陣列之變數,呼叫端須先自行判斷;undefined值會reject而非被忽略,
1123
+ * 空的$nin會reject,$in:[]則為恆假(刪除0筆)。
1124
+ * 6. 語意差異(依後端,見README): MongoDB式: $ne(比較值非null時)與$nin會命中欄位為null或缺欄之數據,$ne:null則排除之;
1125
+ * 欄名同於Object.prototype成員(constructor、toString等)者,數據無此自有欄位時視為缺欄;
1126
+ * Date條件於本後端轉為ISO字串後以字典序比較(與字串型欄位亦會比較):條件內任何位置之Date其年份須介於0000-9999,
1127
+ * 範圍外(年份小於0或大於9999,如new Date(8.64e15)、new Date(-8.64e15))整批reject,訊息為invalid find: Date year is out of range 0000-9999 at [路徑],
1128
+ * 不回顯值,不讀檔、不刪除任何數據;數據側已存入之範圍外Date(如以new Date(8.64e15)作永不過期哨兵)寫入行為不變,
1129
+ * 但此類資料無法以Date條件正確比較(例如{t:{$lt:現在}}會命中哨兵並將其刪除),見README。
1130
+ * 需明確表達時可用可攜寫法: 排除NULL為{$and:[{f:{$ne:null}},{f:{$ne:x}}]},含NULL為{$or:[{f:null},{f:{$ne:x}}]}。
1131
+ *
1132
+ * n與nDeleted皆為實際刪除筆數,不取全表筆數。條件無命中時回{n:0,nDeleted:0,ok:1},不視為錯誤。
1013
1133
  *
1014
1134
  * @memberOf WOrmLowdb
1015
- * @param {Object} [find={}] 輸入刪除條件物件
1016
- * @returns {Promise} 回傳Promise,resolve回傳刪除結果物件{n,nDeleted,ok},n與nDeleted同為實際刪除筆數,reject回傳錯誤訊息
1135
+ * @param {Object} [find={}] 輸入刪除條件物件,不給予find則代表刪除全部數據
1136
+ * @returns {Promise} 回傳Promise,resolve回傳刪除結果物件{n,nDeleted,ok},reject回傳錯誤訊息
1017
1137
  */
1018
- async function delAllCore(find = {}) {
1138
+ async function delAllCore(find) {
1139
+
1140
+ //find, 入口檢核後之複本(procFind), 不再讀取呼叫端之條件
1019
1141
  let isErr = false
1020
1142
 
1021
1143
  //res
@@ -1029,15 +1151,14 @@ function WOrmLowdb(opt = {}) {
1029
1151
  let nAll = size(arrOld)
1030
1152
  let nDel = 0
1031
1153
  let arrNew = null
1032
- if (iseobj(find)) {
1154
+ if (Reflect.ownKeys(find).length > 0) {
1033
1155
 
1034
- //q
1035
- let q = new Query(find)
1036
- // console.log('q', q)
1156
+ //match, 與select共用同一逐筆判定
1157
+ let match = mkMatch(find)
1037
1158
 
1038
1159
  //filter, 以與select同一條件逐筆判定, 保留未命中者
1039
1160
  arrNew = filter(arrOld, (v) => {
1040
- return !q.test(v)
1161
+ return !match(v)
1041
1162
  })
1042
1163
 
1043
1164
  //nDel
@@ -1094,7 +1215,14 @@ function WOrmLowdb(opt = {}) {
1094
1215
  return res
1095
1216
  }
1096
1217
  async function delAll(find = {}) {
1097
- return serialize(() => delAllCore(find))
1218
+
1219
+ //check find, 置於serialize佇列之外, 錯誤不占佇列(R-FIND-01)
1220
+ let pf = procFind('delAll', find)
1221
+ if (pf.err) {
1222
+ return Promise.reject(pf.err)
1223
+ }
1224
+
1225
+ return serialize(() => delAllCore(pf.fc))
1098
1226
  }
1099
1227
 
1100
1228
  //bind
@@ -1125,7 +1253,7 @@ export default WOrmLowdb
1125
1253
  <br class="clear">
1126
1254
 
1127
1255
  <footer>
1128
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Oct 11 2026 13:53:16 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
1256
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Oct 19 2026 20:07:22 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
1129
1257
  </footer>
1130
1258
 
1131
1259
  <script>prettyPrint();</script>
package/docs/index.html CHANGED
@@ -71,7 +71,7 @@
71
71
  <br class="clear">
72
72
 
73
73
  <footer>
74
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Oct 11 2026 13:53:16 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
74
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Oct 19 2026 20:07:22 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
75
75
  </footer>
76
76
 
77
77
  <script>prettyPrint();</script>
package/g-basic.mjs CHANGED
@@ -156,7 +156,7 @@ async function test() {
156
156
  let spc = await wo.select({ '$or': [{ '$and': [{ value: { '$ne': 123 } }, { value: { '$in': [123, 321, 123.456, 456] } }, { value: { '$nin': [456, 654] } }] }, { '$or': [{ value: { '$lte': -1 } }, { value: { '$gte': 400 } }] }] })
157
157
  console.log('select by $or, $and, $ne, $in, $nin', spc)
158
158
 
159
- // //select by regex //mingo不支援regex
159
+ // //select by regex //系列條件白名單不支援$regex(含$options), 會整批reject(invalid find: unsupported operator), 須改為select後自行過濾
160
160
  // let sr = await wo.select({ name: { $regex: 'PeT', $options: '$i' } })
161
161
  // console.log('selectReg', sr)
162
162
 
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "w-orm-lowdb",
3
- "version": "1.0.31",
3
+ "version": "1.0.33",
4
4
  "main": "dist/w-orm-lowdb.umd.js",
5
5
  "dependencies": {
6
6
  "lowdb": "^7.0.1",
7
7
  "mingo": "^7.2.4",
8
- "wsemi": "^1.9.12"
8
+ "wsemi": "^1.9.20"
9
9
  },
10
10
  "devDependencies": {
11
- "w-package-tools": "^1.1.16"
11
+ "w-package-tools": "^1.1.21"
12
12
  },
13
13
  "scripts": {
14
14
  "test": "mocha --parallel \"test/*.test.mjs\" --timeout 180000",
package/src/WOrmLowdb.mjs CHANGED
@@ -19,6 +19,7 @@ import isestr from 'wsemi/src/isestr.mjs'
19
19
  import iseobj from 'wsemi/src/iseobj.mjs'
20
20
  import isearr from 'wsemi/src/isearr.mjs'
21
21
  import haskey from 'wsemi/src/haskey.mjs'
22
+ import cvOrmFind from 'wsemi/src/cvOrmFind.mjs'
22
23
 
23
24
 
24
25
  //kpGqueue, 以資料庫檔案路徑為鍵之全域序列化佇列, 詳見下方serialize說明
@@ -142,6 +143,93 @@ function WOrmLowdb(opt = {}) {
142
143
  return pm
143
144
  }
144
145
 
146
+ //reIsoDate, 西元0000-9999年之toISOString()恆為24字元YYYY-MM-DDTHH:mm:ss.sssZ, 字典序即時間序;
147
+ //年份<0或>9999者帶-yyyyyy-/+yyyyyy-前綴(27字元), 且'+'<'-'<數字, 字典序與時間序顛倒, 故不符此形者視為範圍外
148
+ let reIsoDate = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/
149
+
150
+ //pathKey, 路徑段(欄名或運算子名,皆已通過cvOrmFind之文法): 過長者截短, 規則同cvOrmFind之訊息(單鍵>30字元取前30加...,
151
+ //截斷處切開代理對而成孤立之高代理碼元者, cvOrmFind以?取代)
152
+ let pathKey = (k) => {
153
+ if (k.length <= 30) {
154
+ return k
155
+ }
156
+ let t = k.slice(0, 30)
157
+ let c = t.charCodeAt(29)
158
+ if (c >= 0xD800 && c <= 0xDBFF) {
159
+ t = `${t.slice(0, 29)}?`
160
+ }
161
+ return `${t}...`
162
+ }
163
+
164
+ //pathStr, 路徑字串化: 全路徑>120字元取...加末100字元, 規則同cvOrmFind之訊息
165
+ let pathStr = (s) => {
166
+ return s.length > 120 ? `...${s.slice(-100)}` : s
167
+ }
168
+
169
+ //isoFind, 把cvOrmFind所回傳複本內之Date就地轉為ISO字串(轉譯層): 數據以JSON存檔, Date於寫入時已成toISOString()之字串,
170
+ //條件內之Date不轉則恆不命中; 複本僅含null原型物件、密集陣列、純量與有效Date(無共享參照、無環), 就地轉換不影響呼叫端之find
171
+ //轉換所得之字串須為reIsoDate之形(檢查之值即使用之值), 範圍外(年份<0或>9999)者fail-closed: 拋出invalid find(帳本R-FIND-02), 不回顯值, p為路徑
172
+ let isoFind = (v, p = 'find') => {
173
+ if (v instanceof Date) {
174
+ let s = v.toISOString()
175
+ if (!reIsoDate.test(s)) {
176
+ throw new Error(`invalid find: Date year is out of range 0000-9999 at [${pathStr(p)}]`)
177
+ }
178
+ return s
179
+ }
180
+ if (v !== null && typeof v === 'object') {
181
+
182
+ //物件與陣列皆以Object.keys走訪(陣列為密集, 鍵即索引)
183
+ let isArr = Array.isArray(v)
184
+ for (let k of Object.keys(v)) {
185
+ v[k] = isoFind(v[k], isArr ? `${p}[${k}]` : `${p}.${pathKey(k)}`)
186
+ }
187
+ }
188
+ return v
189
+ }
190
+
191
+ //hasProtoName, 檢核後複本之欄名是否同於Object.prototype之成員(constructor、toString、valueOf、hasOwnProperty等):
192
+ //此類欄名屬白名單內之合法欄名, 但mingo以屬性存取取值, 數據無此自有欄位時會取到繼承之成員(函數)而非缺欄,
193
+ //使{constructor:null}無命中、{constructor:{$ne:null}}命中全部(delAll因而刪光); 欄名只會出現於複本之鍵(含$and/$or內之子條件)
194
+ let hasProtoName = (fc) => {
195
+ for (let k of Object.keys(fc)) {
196
+ if (k === '$and' || k === '$or') {
197
+ for (let sub of fc[k]) {
198
+ if (hasProtoName(sub)) {
199
+ return true
200
+ }
201
+ }
202
+ }
203
+ else if (k in Object.prototype) {
204
+ return true
205
+ }
206
+ }
207
+ return false
208
+ }
209
+
210
+ //mkMatch, 依檢核後複本建立逐筆命中判定, select與delAll共用同一判定(系列最低要求①: select命中之數據即delAll刪除之數據);
211
+ //欄名同於Object.prototype成員者, 改以null原型之淺複本(只含自有可列舉欄位)判定, 令其與一般不存在之欄位同為缺欄語意, 命中者仍回傳原數據
212
+ let mkMatch = (fc) => {
213
+ let q = new Query(fc)
214
+ if (!hasProtoName(fc)) {
215
+ return (v) => q.test(v)
216
+ }
217
+ return (v) => q.test(v !== null && typeof v === 'object' ? Object.assign(Object.create(null), v) : v)
218
+ }
219
+
220
+ //procFind, 條件之入口檢核(帳本R-FIND-01), 供select與delAll於serialize佇列之外、讀取之前呼叫:
221
+ //不合白名單者發出error事件(於reject之前)並回{err}, 由呼叫端reject, 不占用佇列; 合法者回{fc}為全新複本(null原型, 與呼叫端之find脫勾),
222
+ //其後一律只用此複本; 複本無自有鍵(未給、undefined、{})為全部
223
+ let procFind = (mode, find) => {
224
+ try {
225
+ return { fc: isoFind(cvOrmFind(find)) }
226
+ }
227
+ catch (err) {
228
+ emitError(mode, null, err)
229
+ return { err }
230
+ }
231
+ }
232
+
145
233
  //emitChange, 資料實際異動成功後發出, 事件僅為附加通知不承擔回傳義務
146
234
  //一律包try/catch, 令訂閱函數自身拋錯不影響本次操作之結果
147
235
  let emitChange = (mode, data, res) => {
@@ -253,11 +341,16 @@ function WOrmLowdb(opt = {}) {
253
341
  /**
254
342
  * 查詢數據
255
343
  *
344
+ * 條件find僅支援系列白名單(規則同delAll之第1-3點,見delAll),與delAll共用同一檢核,select(find)命中之數據即delAll(find)刪除之數據
345
+ * 條件內之Date年份須介於0000-9999,範圍外整批reject;數據側已存入之範圍外Date無法以Date條件正確比較(規則同delAll之第6點,見README)
346
+ *
256
347
  * @memberOf WOrmLowdb
257
- * @param {Object} [find={}] 輸入查詢條件物件
348
+ * @param {Object} [find={}] 輸入查詢條件物件(系列白名單,規則同delAll之第1-3點); 未給、undefined或{}回傳全部數據; 非條件物件或白名單外運算子整批reject
258
349
  * @returns {Promise} 回傳Promise,resolve回傳數據陣列,無符合數據回傳空陣列,reject回傳錯誤訊息
259
350
  */
260
- async function selectCore(find = {}) {
351
+ async function selectCore(find) {
352
+
353
+ //find, 入口檢核後之複本(procFind), 不再讀取呼叫端之條件
261
354
  let isErr = false
262
355
 
263
356
  //res
@@ -267,15 +360,11 @@ function WOrmLowdb(opt = {}) {
267
360
  //ltdt
268
361
  let ltdt = await getData()
269
362
 
270
- //filter
271
- if (iseobj(find)) {
363
+ //filter, 複本無自有鍵(未給、undefined、{})為全部
364
+ if (Reflect.ownKeys(find).length > 0) {
272
365
 
273
- //q
274
- let q = new Query(find)
275
- // console.log('q', q)
276
-
277
- //find
278
- res = q.find(ltdt).all()
366
+ //find, 與delAll共用同一逐筆判定
367
+ res = ltdt.filter(mkMatch(find))
279
368
  // console.log('res', res)
280
369
 
281
370
  }
@@ -307,7 +396,14 @@ function WOrmLowdb(opt = {}) {
307
396
  return res
308
397
  }
309
398
  async function select(find = {}) {
310
- return serialize(() => selectCore(find))
399
+
400
+ //check find, 置於serialize佇列之外, 錯誤不占佇列(R-FIND-01)
401
+ let pf = procFind('select', find)
402
+ if (pf.err) {
403
+ return Promise.reject(pf.err)
404
+ }
405
+
406
+ return serialize(() => selectCore(pf.fc))
311
407
  }
312
408
 
313
409
  /**
@@ -962,13 +1058,39 @@ function WOrmLowdb(opt = {}) {
962
1058
  }
963
1059
 
964
1060
  /**
965
- * 刪除全部數據,需與del分開,避免未傳數據導致直接刪除全表
1061
+ * 依條件刪除多筆數據(破壞性操作,須審慎使用,非必要不用;刪除單筆請用del)
1062
+ *
1063
+ * 使用須知:
1064
+ * 1. 不給條件即刪除全部數據: find未給、為undefined或為{}時,刪除資料表內全部數據,不另行確認。
1065
+ * 給了條件才是部分刪除。
1066
+ * 2. 傳了值卻不是條件物件(null、陣列含[]與[{...}]、字串、數值、布林、函數、Date、RegExp、Map、Set、類別實例等)
1067
+ * 一律整批reject,不視為{},不刪除任何數據。
1068
+ * 3. 條件僅支援系列白名單: 欄位等值{欄:純量}(多欄為AND)、欄位運算子$eq $ne $gt $gte $lt $lte(值為純量)與
1069
+ * $in $nin(值為純量陣列)、文件層$and $or(值為條件物件之非空陣列)。純量為字串(不含NUL字元與孤立代理字元)、有限數字、布林、有效Date(年份須介於0000-9999,範圍外整批reject,見第6點);
1070
+ * null僅可用於欄位等值、$eq、$ne。白名單外之運算子($not $nor $regex $options $exists $where $expr $like $ilike等)、
1071
+ * 欄位層$and/$or、子查詢、函數值、Symbol鍵、undefined值、空的$nin/$and/$or等一律整批reject,
1072
+ * 訊息為invalid find: 原因 at [路徑],且不刪除任何數據。
1073
+ * 4. 與select共用同一檢核與同一條件,select(find)命中之數據即本函數刪除之數據;
1074
+ * 建議先以select(find)確認命中範圍再刪除(useCache開啟且他行程已寫入時,select可能回傳快取,而本函數一律直讀資料庫)。
1075
+ * 5. 條件值若來自可能為undefined、null或空陣列之變數,呼叫端須先自行判斷;undefined值會reject而非被忽略,
1076
+ * 空的$nin會reject,$in:[]則為恆假(刪除0筆)。
1077
+ * 6. 語意差異(依後端,見README): MongoDB式: $ne(比較值非null時)與$nin會命中欄位為null或缺欄之數據,$ne:null則排除之;
1078
+ * 欄名同於Object.prototype成員(constructor、toString等)者,數據無此自有欄位時視為缺欄;
1079
+ * Date條件於本後端轉為ISO字串後以字典序比較(與字串型欄位亦會比較):條件內任何位置之Date其年份須介於0000-9999,
1080
+ * 範圍外(年份小於0或大於9999,如new Date(8.64e15)、new Date(-8.64e15))整批reject,訊息為invalid find: Date year is out of range 0000-9999 at [路徑],
1081
+ * 不回顯值,不讀檔、不刪除任何數據;數據側已存入之範圍外Date(如以new Date(8.64e15)作永不過期哨兵)寫入行為不變,
1082
+ * 但此類資料無法以Date條件正確比較(例如{t:{$lt:現在}}會命中哨兵並將其刪除),見README。
1083
+ * 需明確表達時可用可攜寫法: 排除NULL為{$and:[{f:{$ne:null}},{f:{$ne:x}}]},含NULL為{$or:[{f:null},{f:{$ne:x}}]}。
1084
+ *
1085
+ * n與nDeleted皆為實際刪除筆數,不取全表筆數。條件無命中時回{n:0,nDeleted:0,ok:1},不視為錯誤。
966
1086
  *
967
1087
  * @memberOf WOrmLowdb
968
- * @param {Object} [find={}] 輸入刪除條件物件
969
- * @returns {Promise} 回傳Promise,resolve回傳刪除結果物件{n,nDeleted,ok},n與nDeleted同為實際刪除筆數,reject回傳錯誤訊息
1088
+ * @param {Object} [find={}] 輸入刪除條件物件,不給予find則代表刪除全部數據
1089
+ * @returns {Promise} 回傳Promise,resolve回傳刪除結果物件{n,nDeleted,ok},reject回傳錯誤訊息
970
1090
  */
971
- async function delAllCore(find = {}) {
1091
+ async function delAllCore(find) {
1092
+
1093
+ //find, 入口檢核後之複本(procFind), 不再讀取呼叫端之條件
972
1094
  let isErr = false
973
1095
 
974
1096
  //res
@@ -982,15 +1104,14 @@ function WOrmLowdb(opt = {}) {
982
1104
  let nAll = size(arrOld)
983
1105
  let nDel = 0
984
1106
  let arrNew = null
985
- if (iseobj(find)) {
1107
+ if (Reflect.ownKeys(find).length > 0) {
986
1108
 
987
- //q
988
- let q = new Query(find)
989
- // console.log('q', q)
1109
+ //match, 與select共用同一逐筆判定
1110
+ let match = mkMatch(find)
990
1111
 
991
1112
  //filter, 以與select同一條件逐筆判定, 保留未命中者
992
1113
  arrNew = filter(arrOld, (v) => {
993
- return !q.test(v)
1114
+ return !match(v)
994
1115
  })
995
1116
 
996
1117
  //nDel
@@ -1047,7 +1168,14 @@ function WOrmLowdb(opt = {}) {
1047
1168
  return res
1048
1169
  }
1049
1170
  async function delAll(find = {}) {
1050
- return serialize(() => delAllCore(find))
1171
+
1172
+ //check find, 置於serialize佇列之外, 錯誤不占佇列(R-FIND-01)
1173
+ let pf = procFind('delAll', find)
1174
+ if (pf.err) {
1175
+ return Promise.reject(pf.err)
1176
+ }
1177
+
1178
+ return serialize(() => delAllCore(pf.fc))
1051
1179
  }
1052
1180
 
1053
1181
  //bind