w-orm-lmdb 1.0.11 → 1.0.13

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.
@@ -29,7 +29,7 @@
29
29
  <nav >
30
30
 
31
31
 
32
- <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WOrmLmdb.html">WOrmLmdb</a><ul class='methods'><li data-type='method'><a href="WOrmLmdb.html#.del">del</a></li><li data-type='method'><a href="WOrmLmdb.html#.delAll">delAll</a></li><li data-type='method'><a href="WOrmLmdb.html#.insert">insert</a></li><li data-type='method'><a href="WOrmLmdb.html#.save">save</a></li><li data-type='method'><a href="WOrmLmdb.html#.select">select</a></li></ul></li></ul>
32
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="WOrmLmdb.html">WOrmLmdb</a><ul class='methods'><li data-type='method'><a href="WOrmLmdb.html#.del">del</a></li><li data-type='method'><a href="WOrmLmdb.html#.delAll">delAll</a></li><li data-type='method'><a href="WOrmLmdb.html#.insert">insert</a></li><li data-type='method'><a href="WOrmLmdb.html#.save">save</a></li><li data-type='method'><a href="WOrmLmdb.html#.select">select</a></li><li data-type='method'><a href="WOrmLmdb.html#.selectById">selectById</a></li></ul></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -45,663 +45,818 @@
45
45
 
46
46
  <section>
47
47
  <article>
