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.
@@ -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
+ })