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.
- package/README.md +47 -4
- 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 +169 -11
- package/docs/WOrmLmdb.mjs.html +814 -659
- package/docs/index.html +2 -2
- package/g-basic.mjs +14 -4
- package/package.json +1 -1
- package/src/WOrmLmdb.mjs +812 -657
- package/test/{basic.test.mjs → unit-basic.test.mjs} +55 -14
- package/test/{cache.test.mjs → 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 +171 -0
- package/test/unit-save.test.mjs +254 -0
- 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 +283 -0
- package/_db_cache/worm/users/data.mdb +0 -0
- package/_db_cache/worm/users/lock.mdb +0 -0
|
@@ -110,17 +110,17 @@ describe('basic', function() {
|
|
|
110
110
|
//save
|
|
111
111
|
rt = null
|
|
112
112
|
// vans[3] = [
|
|
113
|
-
// { n: 1, nModified: 1, ok: 1 },
|
|
114
|
-
// { n: 1, nModified: 1, ok: 1 },
|
|
115
|
-
// { n: 0, nModified: 0, ok: 1 }
|
|
113
|
+
// { n: 1, nInserted: 0, nModified: 1, ok: 1 },
|
|
114
|
+
// { n: 1, nInserted: 0, nModified: 1, ok: 1 },
|
|
115
|
+
// { n: 0, nInserted: 0, nModified: 0, ok: 1 }
|
|
116
116
|
// ]
|
|
117
117
|
await wo.save(rsm, { autoInsert: false })
|
|
118
118
|
.then(function(msg) {
|
|
119
119
|
// console.log('save then', msg)
|
|
120
120
|
// save then [
|
|
121
|
-
// { n: 1, nModified: 1, ok: 1 },
|
|
122
|
-
// { n: 1, nModified: 1, ok: 1 },
|
|
123
|
-
// { n: 0, nModified: 0, ok: 1 }
|
|
121
|
+
// { n: 1, nInserted: 0, nModified: 1, ok: 1 },
|
|
122
|
+
// { n: 1, nInserted: 0, nModified: 1, ok: 1 },
|
|
123
|
+
// { n: 0, nInserted: 0, nModified: 0, ok: 1 }
|
|
124
124
|
// ]
|
|
125
125
|
rt = msg
|
|
126
126
|
})
|
|
@@ -184,6 +184,36 @@ describe('basic', function() {
|
|
|
184
184
|
})
|
|
185
185
|
vget[5] = rt
|
|
186
186
|
|
|
187
|
+
//selectById
|
|
188
|
+
rt = null
|
|
189
|
+
// vans[12] = { id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 }
|
|
190
|
+
await wo.selectById('id-rosemary')
|
|
191
|
+
.then(function(msg) {
|
|
192
|
+
// console.log('selectById then', msg)
|
|
193
|
+
// selectById { id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 }
|
|
194
|
+
rt = msg
|
|
195
|
+
})
|
|
196
|
+
.catch(function(msg) {
|
|
197
|
+
// console.log('selectById catch', msg)
|
|
198
|
+
rt = msg.toString()
|
|
199
|
+
})
|
|
200
|
+
vget[12] = rt
|
|
201
|
+
|
|
202
|
+
//selectById by id not existed
|
|
203
|
+
rt = null
|
|
204
|
+
// vans[13] = null
|
|
205
|
+
await wo.selectById('id-not-existed')
|
|
206
|
+
.then(function(msg) {
|
|
207
|
+
// console.log('selectById by id not existed then', msg)
|
|
208
|
+
// selectById by id not existed null
|
|
209
|
+
rt = msg
|
|
210
|
+
})
|
|
211
|
+
.catch(function(msg) {
|
|
212
|
+
// console.log('selectById by id not existed catch', msg)
|
|
213
|
+
rt = msg.toString()
|
|
214
|
+
})
|
|
215
|
+
vget[13] = rt
|
|
216
|
+
|
|
187
217
|
//select by $and, $gt, $lt
|
|
188
218
|
rt = null
|
|
189
219
|
// vans[6] = [{ id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 }]
|
|
@@ -292,14 +322,14 @@ describe('basic', function() {
|
|
|
292
322
|
|
|
293
323
|
//save
|
|
294
324
|
rt = null
|
|
295
|
-
// vans[10] = [ { n: 1, nModified: 1, ok: 1 } ]
|
|
325
|
+
// vans[10] = [ { n: 1, nInserted: 0, nModified: 1, ok: 1 } ]
|
|
296
326
|
await wo.save(rsa, { autoInsert: true })
|
|
297
327
|
.then(function(msg) {
|
|
298
328
|
// console.log('save then', msg)
|
|
299
329
|
// save then [
|
|
300
|
-
// { n: 1, nModified: 1, ok: 1 },
|
|
301
|
-
// { n: 1, nModified: 1, ok: 1 },
|
|
302
|
-
// { n: 0, nModified: 0, ok: 1 }
|
|
330
|
+
// { n: 1, nInserted: 0, nModified: 1, ok: 1 },
|
|
331
|
+
// { n: 1, nInserted: 0, nModified: 1, ok: 1 },
|
|
332
|
+
// { n: 0, nInserted: 0, nModified: 0, ok: 1 }
|
|
303
333
|
// ]
|
|
304
334
|
rt = msg
|
|
305
335
|
})
|
|
@@ -344,9 +374,9 @@ describe('basic', function() {
|
|
|
344
374
|
})
|
|
345
375
|
|
|
346
376
|
vans[3] = [
|
|
347
|
-
{ n: 1, nModified: 1, ok: 1 },
|
|
348
|
-
{ n: 1, nModified: 1, ok: 1 },
|
|
349
|
-
{ n: 0, nModified: 0, ok: 1 }
|
|
377
|
+
{ n: 1, nInserted: 0, nModified: 1, ok: 1 },
|
|
378
|
+
{ n: 1, nInserted: 0, nModified: 1, ok: 1 },
|
|
379
|
+
{ n: 0, nInserted: 0, nModified: 0, ok: 1 }
|
|
350
380
|
]
|
|
351
381
|
it(`should get ${JSON.stringify(vans[3])} for save(autoInsert=false)`, async function() {
|
|
352
382
|
assert.strict.deepStrictEqual(vget[3], vans[3])
|
|
@@ -370,6 +400,17 @@ describe('basic', function() {
|
|
|
370
400
|
assert.strict.deepStrictEqual(vget[5], vans[5])
|
|
371
401
|
})
|
|
372
402
|
|
|
403
|
+
//vans[12]、vans[13]為後續新增之selectById, 為不更動既有編號故接續於末號之後
|
|
404
|
+
vans[12] = { id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 }
|
|
405
|
+
it(`should get ${JSON.stringify(vans[12])} for selectById`, async function() {
|
|
406
|
+
assert.strict.deepStrictEqual(vget[12], vans[12])
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
vans[13] = null
|
|
410
|
+
it(`should get ${JSON.stringify(vans[13])} for selectById by id not existed`, async function() {
|
|
411
|
+
assert.strict.deepStrictEqual(vget[13], vans[13])
|
|
412
|
+
})
|
|
413
|
+
|
|
373
414
|
vans[6] = [{ id: 'id-rosemary', name: 'rosemary(modify)', value: 123.456 }]
|
|
374
415
|
it(`should get ${JSON.stringify(vans[6])} for select by $and, $gt, $lt`, async function() {
|
|
375
416
|
assert.strict.deepStrictEqual(vget[6], vans[6])
|
|
@@ -407,7 +448,7 @@ describe('basic', function() {
|
|
|
407
448
|
// assert.strict.deepStrictEqual(vget[9], vans[9])
|
|
408
449
|
// })
|
|
409
450
|
|
|
410
|
-
vans[10] = [{ n: 1, nModified: 1, ok: 1 }]
|
|
451
|
+
vans[10] = [{ n: 1, nInserted: 0, nModified: 1, ok: 1 }]
|
|
411
452
|
it(`should get ${JSON.stringify(vans[10])} for save(autoInsert=true)`, async function() {
|
|
412
453
|
assert.strict.deepStrictEqual(vget[10], vans[10])
|
|
413
454
|
})
|
|
@@ -100,7 +100,7 @@ describe('cache', function() {
|
|
|
100
100
|
|
|
101
101
|
//save (更新rosemary, 觸發快取重設)
|
|
102
102
|
rt = null
|
|
103
|
-
// vans[3] = [{ n: 1, nModified: 1, ok: 1 }]
|
|
103
|
+
// vans[3] = [{ n: 1, nInserted: 0, nModified: 1, ok: 1 }]
|
|
104
104
|
await wo.save(rsm, { autoInsert: false })
|
|
105
105
|
.then(function(msg) {
|
|
106
106
|
// console.log('save then', msg)
|
|
@@ -151,7 +151,7 @@ describe('cache', function() {
|
|
|
151
151
|
assert.strict.deepStrictEqual(vget[2], vans[2])
|
|
152
152
|
})
|
|
153
153
|
|
|
154
|
-
vans[3] = [{ n: 1, nModified: 1, ok: 1 }]
|
|
154
|
+
vans[3] = [{ n: 1, nInserted: 0, nModified: 1, ok: 1 }]
|
|
155
155
|
it(`should get ${JSON.stringify(vans[3])} for save (invalidate cache)`, async function() {
|
|
156
156
|
assert.strict.deepStrictEqual(vget[3], vans[3])
|
|
157
157
|
})
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import _ from 'lodash-es'
|
|
3
|
+
import w from 'wsemi'
|
|
4
|
+
import WOrm from '../src/WOrmLmdb.mjs'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
describe('del', function() {
|
|
8
|
+
let rt = null
|
|
9
|
+
let vans = {}
|
|
10
|
+
let vget = {}
|
|
11
|
+
|
|
12
|
+
before(async function() {
|
|
13
|
+
|
|
14
|
+
w.fsDeleteFolder('./_db_del')
|
|
15
|
+
|
|
16
|
+
let url = './_db_del'
|
|
17
|
+
|
|
18
|
+
let wo = WOrm({ url, db: 'worm', cl: 'users' })
|
|
19
|
+
await wo.insert([
|
|
20
|
+
{ id: 'd1', name: 'peter' },
|
|
21
|
+
{ id: 'd2', name: 'rosemary' },
|
|
22
|
+
])
|
|
23
|
+
|
|
24
|
+
//主鍵命中並刪除
|
|
25
|
+
rt = null
|
|
26
|
+
// vans[1] = [{ n: 1, nDeleted: 1, ok: 1 }]
|
|
27
|
+
await wo.del({ id: 'd1' })
|
|
28
|
+
.then(function(msg) {
|
|
29
|
+
// console.log('del then', msg)
|
|
30
|
+
// del then [ { n: 1, nDeleted: 1, ok: 1 } ]
|
|
31
|
+
rt = msg
|
|
32
|
+
})
|
|
33
|
+
.catch(function(msg) {
|
|
34
|
+
// console.log('del catch', msg)
|
|
35
|
+
rt = msg.toString()
|
|
36
|
+
})
|
|
37
|
+
vget[1] = rt
|
|
38
|
+
|
|
39
|
+
//主鍵未命中, 非錯誤故ok為1, 未命中故n為0
|
|
40
|
+
rt = null
|
|
41
|
+
// vans[2] = [{ n: 0, nDeleted: 0, ok: 1 }]
|
|
42
|
+
await wo.del({ id: 'd-not-existed' })
|
|
43
|
+
.then(function(msg) {
|
|
44
|
+
rt = msg
|
|
45
|
+
})
|
|
46
|
+
.catch(function(msg) {
|
|
47
|
+
rt = msg.toString()
|
|
48
|
+
})
|
|
49
|
+
vget[2] = rt
|
|
50
|
+
|
|
51
|
+
//未給有效主鍵, 屬該筆數據有問題故ok為0且須附err
|
|
52
|
+
rt = null
|
|
53
|
+
await wo.del({ name: 'no-id' })
|
|
54
|
+
.then(function(msg) {
|
|
55
|
+
rt = msg
|
|
56
|
+
})
|
|
57
|
+
.catch(function(msg) {
|
|
58
|
+
rt = msg.toString()
|
|
59
|
+
})
|
|
60
|
+
vget[3] = _.map(rt, function(v) {
|
|
61
|
+
return _.omit(v, 'err')
|
|
62
|
+
})
|
|
63
|
+
vget[4] = w.isestr(_.get(rt, '0.err'))
|
|
64
|
+
|
|
65
|
+
//整批含[未命中]與[未給有效主鍵], 單筆問題不中斷整批, 陣列長度須與輸入等長
|
|
66
|
+
rt = null
|
|
67
|
+
await wo.del([{ id: 'd2' }, { id: 'd-not-existed' }, { name: 'no-id' }])
|
|
68
|
+
.then(function(msg) {
|
|
69
|
+
rt = msg
|
|
70
|
+
})
|
|
71
|
+
.catch(function(msg) {
|
|
72
|
+
rt = msg.toString()
|
|
73
|
+
})
|
|
74
|
+
vget[5] = _.map(rt, function(v) {
|
|
75
|
+
return _.omit(v, 'err')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
//刪除後剩餘筆數
|
|
79
|
+
vget[6] = _.size(await wo.select())
|
|
80
|
+
|
|
81
|
+
//輸入非有效物件或陣列
|
|
82
|
+
vget[7] = await wo.del(null)
|
|
83
|
+
|
|
84
|
+
await wo.close()
|
|
85
|
+
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
vans[1] = [{ n: 1, nDeleted: 1, ok: 1 }]
|
|
89
|
+
it(`should get ${JSON.stringify(vans[1])} for del with id existed`, async function() {
|
|
90
|
+
assert.strict.deepStrictEqual(vget[1], vans[1])
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
vans[2] = [{ n: 0, nDeleted: 0, ok: 1 }]
|
|
94
|
+
it(`should get ${JSON.stringify(vans[2])} for del with id not existed`, async function() {
|
|
95
|
+
assert.strict.deepStrictEqual(vget[2], vans[2])
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
vans[3] = [{ n: 0, nDeleted: 0, ok: 0 }]
|
|
99
|
+
it(`should get ${JSON.stringify(vans[3])} for del without valid id`, async function() {
|
|
100
|
+
assert.strict.deepStrictEqual(vget[3], vans[3])
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
vans[4] = true
|
|
104
|
+
it(`should get ${JSON.stringify(vans[4])} for err message existed in del without valid id`, async function() {
|
|
105
|
+
assert.strict.deepStrictEqual(vget[4], vans[4])
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
vans[5] = [
|
|
109
|
+
{ n: 1, nDeleted: 1, ok: 1 },
|
|
110
|
+
{ n: 0, nDeleted: 0, ok: 1 },
|
|
111
|
+
{ n: 0, nDeleted: 0, ok: 0 },
|
|
112
|
+
]
|
|
113
|
+
it(`should get ${JSON.stringify(vans[5])} for del by array mixing valid and invalid items`, async function() {
|
|
114
|
+
assert.strict.deepStrictEqual(vget[5], vans[5])
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
vans[6] = 0
|
|
118
|
+
it(`should get ${JSON.stringify(vans[6])} for records after del`, async function() {
|
|
119
|
+
assert.strict.deepStrictEqual(vget[6], vans[6])
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
vans[7] = []
|
|
123
|
+
it(`should get ${JSON.stringify(vans[7])} for del with invalid input`, async function() {
|
|
124
|
+
assert.strict.deepStrictEqual(vget[7], vans[7])
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
})
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import w from 'wsemi'
|
|
3
|
+
import WOrm from '../src/WOrmLmdb.mjs'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
describe('delAll', function() {
|
|
7
|
+
let rt = null
|
|
8
|
+
let vans = {}
|
|
9
|
+
let vget = {}
|
|
10
|
+
|
|
11
|
+
before(async function() {
|
|
12
|
+
|
|
13
|
+
w.fsDeleteFolder('./_db_delall')
|
|
14
|
+
|
|
15
|
+
let url = './_db_delall'
|
|
16
|
+
|
|
17
|
+
let wo = WOrm({ url, db: 'worm', cl: 'users' })
|
|
18
|
+
await wo.insert([
|
|
19
|
+
{ id: 'x1', g: 'A' },
|
|
20
|
+
{ id: 'x2', g: 'A' },
|
|
21
|
+
{ id: 'x3', g: 'B' },
|
|
22
|
+
{ id: 'x4', g: 'B' },
|
|
23
|
+
{ id: 'x5', g: 'B' },
|
|
24
|
+
])
|
|
25
|
+
|
|
26
|
+
//delAll帶find且僅部份命中, n須為實際刪除筆數而非全表筆數
|
|
27
|
+
rt = null
|
|
28
|
+
// vans[1] = { n: 2, nDeleted: 2, ok: 1 }
|
|
29
|
+
await wo.delAll({ g: 'A' })
|
|
30
|
+
.then(function(msg) {
|
|
31
|
+
// console.log('delAll(find) then', msg)
|
|
32
|
+
// delAll(find) then { n: 2, nDeleted: 2, ok: 1 }
|
|
33
|
+
rt = msg
|
|
34
|
+
})
|
|
35
|
+
.catch(function(msg) {
|
|
36
|
+
// console.log('delAll(find) catch', msg)
|
|
37
|
+
rt = msg.toString()
|
|
38
|
+
})
|
|
39
|
+
vget[1] = rt
|
|
40
|
+
|
|
41
|
+
//刪除後剩餘筆數
|
|
42
|
+
vget[2] = await wo.select().then(function(msg) {
|
|
43
|
+
return msg.length
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
//delAll帶find但無命中
|
|
47
|
+
rt = null
|
|
48
|
+
// vans[3] = { n: 0, nDeleted: 0, ok: 1 }
|
|
49
|
+
await wo.delAll({ g: 'not-existed' })
|
|
50
|
+
.then(function(msg) {
|
|
51
|
+
rt = msg
|
|
52
|
+
})
|
|
53
|
+
.catch(function(msg) {
|
|
54
|
+
rt = msg.toString()
|
|
55
|
+
})
|
|
56
|
+
vget[3] = rt
|
|
57
|
+
|
|
58
|
+
//delAll不帶find, 全部刪除
|
|
59
|
+
rt = null
|
|
60
|
+
// vans[4] = { n: 3, nDeleted: 3, ok: 1 }
|
|
61
|
+
await wo.delAll()
|
|
62
|
+
.then(function(msg) {
|
|
63
|
+
rt = msg
|
|
64
|
+
})
|
|
65
|
+
.catch(function(msg) {
|
|
66
|
+
rt = msg.toString()
|
|
67
|
+
})
|
|
68
|
+
vget[4] = rt
|
|
69
|
+
|
|
70
|
+
vget[5] = await wo.select().then(function(msg) {
|
|
71
|
+
return msg.length
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
//空表再delAll
|
|
75
|
+
rt = null
|
|
76
|
+
// vans[6] = { n: 0, nDeleted: 0, ok: 1 }
|
|
77
|
+
await wo.delAll()
|
|
78
|
+
.then(function(msg) {
|
|
79
|
+
rt = msg
|
|
80
|
+
})
|
|
81
|
+
.catch(function(msg) {
|
|
82
|
+
rt = msg.toString()
|
|
83
|
+
})
|
|
84
|
+
vget[6] = rt
|
|
85
|
+
|
|
86
|
+
await wo.close()
|
|
87
|
+
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
//n為實際刪除筆數, 與nDeleted同值, 不受全表筆數影響
|
|
91
|
+
vans[1] = { n: 2, nDeleted: 2, ok: 1 }
|
|
92
|
+
it(`should get ${JSON.stringify(vans[1])} for delAll(find) matching 2 of 5`, async function() {
|
|
93
|
+
assert.strict.deepStrictEqual(vget[1], vans[1])
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
vans[2] = 3
|
|
97
|
+
it(`should get ${JSON.stringify(vans[2])} for records after delAll(find)`, async function() {
|
|
98
|
+
assert.strict.deepStrictEqual(vget[2], vans[2])
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
vans[3] = { n: 0, nDeleted: 0, ok: 1 }
|
|
102
|
+
it(`should get ${JSON.stringify(vans[3])} for delAll(find) matching none`, async function() {
|
|
103
|
+
assert.strict.deepStrictEqual(vget[3], vans[3])
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
vans[4] = { n: 3, nDeleted: 3, ok: 1 }
|
|
107
|
+
it(`should get ${JSON.stringify(vans[4])} for delAll without find`, async function() {
|
|
108
|
+
assert.strict.deepStrictEqual(vget[4], vans[4])
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
vans[5] = 0
|
|
112
|
+
it(`should get ${JSON.stringify(vans[5])} for records after delAll without find`, async function() {
|
|
113
|
+
assert.strict.deepStrictEqual(vget[5], vans[5])
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
vans[6] = { n: 0, nDeleted: 0, ok: 1 }
|
|
117
|
+
it(`should get ${JSON.stringify(vans[6])} for delAll on empty collection`, async function() {
|
|
118
|
+
assert.strict.deepStrictEqual(vget[6], vans[6])
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
})
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import { spawn } from 'child_process'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import _ from 'lodash-es'
|
|
5
|
+
import w from 'wsemi'
|
|
6
|
+
import WOrm from '../src/WOrmLmdb.mjs'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
//runProc, 另起行程對同一資料表插入相同id, 用於驗證跨行程之插入原子性
|
|
10
|
+
function runProc(tag, url, ids) {
|
|
11
|
+
return new Promise(function(resolve) {
|
|
12
|
+
let code = `
|
|
13
|
+
import { pathToFileURL } from 'url'
|
|
14
|
+
let { default: WOrm } = await import(pathToFileURL(process.env.SRC).href)
|
|
15
|
+
let wo = WOrm({ url: process.env.URL, db: 'worm', cl: 'race' })
|
|
16
|
+
let ids = JSON.parse(process.env.IDS)
|
|
17
|
+
let n = 0
|
|
18
|
+
for (let id of ids) {
|
|
19
|
+
let r = await wo.insert({ id, from: process.env.TAG })
|
|
20
|
+
n += r.nInserted
|
|
21
|
+
}
|
|
22
|
+
console.log(JSON.stringify({ tag: process.env.TAG, nInserted: n }))
|
|
23
|
+
await wo.close()
|
|
24
|
+
`
|
|
25
|
+
let out = ''
|
|
26
|
+
let p = spawn(process.execPath, ['--input-type=module', '-e', code], {
|
|
27
|
+
shell: false,
|
|
28
|
+
env: {
|
|
29
|
+
...process.env,
|
|
30
|
+
SRC: path.resolve('./src/WOrmLmdb.mjs'),
|
|
31
|
+
URL: url,
|
|
32
|
+
IDS: JSON.stringify(ids),
|
|
33
|
+
TAG: tag,
|
|
34
|
+
},
|
|
35
|
+
})
|
|
36
|
+
p.stdout.on('data', function(d) {
|
|
37
|
+
out += d.toString()
|
|
38
|
+
})
|
|
39
|
+
p.stderr.on('data', function(d) {
|
|
40
|
+
out += d.toString()
|
|
41
|
+
})
|
|
42
|
+
p.on('close', function() {
|
|
43
|
+
let r = null
|
|
44
|
+
try {
|
|
45
|
+
r = JSON.parse(_.trim(out))
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
r = { tag, error: _.trim(out) }
|
|
49
|
+
}
|
|
50
|
+
resolve(r)
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
describe('insert', function() {
|
|
57
|
+
let rt = null
|
|
58
|
+
let vans = {}
|
|
59
|
+
let vget = {}
|
|
60
|
+
|
|
61
|
+
before(async function() {
|
|
62
|
+
|
|
63
|
+
w.fsDeleteFolder('./_db_insert')
|
|
64
|
+
|
|
65
|
+
let url = './_db_insert'
|
|
66
|
+
|
|
67
|
+
//insert同批含重複id, 僅首筆應被插入
|
|
68
|
+
rt = null
|
|
69
|
+
// vans[1] = { n: 3, nInserted: 2, ok: 1 }
|
|
70
|
+
let woDup = WOrm({ url, db: 'worm', cl: 'dup' })
|
|
71
|
+
await woDup.insert([
|
|
72
|
+
{ id: 'id-dup', name: 'first' },
|
|
73
|
+
{ id: 'id-dup', name: 'second' },
|
|
74
|
+
{ id: 'id-other', name: 'other' },
|
|
75
|
+
])
|
|
76
|
+
.then(function(msg) {
|
|
77
|
+
// console.log('insert(含重複id) then', msg)
|
|
78
|
+
// insert(含重複id) then { n: 3, nInserted: 2, ok: 1 }
|
|
79
|
+
rt = msg
|
|
80
|
+
})
|
|
81
|
+
.catch(function(msg) {
|
|
82
|
+
// console.log('insert(含重複id) catch', msg)
|
|
83
|
+
rt = msg.toString()
|
|
84
|
+
})
|
|
85
|
+
vget[1] = rt
|
|
86
|
+
|
|
87
|
+
//同批重複id時應保留首筆內容
|
|
88
|
+
vget[2] = await woDup.selectById('id-dup')
|
|
89
|
+
|
|
90
|
+
//對已存在id再insert, 應不插入且不覆寫
|
|
91
|
+
rt = null
|
|
92
|
+
// vans[3] = { n: 1, nInserted: 0, ok: 1 }
|
|
93
|
+
await woDup.insert({ id: 'id-dup', name: 'third' })
|
|
94
|
+
.then(function(msg) {
|
|
95
|
+
rt = msg
|
|
96
|
+
})
|
|
97
|
+
.catch(function(msg) {
|
|
98
|
+
rt = msg.toString()
|
|
99
|
+
})
|
|
100
|
+
vget[3] = rt
|
|
101
|
+
vget[4] = await woDup.selectById('id-dup')
|
|
102
|
+
await woDup.close()
|
|
103
|
+
|
|
104
|
+
//單行程併發: 對同一id併發insert 100次, nInserted總和須為1
|
|
105
|
+
let woPar = WOrm({ url, db: 'worm', cl: 'par' })
|
|
106
|
+
let rs = await Promise.all(_.times(100, function(k) {
|
|
107
|
+
return woPar.insert({ id: 'id-race', k })
|
|
108
|
+
}))
|
|
109
|
+
vget[5] = _.sum(_.map(rs, 'nInserted'))
|
|
110
|
+
vget[6] = _.size(await woPar.select())
|
|
111
|
+
await woPar.close()
|
|
112
|
+
|
|
113
|
+
//跨行程併發: 兩行程各自對同50個id插入, 資料表須恰為50筆而無重複
|
|
114
|
+
//註: 此處不斷言nInserted總和為50, 因lmdb-js之條件寫入於跨行程下並非完全可靠,
|
|
115
|
+
//實測3行程×50id×30回合(CPU負載下)有5回合總和多算1至2, 記錄筆數則始終正確,
|
|
116
|
+
//ifNoExists、transaction、transactionSync三種機制皆有相同現象, 詳見[併發保證]說明
|
|
117
|
+
let ids = _.times(50, function(k) {
|
|
118
|
+
return `id-x${k}`
|
|
119
|
+
})
|
|
120
|
+
await Promise.all([
|
|
121
|
+
runProc('P1', url, ids),
|
|
122
|
+
runProc('P2', url, ids),
|
|
123
|
+
])
|
|
124
|
+
let woX = WOrm({ url, db: 'worm', cl: 'race' })
|
|
125
|
+
vget[7] = _.size(await woX.select())
|
|
126
|
+
vget[8] = _.size(_.uniq(_.map(await woX.select(), 'id')))
|
|
127
|
+
await woX.close()
|
|
128
|
+
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
vans[1] = { n: 3, nInserted: 2, ok: 1 }
|
|
132
|
+
it(`should get ${JSON.stringify(vans[1])} for insert with duplicated id in same batch`, async function() {
|
|
133
|
+
assert.strict.deepStrictEqual(vget[1], vans[1])
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
vans[2] = { id: 'id-dup', name: 'first' }
|
|
137
|
+
it(`should get ${JSON.stringify(vans[2])} for keeping first one of duplicated id`, async function() {
|
|
138
|
+
assert.strict.deepStrictEqual(vget[2], vans[2])
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
vans[3] = { n: 1, nInserted: 0, ok: 1 }
|
|
142
|
+
it(`should get ${JSON.stringify(vans[3])} for insert existed id`, async function() {
|
|
143
|
+
assert.strict.deepStrictEqual(vget[3], vans[3])
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
vans[4] = { id: 'id-dup', name: 'first' }
|
|
147
|
+
it(`should get ${JSON.stringify(vans[4])} for not overwriting existed id`, async function() {
|
|
148
|
+
assert.strict.deepStrictEqual(vget[4], vans[4])
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
vans[5] = 1
|
|
152
|
+
it(`should get ${JSON.stringify(vans[5])} for sum of nInserted by 100 concurrent insert with same id`, async function() {
|
|
153
|
+
assert.strict.deepStrictEqual(vget[5], vans[5])
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
vans[6] = 1
|
|
157
|
+
it(`should get ${JSON.stringify(vans[6])} for records after 100 concurrent insert with same id`, async function() {
|
|
158
|
+
assert.strict.deepStrictEqual(vget[6], vans[6])
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
vans[7] = 50
|
|
162
|
+
it(`should get ${JSON.stringify(vans[7])} for records after 2 processes inserting same 50 ids`, async function() {
|
|
163
|
+
assert.strict.deepStrictEqual(vget[7], vans[7])
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
vans[8] = 50
|
|
167
|
+
it(`should get ${JSON.stringify(vans[8])} for distinct ids after 2 processes inserting same 50 ids`, async function() {
|
|
168
|
+
assert.strict.deepStrictEqual(vget[8], vans[8])
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
})
|