48
- <pre class="prettyprint source linenums"><code>import { open } from 'lmdb'
49
- // import mingo from 'mingo' //mingo內未更新import寫法, 會導致ERR_UNSUPPORTED_DIR_IMPORT, 故須改用require引入使用
50
- import mingo from './reqMingo.js'
51
- import size from 'lodash-es/size.js'
52
- import get from 'lodash-es/get.js'
53
- import each from 'lodash-es/each.js'
54
- import map from 'lodash-es/map.js'
55
- import merge from 'lodash-es/merge.js'
56
- import isEqual from 'lodash-es/isEqual.js'
57
- import cloneDeep from 'lodash-es/cloneDeep.js'
58
- import isestr from 'wsemi/src/isestr.mjs'
59
- import isarr from 'wsemi/src/isarr.mjs'
60
- import isearr from 'wsemi/src/isearr.mjs'
61
- import iseobj from 'wsemi/src/iseobj.mjs'
62
- import isbol from 'wsemi/src/isbol.mjs'
63
- import haskey from 'wsemi/src/haskey.mjs'
64
- import evem from 'wsemi/src/evem.mjs'
65
- import genIDSeq from 'wsemi/src/genIDSeq.mjs'
66
- import pmSeries from 'wsemi/src/pmSeries.mjs'
67
- import waitFun from 'wsemi/src/waitFun.mjs'
68
-
69
-
70
- /**
71
- * 操作資料庫(LMDB)
72
- *
73
- * @class
74
- * @param {Object} [opt={}] 輸入設定物件,預設{}
75
- * @param {String} [opt.url='_db'] 輸入資料庫用資料夾字串,預設'_db'
76
- * @param {String} [opt.db='worm'] 輸入使用資料庫名稱字串,預設'worm'
77
- * @param {String} [opt.cl='test'] 輸入使用資料表名稱字串,預設'test'
78
- * @param {Boolean} [opt.useCache=false] 輸入是否使用select快取,適用於單程序操作,預設false
79
- * @returns {Object} 回傳操作資料庫物件,各事件功能詳見說明
80
- */
81
- function WOrmLmdb(opt = {}) {
82
-
83
- //_cache
84
- let _cache = null
85
-
86
- //url
87
- let url = get(opt, 'url')
88
- if (!isestr(url)) {
89
- url = './_db'
90
- }
91
-
92
- //db
93
- let db = get(opt, 'db')
94
- if (!isestr(db)) {
95
- db = 'worm'
96
- }
97
-
98
- //cl
99
- let cl = get(opt, 'cl')
100
- if (!isestr(cl)) {
101
- cl = 'test'
102
- }
103
-
104
- //useCache
105
- let useCache = get(opt, 'useCache')
106
- if (!isbol(useCache)) {
107
- useCache = false
108
- }
109
-
110
- //storage
111
- let storage = `${url}/${db}/${cl}`
112
- // console.log('storage',storage)
113
-
114
- // client
115
- let client = open({
116
- path: storage,
117
- compression: true,
118
- useVersions: true,
119
- })
120
-
121
- //ee
122
- let ee = evem()
123
-
124
- //getData
125
- let getData = async() => {
126
-
127
- //check
128
- if (useCache &amp;&amp; isarr(_cache)) {
129
- return cloneDeep(_cache) //與外部使用數據脫勾
130
- }
131
-
132
- //waitFun
133
- await waitFun(() => {
134
- if (client.status === 'closed') {
135
- console.log(`client.status[${client.status}], level is closed`)
136
- }
137
- return client.status === 'open'
138
- })
139
- // console.log('client.status',client.status)
140
-
141
- //ltdt
142
- let ltdt = []
143
- for await (let { value: dt } of client.getRange()) {
144
- // console.log('dt',dt)
145
- ltdt.push(dt)
146
- }
147
-
148
- if (useCache) {
149
- _cache = ltdt
150
- return cloneDeep(ltdt) //與外部使用數據脫勾
151
- }
152
- return ltdt
153
- }
154
-
155
- //getValue
156
- let getValue = async (key) => {
157
- let value = null
158
- try {
159
- value = await client.get(key)
160
- }
161
- catch (err) {
162
- // if (err.notFound) {
163
- // console.log('資料不存在')
164
- // }
165
- // else {
166
- // console.log('其他錯誤:', err)
167
- // }
168
- }
169
- return value
170
- }
171
-
172
- /**
173
- * 查詢數據
174
- *
175
- * @memberOf WOrmLmdb
176
- * @param {Object} [find={}] 輸入查詢條件物件
177
- * @returns {Promise} 回傳Promise,resolve回傳數據,reject回傳錯誤訊息
178
- */
179
- async function select(find = {}) {
180
- let isErr = false
181
- let res = null
182
-
183
- try {
184
-
185
- //ltdt
186
- let ltdt = await getData()
187
- // console.log('select ltdt',ltdt)
188
-
189
- //filter
190
- if (iseobj(find)) {
191
-
192
- //q
193
- let q = new mingo.Query(find)
194
- // console.log('q', q)
195
-
196
- //find
197
- res = q.find(ltdt).all()
198
- // console.log('select(find) ltdt',res)
199
-
200
- }
201
- else {
202
- res = ltdt
203
- }
204
-
205
- //check
206
- if (!isarr(res)) {
207
- isErr = true
208
- res = `can not select by find[${JSON.stringify(find)}]`
209
- }
210
-
211
- }
212
- catch (err) {
213
- isErr = true
214
- res = err
215
- }
216
- // console.log('res', res)
217
-
218
- //check
219
- if (isErr) {
220
- return Promise.reject(res)
221
- }
222
-
223
- return res
224
- }
225
-
226
- /**
227
- * 插入數據
228
- *
229
- * @memberOf WOrmLmdb
230
- * @param {Object|Array} data 輸入數據物件或陣列
231
- * @returns {Promise} 回傳Promise,resolve回傳插入結果,reject回傳錯誤訊息
232
- */
233
- async function insert(data) {
234
- let isErr = false
235
-
236
- //check
237
- if (!iseobj(data) &amp;&amp; !isearr(data)) {
238
- return {
239
- n: 0,
240
- nInserted: 0,
241
- ok: 1,
242
- }
243
- }
244
-
245
- //cloneDeep, 與外部數據脫勾
246
- data = cloneDeep(data)
247
-
248
- //res
249
- let res = null
250
- try {
251
-
252
- //check
253
- if (!isarr(data)) {
254
- data = [data]
255
- }
256
-
257
- //check id
258
- data = map(data, function(v) {
259
- if (!isestr(v.id)) {
260
- v.id = genIDSeq()
261
- }
262
- return v
263
- })
264
-
265
- //each
266
- let nAll = size(data)
267
- let nPush = 0
268
- for (let v of data) {
269
- // console.log(v)
270
-
271
- //查找資料表內v.id
272
- let vv = await getValue(v.id) //不會有catch
273
- // console.log('getValue',vv)
274
-
275
- //check
276
- if (!iseobj(vv)) {
277
- //未存在v.id
278
-
279
- //put
280
- await client.put(v.id, v)
281
-
282
- nPush++
283
- }
284
- else {
285
- //已存在v.id則不push
286
- }
287
-
288
- }
289
-
290
- //res
291
- res = {
292
- n: nAll,
293
- nInserted: nPush,
294
- ok: 1,
295
- }
296
-
297
- }
298
- catch (err) {
299
- isErr = true
300
- res = err
301
- }
302
-
303
- //update, 不能保證插入多少, 一律重設快取
304
- _cache = null
305
-
306
- //emit, 於change可能須使用select, 故須放在重設快取之後
307
- if (!isErr) {
308
- try {
309
-
310
- //emit
311
- ee.emit('change', 'insert', data, res)
312
-
313
- }
314
- catch (err) {
315
- console.log(err)
316
- }
317
- }
318
-
319
- //check
320
- if (isErr) {
321
- return Promise.reject(res)
322
- }
323
-
324
- return res
325
- }
326
-
327
- /**
328
- * 儲存數據
329
- *
330
- * @memberOf WOrmLmdb
331
- * @param {Object|Array} data 輸入數據物件或陣列
332
- * @param {Object} [option={}] 輸入設定物件,預設為{}
333
- * @param {boolean} [option.autoInsert=true] 輸入是否於儲存時發現原本無數據,則自動改以插入處理,預設為true
334
- * @returns {Promise} 回傳Promise,resolve回傳儲存結果,reject回傳錯誤訊息
335
- */
336
- async function save(data, option = {}) {
337
- let isErr = false
338
-
339
- //check
340
- if (!iseobj(data) &amp;&amp; !isearr(data)) {
341
- return []
342
- }
343
-
344
- //cloneDeep, 與外部數據脫勾
345
- data = cloneDeep(data)
346
-
347
- //autoInsert
348
- let autoInsert = get(option, 'autoInsert', true)
349
-
350
- //res
351
- let res = null
352
- try {
353
-
354
- //check
355
- if (!isarr(data)) {
356
- data = [data]
357
- }
358
-
359
- //check id
360
- data = map(data, function(v) {
361
- if (!isestr(v.id)) {
362
- v.id = genIDSeq()
363
- }
364
- return v
365
- })
366
-
367
- //pmSeries
368
- res = await pmSeries(data, async(v) => {
369
-
370
- //rest
371
- let rest = null
372
-
373
- //查找資料表內v.id
374
- let vv = await getValue(v.id) //不會有catch
375
-
376
- //existed
377
- let existed = iseobj(vv)
378
-
379
- //check
380
- if (existed) {
381
- //已存在v.id
382
- if (isEqual(v, vv)) {
383
- //內容相同不更新
384
- }
385
- else {
386
- //內容不同須更新
387
-
388
- //merge and put
389
- await client.put(v.id, merge(vv, v))
390
-
391
- rest = { update: true }
392
- }
393
- }
394
- else {
395
- //內容不存在
396
- }
397
-
398
- //rest
399
- if (iseobj(rest)) {
400
- rest = {
401
- n: 1,
402
- nModified: 1,
403
- ok: 1,
404
- }
405
- }
406
- else {
407
- rest = {
408
- n: 0,
409
- nModified: 0,
410
- ok: 1,
411
- }
412
- }
413
- //rest.n === 0:
414
- // 內容相同不更新, 不須update
415
- // 內容不存在, 若autoInsert則須insert
416
-
417
- //autoInsert
418
- if (autoInsert &amp;&amp; rest.n === 0 &amp;&amp; !existed) {
419
- // console.log('insert(v)', v)
420
- rest = await insert(v)
421
- }
422
-
423
- return rest
424
- })
425
-
426
- }
427
- catch (err) {
428
- isErr = true
429
- res = err
430
- }
431
-
432
- //update, 不能保證變更多少, 一律重設快取
433
- _cache = null
434
-
435
- //emit, 於change可能須使用select, 故須放在重設快取之後
436
- if (!isErr) {
437
- try {
438
-
439
- //emit
440
- ee.emit('change', 'save', data, res)
441
-
442
- }
443
- catch (err) {
444
- console.log(err)
445
- }
446
- }
447
-
448
- //check
449
- if (isErr) {
450
- return Promise.reject(res)
451
- }
452
-
453
- return res
454
- }
455
-
456
- /**
457
- * 刪除數據
458
- *
459
- * @memberOf WOrmLmdb
460
- * @param {Object|Array} data 輸入數據物件或陣列
461
- * @returns {Promise} 回傳Promise,resolve回傳刪除結果,reject回傳錯誤訊息
462
- */
463
- async function del(data) {
464
- let isErr = false
465
-
466
- //check
467
- if (!iseobj(data) &amp;&amp; !isearr(data)) {
468
- return []
469
- }
470
-
471
- //cloneDeep, 與外部數據脫勾
472
- data = cloneDeep(data)
473
-
474
- //res
475
- let res = null
476
- try {
477
-
478
- //check
479
- if (!isarr(data)) {
480
- data = [data]
481
- }
482
-
483
- //pmSeries
484
- res = await pmSeries(data, async(v) => {
485
-
486
- //rest
487
- let rest = null
488
-
489
- //id
490
- let id = get(v, 'id', '')
491
-
492
- //check
493
- if (isestr(id)) {
494
-
495
- //查找資料表內v.id
496
- let vv = await getValue(v.id) //不會有catch
497
-
498
- //check
499
- if (iseobj(vv)) {
500
- //已存在v.id則須刪除
501
-
502
- //del
503
- await client.del(v.id)
504
-
505
- //rest
506
- rest = {
507
- n: 1,
508
- nDeleted: 1,
509
- ok: 1,
510
- }
511
-
512
- }
513
- else {
514
- //不存在v.id則不刪除
515
-
516
- //rest
517
- rest = {
518
- n: 1,
519
- nDeleted: 0,
520
- ok: 1,
521
- }
522
-
523
- }
524
-
525
- }
526
- else {
527
- //未給v.id則不刪除
528
-
529
- //rest
530
- rest = {
531
- n: 1,
532
- nDeleted: 0,
533
- ok: 0, //未給v.id視為有問題數據, 故ok給0
534
- }
535
-
536
- }
537
-
538
- return rest
539
- })
540
-
541
- }
542
- catch (err) {
543
- isErr = true
544
- res = err
545
- }
546
-
547
- //update, 不能保證刪除多少, 一律重設快取
548
- _cache = null
549
-
550
- //emit, 於change可能須使用select, 故須放在重設快取之後
551
- if (!isErr) {
552
- try {
553
-
554
- //emit
555
- ee.emit('change', 'del', data, res)
556
-
557
- }
558
- catch (err) {
559
- console.log(err)
560
- }
561
- }
562
-
563
- //check
564
- if (isErr) {
565
- return Promise.reject(res)
566
- }
567
-
568
- return res
569
- }
570
-
571
- /**
572
- * 刪除全部數據,需與del分開,避免未傳數據導致直接刪除全表
573
- *
574
- * @memberOf WOrmLmdb
575
- * @param {Object} [find={}] 輸入刪除條件物件
576
- * @returns {Promise} 回傳Promise,resolve回傳刪除結果,reject回傳錯誤訊息
577
- */
578
- async function delAll(find = {}) {
579
- let isErr = false
580
-
581
- //res
582
- let res = null
583
- try {
584
-
585
- //ltdt
586
- let ltdt = await getData()
587
-
588
- //filter
589
- let nAll = size(ltdt)
590
- let nDel = 0
591
- if (iseobj(find)) {
592
-
593
- //q
594
- let q = new mingo.Query(find)
595
- // console.log('q', q)
596
-
597
- //find
598
- let _res = q.find(ltdt).all()
599
- // console.log('_res', _res)
600
-
601
- //nDel
602
- nDel = size(_res)
603
- // console.log('nDel', nDel)
604
-
605
- if (nDel === 0) {
606
- //未有find結果等於不刪除
607
- }
608
- else if (nAll === nDel) {
609
- //全在find結果內等於全部刪除
610
-
611
- //empty
612
- for (let v of ltdt) {
613
- await client.del(v.id)
614
- }
615
-
616
- }
617
- else {
618
- //部份在find結果內
619
-
620
- //_kp
621
- let _kp = {}
622
- each(_res, (v, k) => {
623
- _kp[v.id] = { k, v }
624
- })
625
-
626
- //del
627
- for (let v of ltdt) {
628
- if (haskey(_kp, v.id)) {
629
- //在find結果內代表須刪除
630
- await client.del(v.id)
631
- }
632
- }
633
-
634
- }
635
- }
636
- else {
637
-
638
- //nDel
639
- nDel = nAll
640
-
641
- //empty
642
- for (let v of ltdt) {
643
- await client.del(v.id)
644
- }
645
-
646
- }
647
-
648
- //res
649
- res = {
650
- n: nAll,
651
- nDeleted: nDel,
652
- ok: 1,
653
- }
654
-
655
- }
656
- catch (err) {
657
- isErr = true
658
- res = err
659
- }
660
-
661
- //update, 不能保證刪除多少, 一律重設快取
662
- _cache = null
663
-
664
- //emit, 於change可能須使用select, 故須放在重設快取之後
665
- if (!isErr) {
666
- try {
667
-
668
- //emit
669
- ee.emit('change', 'delAll', null, res)
670
-
671
- }
672
- catch (err) {
673
- console.log(err)
674
- }
675
- }
676
-
677
- //check
678
- if (isErr) {
679
- return Promise.reject(res)
680
- }
681
-
682
- return res
683
- }
684
-
685
- //close, 關閉內部LMDB env並釋放檔案鎖, close後該實例即報廢, 須重新new WOrmLmdb
686
- let close = async () => {
687
- if (client &amp;&amp; client.status !== 'closed') {
688
- await client.close() //lmdb root database之close(), 回傳Promise
689
- }
690
- }
691
-
692
- //save
693
- ee.select = select
694
- ee.insert = insert
695
- ee.save = save
696
- ee.del = del
697
- ee.delAll = delAll
698
- ee.close = close
699
-
700
- return ee
701
- }
702
-
703
-
704
- export default WOrmLmdb
48
+ <pre class="prettyprint source linenums"><code>import { open } from 'lmdb'
49
+ // import mingo from 'mingo' //mingo內未更新import寫法, 會導致ERR_UNSUPPORTED_DIR_IMPORT, 故須改用require引入使用
50
+ import mingo from './reqMingo.js'
51
+ import size from 'lodash-es/size.js'
52
+ import get from 'lodash-es/get.js'
53
+ import each from 'lodash-es/each.js'
54
+ import map from 'lodash-es/map.js'
55
+ import merge from 'lodash-es/merge.js'
56
+ import isEqual from 'lodash-es/isEqual.js'
57
+ import cloneDeep from 'lodash-es/cloneDeep.js'
58
+ import isestr from 'wsemi/src/isestr.mjs'
59
+ import isarr from 'wsemi/src/isarr.mjs'
60
+ import isearr from 'wsemi/src/isearr.mjs'
61
+ import iseobj from 'wsemi/src/iseobj.mjs'
62
+ import isbol from 'wsemi/src/isbol.mjs'
63
+ import haskey from 'wsemi/src/haskey.mjs'
64
+ import evem from 'wsemi/src/evem.mjs'
65
+ import genIDSeq from 'wsemi/src/genIDSeq.mjs'
66
+ import pmSeries from 'wsemi/src/pmSeries.mjs'
67
+ import waitFun from 'wsemi/src/waitFun.mjs'
68
+
69
+
70
+ /**
71
+ * 操作資料庫(LMDB)
72
+ *
73
+ * @class
74
+ * @param {Object} [opt={}] 輸入設定物件,預設{}
75
+ * @param {String} [opt.url='_db'] 輸入資料庫用資料夾字串,預設'_db'
76
+ * @param {String} [opt.db='worm'] 輸入使用資料庫名稱字串,預設'worm'
77
+ * @param {String} [opt.cl='test'] 輸入使用資料表名稱字串,預設'test'
78
+ * @param {Boolean} [opt.useCache=false] 輸入是否使用select快取,適用於單程序操作,預設false
79
+ * @returns {Object} 回傳操作資料庫物件,各事件功能詳見說明
80
+ */
81
+ function WOrmLmdb(opt = {}) {
82
+
83
+ //_cache
84
+ let _cache = null
85
+
86
+ //url
87
+ let url = get(opt, 'url')
88
+ if (!isestr(url)) {
89
+ url = './_db'
90
+ }
91
+
92
+ //db
93
+ let db = get(opt, 'db')
94
+ if (!isestr(db)) {
95
+ db = 'worm'
96
+ }
97
+
98
+ //cl
99
+ let cl = get(opt, 'cl')
100
+ if (!isestr(cl)) {
101
+ cl = 'test'
102
+ }
103
+
104
+ //useCache
105
+ let useCache = get(opt, 'useCache')
106
+ if (!isbol(useCache)) {
107
+ useCache = false
108
+ }
109
+
110
+ //storage
111
+ let storage = `${url}/${db}/${cl}`
112
+ // console.log('storage',storage)
113
+
114
+ // client
115
+ let client = open({
116
+ path: storage,
117
+ compression: true,
118
+ useVersions: true,
119
+ })
120
+
121
+ //ee
122
+ let ee = evem()
123
+
124
+ //getData
125
+ let getData = async() => {
126
+
127
+ //check
128
+ if (useCache &amp;&amp; isarr(_cache)) {
129
+ return cloneDeep(_cache) //與外部使用數據脫勾
130
+ }
131
+
132
+ //waitOpen
133
+ await waitOpen()
134
+ // console.log('client.status',client.status)
135
+
136
+ //ltdt
137
+ let ltdt = []
138
+ for await (let { value: dt } of client.getRange()) {
139
+ // console.log('dt',dt)
140
+ ltdt.push(dt)
141
+ }
142
+
143
+ if (useCache) {
144
+ _cache = ltdt
145
+ return cloneDeep(ltdt) //與外部使用數據脫勾
146
+ }
147
+ return ltdt
148
+ }
149
+
150
+ //waitOpen, 等待client開啟, 因getRange與get於client關閉時會拋錯
151
+ let waitOpen = async () => {
152
+ await waitFun(() => {
153
+ if (client.status === 'closed') {
154
+ console.log(`client.status[${client.status}], level is closed`)
155
+ }
156
+ return client.status === 'open'
157
+ })
158
+ }
159
+
160
+ //getErrMsg, 取錯誤訊息字串, 供逐筆結果之err欄位使用
161
+ let getErrMsg = (err) => {
162
+ let m = get(err, 'message')
163
+ if (isestr(m)) {
164
+ return m
165
+ }
166
+ return String(err)
167
+ }
168
+
169
+ //getValue
170
+ let getValue = async (key) => {
171
+ let value = null
172
+ try {
173
+ value = await client.get(key)
174
+ }
175
+ catch (err) {
176
+ // if (err.notFound) {
177
+ // console.log('資料不存在')
178
+ // }
179
+ // else {
180
+ // console.log('其他錯誤:', err)
181
+ // }
182
+ }
183
+ return value
184
+ }
185
+
186
+ /**
187
+ * 查詢數據
188
+ *
189
+ * @memberOf WOrmLmdb
190
+ * @param {Object} [find={}] 輸入查詢條件物件
191
+ * @returns {Promise} 回傳Promise,resolve回傳數據,reject回傳錯誤訊息
192
+ */
193
+ async function select(find = {}) {
194
+ let isErr = false
195
+ let res = null
196
+
197
+ try {
198
+
199
+ //ltdt
200
+ let ltdt = await getData()
201
+ // console.log('select ltdt',ltdt)
202
+
203
+ //filter
204
+ if (iseobj(find)) {
205
+
206
+ //q
207
+ let q = new mingo.Query(find)
208
+ // console.log('q', q)
209
+
210
+ //find
211
+ res = q.find(ltdt).all()
212
+ // console.log('select(find) ltdt',res)
213
+
214
+ }
215
+ else {
216
+ res = ltdt
217
+ }
218
+
219
+ //check
220
+ if (!isarr(res)) {
221
+ isErr = true
222
+ res = `can not select by find[${JSON.stringify(find)}]`
223
+ }
224
+
225
+ }
226
+ catch (err) {
227
+ isErr = true
228
+ res = err
229
+ }
230
+ // console.log('res', res)
231
+
232
+ //check
233
+ if (isErr) {
234
+ return Promise.reject(res)
235
+ }
236
+
237
+ return res
238
+ }
239
+
240
+ /**
241
+ * 由主鍵id查詢單筆數據,因直接由LMDB取值,不需如select提取全表數據再過濾,故數據量大時效能較佳
242
+ *
243
+ * @memberOf WOrmLmdb
244
+ * @param {String} id 輸入主鍵字串
245
+ * @returns {Promise} 回傳Promise,resolve回傳數據物件,若無此id則回傳null,reject回傳錯誤訊息
246
+ */
247
+ async function selectById(id) {
248
+ let isErr = false
249
+ let res = null
250
+
251
+ try {
252
+
253
+ //check
254
+ if (!isestr(id)) {
255
+ //未給有效id視為查無數據
256
+ return null
257
+ }
258
+
259
+ //waitOpen
260
+ await waitOpen()
261
+
262
+ //查找資料表內id
263
+ let v = await getValue(id) //不會有catch
264
+
265
+ //check, 判定基準與insert、save、del內對既有數據之認定一致
266
+ if (iseobj(v)) {
267
+ res = v
268
+ }
269
+ else {
270
+ //不存在id, 回傳null
271
+ res = null
272
+ }
273
+
274
+ }
275
+ catch (err) {
276
+ isErr = true
277
+ res = err
278
+ }
279
+
280
+ //check
281
+ if (isErr) {
282
+ return Promise.reject(res)
283
+ }
284
+
285
+ return res
286
+ }
287
+
288
+ /**
289
+ * 插入數據
290
+ *
291
+ * @memberOf WOrmLmdb
292
+ * @param {Object|Array} data 輸入數據物件或陣列
293
+ * @returns {Promise} 回傳Promise,resolve回傳插入結果物件{n,nInserted,ok},n為輸入筆數、nInserted為實際插入筆數,reject回傳錯誤訊息
294
+ */
295
+ async function insert(data) {
296
+ let isErr = false
297
+
298
+ //check
299
+ if (!iseobj(data) &amp;&amp; !isearr(data)) {
300
+ return {
301
+ n: 0,
302
+ nInserted: 0,
303
+ ok: 1,
304
+ }
305
+ }
306
+
307
+ //cloneDeep, 與外部數據脫勾
308
+ data = cloneDeep(data)
309
+
310
+ //res
311
+ let res = null
312
+ try {
313
+
314
+ //check
315
+ if (!isarr(data)) {
316
+ data = [data]
317
+ }
318
+
319
+ //check id
320
+ data = map(data, function(v) {
321
+ if (!isestr(v.id)) {
322
+ v.id = genIDSeq()
323
+ }
324
+ return v
325
+ })
326
+
327
+ //each
328
+ //ifNoExists, 由LMDB於同一寫交易內原子完成[檢查v.id未存在]與[寫入], 併發時同一v.id僅有一次成功,
329
+ //同批含重複v.id時亦僅首筆成功, 故不逐筆await而一次送出, 由LMDB寫執行緒依序處理並合併為批次寫交易
330
+ //注意: 回調函數須為同步函數, 且client.put須於回調函數內直接呼叫,
331
+ //若將client.put置於await之後, 該寫入會逸出條件批次而變成無條件寫入, 並使回傳值與實際狀態不一致
332
+ let nAll = size(data)
333
+ let ltdone = await Promise.all(map(data, function(v) {
334
+ // console.log(v)
335
+ return client.ifNoExists(v.id, () => {
336
+ client.put(v.id, v)
337
+ })
338
+ }))
339
+
340
+ //nPush
341
+ let nPush = 0
342
+ each(ltdone, function(done) {
343
+ if (done) {
344
+ //未存在v.id, 已寫入
345
+ nPush++
346
+ }
347
+ else {
348
+ //已存在v.id則不push
349
+ }
350
+ })
351
+
352
+ //res
353
+ res = {
354
+ n: nAll,
355
+ nInserted: nPush,
356
+ ok: 1,
357
+ }
358
+
359
+ }
360
+ catch (err) {
361
+ isErr = true
362
+ res = err
363
+ }
364
+
365
+ //update, 不能保證插入多少, 一律重設快取
366
+ _cache = null
367
+
368
+ //emit, 於change可能須使用select, 故須放在重設快取之後
369
+ if (!isErr) {
370
+ try {
371
+
372
+ //emit
373
+ ee.emit('change', 'insert', data, res)
374
+
375
+ }
376
+ catch (err) {
377
+ console.log(err)
378
+ }
379
+ }
380
+
381
+ //check
382
+ if (isErr) {
383
+ return Promise.reject(res)
384
+ }
385
+
386
+ return res
387
+ }
388
+
389
+ /**
390
+ * 儲存數據
391
+ *
392
+ * @memberOf WOrmLmdb
393
+ * @param {Object|Array} data 輸入數據物件或陣列
394
+ * @param {Object} [option={}] 輸入設定物件,預設為{}
395
+ * @param {boolean} [option.autoInsert=true] 輸入是否於儲存時發現原本無數據,則自動改以插入處理,預設為true
396
+ * @returns {Promise} 回傳Promise,resolve回傳與輸入等長之儲存結果陣列,各筆為{n,nInserted,nModified,ok},n為主鍵命中筆數,單筆失敗者ok為0並附err,reject回傳錯誤訊息
397
+ */
398
+ async function save(data, option = {}) {
399
+ let isErr = false
400
+
401
+ //check
402
+ if (!iseobj(data) &amp;&amp; !isearr(data)) {
403
+ return []
404
+ }
405
+
406
+ //cloneDeep, 與外部數據脫勾
407
+ data = cloneDeep(data)
408
+
409
+ //autoInsert
410
+ let autoInsert = get(option, 'autoInsert', true)
411
+
412
+ //res
413
+ let res = null
414
+ try {
415
+
416
+ //check
417
+ if (!isarr(data)) {
418
+ data = [data]
419
+ }
420
+
421
+ //check id
422
+ data = map(data, function(v) {
423
+ if (!isestr(v.id)) {
424
+ v.id = genIDSeq()
425
+ }
426
+ return v
427
+ })
428
+
429
+ //pmSeries
430
+ res = await pmSeries(data, async(v) => {
431
+
432
+ //rest
433
+ let rest = null
434
+
435
+ //inserted, 供emit使用
436
+ let inserted = false
437
+
438
+ try {
439
+
440
+ //快速路徑, 先不開寫交易直接預讀, 內容相同者本就不須寫入, 可省去開啟寫交易之成本
441
+ //預讀值縱使已被其他寫入者更動而過期亦不影響正確性, 因內容相同時本次save等價於無操作,
442
+ //無操作可視為於預讀當下即已完成, 其後他人之寫入結果與[本次save先執行再輪到他人]相同
443
+ //註: 預讀僅用於判斷是否略過寫入, 不用於決定寫入內容, 寫入內容一律由交易內讀到之現值決定
444
+ let vp = await getValue(v.id) //不會有catch
445
+ if (iseobj(vp) &amp;&amp; isEqual(merge({}, vp, v), vp)) {
446
+ //合併後與現值相同不須寫入, 惟已命中v.id故n為1
447
+ return {
448
+ n: 1,
449
+ nInserted: 0,
450
+ nModified: 0,
451
+ ok: 1,
452
+ }
453
+ }
454
+
455
+ //existed, updated
456
+ let existed = false
457
+ let updated = false
458
+
459
+ //transaction, 由LMDB於同一寫交易內原子完成[查找v.id]與[合併寫入],
460
+ //併發時不會有兩方各自讀到同一舊值再各自寫入而導致遺失更新
461
+ //注意: 回調函數須為同步函數, client.get於寫交易內為同步取值, 不可使用await,
462
+ //若將client.get或client.put置於await之後, 該操作會逸出本交易而失去原子性
463
+ await client.transaction(() => {
464
+
465
+ //查找資料表內v.id
466
+ let vv = client.get(v.id)
467
+
468
+ //existed
469
+ existed = iseobj(vv)
470
+
471
+ //check
472
+ if (existed) {
473
+ //已存在v.id
474
+
475
+ //vm, 合併後之結果
476
+ let vm = merge({}, vv, v)
477
+
478
+ //判定基準為[合併後結果與現值相同], 而非[待寫入物件與現值全等],
479
+ //令nModified忠實反映資料庫端是否真的寫入, 僅給部份欄位且值皆相同者不應回報已修改
480
+ if (isEqual(vm, vv)) {
481
+ //合併後與現值相同不須寫入
482
+ }
483
+ else {
484
+ //合併後與現值不同須更新
485
+
486
+ //put
487
+ client.put(v.id, vm)
488
+
489
+ updated = true
490
+ }
491
+ }
492
+ else {
493
+ //內容不存在, 若autoInsert則須插入
494
+ if (autoInsert) {
495
+
496
+ //put
497
+ client.put(v.id, v)
498
+
499
+ inserted = true
500
+ }
501
+ }
502
+
503
+ })
504
+
505
+ //rest, nInserted與nModified恆存在, 無對應行為者給0
506
+ if (inserted) {
507
+ rest = {
508
+ n: 1,
509
+ nInserted: 1,
510
+ nModified: 0,
511
+ ok: 1,
512
+ }
513
+ }
514
+ else if (updated) {
515
+ rest = {
516
+ n: 1,
517
+ nInserted: 0,
518
+ nModified: 1,
519
+ ok: 1,
520
+ }
521
+ }
522
+ else {
523
+ //未寫入, 有二:
524
+ // 已存在v.id而內容相同不更新, 已命中故n為1
525
+ // 不存在v.id且未開啟autoInsert, 未命中故n為0
526
+ //n為v.id之命中筆數, 與nModified[實際更新筆數]分屬不同語義, 不可混用
527
+ rest = {
528
+ n: existed ? 1 : 0,
529
+ nInserted: 0,
530
+ nModified: 0,
531
+ ok: 1,
532
+ }
533
+ }
534
+
535
+ }
536
+ catch (err) {
537
+
538
+ //本筆失敗不中斷整批, 以ok為0並附err回報, 由呼叫端逐筆檢查
539
+ rest = {
540
+ n: 1,
541
+ nInserted: 0,
542
+ nModified: 0,
543
+ ok: 0,
544
+ err: getErrMsg(err),
545
+ }
546
+
547
+ }
548
+
549
+ //emit, 原autoInsert係轉呼叫insert故會發出insert事件, 此處維持既有行為,
550
+ //須於交易外發出, 避免外部訂閱函數於寫交易內執行
551
+ if (inserted) {
552
+
553
+ //update, 不能保證插入多少, 一律重設快取
554
+ _cache = null
555
+
556
+ try {
557
+
558
+ //emit
559
+ ee.emit('change', 'insert', [v], rest)
560
+
561
+ }
562
+ catch (err) {
563
+ console.log(err)
564
+ }
565
+
566
+ }
567
+
568
+ return rest
569
+ })
570
+
571
+ }
572
+ catch (err) {
573
+ isErr = true
574
+ res = err
575
+ }
576
+
577
+ //update, 不能保證變更多少, 一律重設快取
578
+ _cache = null
579
+
580
+ //emit, 於change可能須使用select, 故須放在重設快取之後
581
+ if (!isErr) {
582
+ try {
583
+
584
+ //emit
585
+ ee.emit('change', 'save', data, res)
586
+
587
+ }
588
+ catch (err) {
589
+ console.log(err)
590
+ }
591
+ }
592
+
593
+ //check
594
+ if (isErr) {
595
+ return Promise.reject(res)
596
+ }
597
+
598
+ return res
599
+ }
600
+
601
+ /**
602
+ * 刪除數據
603
+ *
604
+ * @memberOf WOrmLmdb
605
+ * @param {Object|Array} data 輸入數據物件或陣列
606
+ * @returns {Promise} 回傳Promise,resolve回傳與輸入等長之刪除結果陣列,各筆為{n,nDeleted,ok},n為主鍵命中筆數,單筆失敗或未給有效id者ok為0並附err,reject回傳錯誤訊息
607
+ */
608
+ async function del(data) {
609
+ let isErr = false
610
+
611
+ //check
612
+ if (!iseobj(data) &amp;&amp; !isearr(data)) {
613
+ return []
614
+ }
615
+
616
+ //cloneDeep, 與外部數據脫勾
617
+ data = cloneDeep(data)
618
+
619
+ //res
620
+ let res = null
621
+ try {
622
+
623
+ //check
624
+ if (!isarr(data)) {
625
+ data = [data]
626
+ }
627
+
628
+ //pmSeries
629
+ res = await pmSeries(data, async(v) => {
630
+
631
+ //rest
632
+ let rest = null
633
+
634
+ try {
635
+
636
+ //id
637
+ let id = get(v, 'id', '')
638
+
639
+ //check
640
+ if (!isestr(id)) {
641
+ //未給有效v.id視為該筆數據有問題而無法處理, 以ok為0並附err回報,
642
+ //與[已給v.id但查無數據]之ok為1有別, 二者須可由ok分辨
643
+ return {
644
+ n: 0,
645
+ nDeleted: 0,
646
+ ok: 0,
647
+ err: `can not delete by invalid id[${id}]`,
648
+ }
649
+ }
650
+
651
+ //查找資料表內v.id
652
+ let vv = await getValue(id) //不會有catch
653
+
654
+ //check
655
+ if (iseobj(vv)) {
656
+ //已存在v.id則須刪除
657
+
658
+ //del
659
+ await client.del(id)
660
+
661
+ //rest
662
+ rest = {
663
+ n: 1,
664
+ nDeleted: 1,
665
+ ok: 1,
666
+ }
667
+
668
+ }
669
+ else {
670
+ //不存在v.id則不刪除, 未命中故n為0
671
+ rest = {
672
+ n: 0,
673
+ nDeleted: 0,
674
+ ok: 1,
675
+ }
676
+
677
+ }
678
+
679
+ }
680
+ catch (err) {
681
+
682
+ //本筆失敗不中斷整批, 以ok為0並附err回報, 由呼叫端逐筆檢查
683
+ rest = {
684
+ n: 1,
685
+ nDeleted: 0,
686
+ ok: 0,
687
+ err: getErrMsg(err),
688
+ }
689
+
690
+ }
691
+
692
+ return rest
693
+ })
694
+
695
+ }
696
+ catch (err) {
697
+ isErr = true
698
+ res = err
699
+ }
700
+
701
+ //update, 不能保證刪除多少, 一律重設快取
702
+ _cache = null
703
+
704
+ //emit, 於change可能須使用select, 故須放在重設快取之後
705
+ if (!isErr) {
706
+ try {
707
+
708
+ //emit
709
+ ee.emit('change', 'del', data, res)
710
+
711
+ }
712
+ catch (err) {
713
+ console.log(err)
714
+ }
715
+ }
716
+
717
+ //check
718
+ if (isErr) {
719
+ return Promise.reject(res)
720
+ }
721
+
722
+ return res
723
+ }
724
+
725
+ /**
726
+ * 刪除全部數據,需與del分開,避免未傳數據導致直接刪除全表
727
+ *
728
+ * @memberOf WOrmLmdb
729
+ * @param {Object} [find={}] 輸入刪除條件物件
730
+ * @returns {Promise} 回傳Promise,resolve回傳刪除結果物件{n,nDeleted,ok},n與nDeleted同為實際刪除筆數,reject回傳錯誤訊息
731
+ */
732
+ async function delAll(find = {}) {
733
+ let isErr = false
734
+
735
+ //res
736
+ let res = null
737
+ try {
738
+
739
+ //ltdt
740
+ let ltdt = await getData()
741
+
742
+ //filter
743
+ let nAll = size(ltdt)
744
+ let nDel = 0
745
+ if (iseobj(find)) {
746
+
747
+ //q
748
+ let q = new mingo.Query(find)
749
+ // console.log('q', q)
750
+
751
+ //find
752
+ let _res = q.find(ltdt).all()
753
+ // console.log('_res', _res)
754
+
755
+ //nDel
756
+ nDel = size(_res)
757
+ // console.log('nDel', nDel)
758
+
759
+ if (nDel === 0) {
760
+ //未有find結果等於不刪除
761
+ }
762
+ else if (nAll === nDel) {
763
+ //全在find結果內等於全部刪除
764
+
765
+ //empty
766
+ for (let v of ltdt) {
767
+ await client.del(v.id)
768
+ }
769
+
770
+ }
771
+ else {
772
+ //部份在find結果內
773
+
774
+ //_kp
775
+ let _kp = {}
776
+ each(_res, (v, k) => {
777
+ _kp[v.id] = { k, v }
778
+ })
779
+
780
+ //del
781
+ for (let v of ltdt) {
782
+ if (haskey(_kp, v.id)) {
783
+ //在find結果內代表須刪除
784
+ await client.del(v.id)
785
+ }
786
+ }
787
+
788
+ }
789
+ }
790
+ else {
791
+
792
+ //nDel
793
+ nDel = nAll
794
+
795
+ //empty
796
+ for (let v of ltdt) {
797
+ await client.del(v.id)
798
+ }
799
+
800
+ }
801
+
802
+ //res, n為實際刪除筆數而非全表筆數, 故與nDeleted同值
803
+ res = {
804
+ n: nDel,
805
+ nDeleted: nDel,
806
+ ok: 1,
807
+ }
808
+
809
+ }
810
+ catch (err) {
811
+ isErr = true
812
+ res = err
813
+ }
814
+
815
+ //update, 不能保證刪除多少, 一律重設快取
816
+ _cache = null
817
+
818
+ //emit, 於change可能須使用select, 故須放在重設快取之後
819
+ if (!isErr) {
820
+ try {
821
+
822
+ //emit
823
+ ee.emit('change', 'delAll', null, res)
824
+
825
+ }
826
+ catch (err) {
827
+ console.log(err)
828
+ }
829
+ }
830
+
831
+ //check
832
+ if (isErr) {
833
+ return Promise.reject(res)
834
+ }
835
+
836
+ return res
837
+ }
838
+
839
+ //close, 關閉內部LMDB env並釋放檔案鎖, close後該實例即報廢, 須重新new WOrmLmdb
840
+ let close = async () => {
841
+ if (client &amp;&amp; client.status !== 'closed') {
842
+ await client.close() //lmdb root database之close(), 回傳Promise
843
+ }
844
+ }
845
+
846
+ //save
847
+ ee.select = select
848
+ ee.selectById = selectById
849
+ ee.insert = insert
850
+ ee.save = save
851
+ ee.del = del
852
+ ee.delAll = delAll
853
+ ee.close = close
854
+
855
+ return ee
856
+ }
857
+
858
+
859
+ export default WOrmLmdb
705
860
  </code></pre>
706
861
  </article>
707
862
  </section>
@@ -716,7 +871,7 @@ export default WOrmLmdb
716
871
  <br class="clear">
717
872
 
718
873
  <footer>
719
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 14:28:51 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
874
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Aug 16 2026 21:38:34 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
720
875
  </footer>
721
876
 
722
877
  <script>prettyPrint();</script>