w-orm-postgresql 1.0.7 → 1.0.8
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 +443 -428
- package/dist/w-orm-postgresql.umd.js +2 -2
- package/dist/w-orm-postgresql.umd.js.map +1 -1
- package/docs/WOrmPostgresql.html +167 -6
- package/docs/WOrmPostgresql.mjs.html +247 -62
- package/docs/index.html +2 -2
- package/g-basic.mjs +15 -1
- package/package.json +2 -2
- package/src/WOrmPostgresql.mjs +245 -60
- package/test/{basic.test.mjs → api-postgresql.test.mjs} +463 -21
package/package.json
CHANGED
package/src/WOrmPostgresql.mjs
CHANGED
|
@@ -367,6 +367,118 @@ function WOrmPostgresql(opt = {}) {
|
|
|
367
367
|
return res
|
|
368
368
|
}
|
|
369
369
|
|
|
370
|
+
/**
|
|
371
|
+
* 由主鍵time查詢單筆數據,因直接由資料表主鍵索引取值,不需如select提取數據至前端再處理,故數據量大時效能較佳
|
|
372
|
+
*
|
|
373
|
+
* @memberOf WOrmPostgresql
|
|
374
|
+
* @param {String|Date} time 輸入主鍵time值,本套件以time欄位為主鍵
|
|
375
|
+
* @returns {Promise} 回傳Promise,resolve回傳數據物件,若無此time則回傳null,reject回傳錯誤訊息
|
|
376
|
+
*/
|
|
377
|
+
async function selectByTime(time) {
|
|
378
|
+
let isErr = false
|
|
379
|
+
let res = null
|
|
380
|
+
|
|
381
|
+
//check
|
|
382
|
+
if (!isDate(time)) {
|
|
383
|
+
//未給有效主鍵值視為查無數據, 判定基準與insert、save、del內對time之認定一致
|
|
384
|
+
return null
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
//find, 本套件以time欄位為主鍵, 與save、del、genConflictSQL之認定一致
|
|
388
|
+
let find = {
|
|
389
|
+
time,
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
//cache, 與select共用快取, 因快取鍵值由find與order組成, 故此處等同select(find)之結果
|
|
393
|
+
if (useCache) {
|
|
394
|
+
let cache = getCache(find, {})
|
|
395
|
+
if (isarr(cache)) {
|
|
396
|
+
return get(cache, 0, null)
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
//client
|
|
401
|
+
let client = new PgClient({ connectionString })
|
|
402
|
+
|
|
403
|
+
//connect
|
|
404
|
+
try {
|
|
405
|
+
|
|
406
|
+
//connect
|
|
407
|
+
await client.connect()
|
|
408
|
+
|
|
409
|
+
}
|
|
410
|
+
catch (err) {
|
|
411
|
+
isErr = true
|
|
412
|
+
res = err
|
|
413
|
+
client = null
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
//check
|
|
417
|
+
if (isErr) {
|
|
418
|
+
return Promise.reject(res)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
try {
|
|
422
|
+
|
|
423
|
+
//mr, 不使用limit以令結果與select(find)完全等價, 方能安全共用快取,
|
|
424
|
+
//time為主鍵時本就僅回傳1筆, 不會因此多拉數據
|
|
425
|
+
let mr = mongoSql.sql({
|
|
426
|
+
type: 'select',
|
|
427
|
+
table: cl,
|
|
428
|
+
where: find,
|
|
429
|
+
})
|
|
430
|
+
// console.log('mr', mr)
|
|
431
|
+
// console.log('mr.query', mr.query)
|
|
432
|
+
// console.log('mr.values', mr.values)
|
|
433
|
+
|
|
434
|
+
//select
|
|
435
|
+
let r = await client.query(mr.query, mr.values)
|
|
436
|
+
// console.log('r', r)
|
|
437
|
+
|
|
438
|
+
//rows
|
|
439
|
+
let rows = get(r, 'rows')
|
|
440
|
+
|
|
441
|
+
//check
|
|
442
|
+
if (!isarr(rows)) {
|
|
443
|
+
throw new Error(`can not select by time[${time}]`)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
//cache
|
|
447
|
+
if (useCache) {
|
|
448
|
+
setCache(find, {}, rows)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
//v
|
|
452
|
+
let v = get(rows, 0, null)
|
|
453
|
+
|
|
454
|
+
//check, 判定基準與save、del內對既有數據之認定一致
|
|
455
|
+
if (iseobj(v)) {
|
|
456
|
+
res = v
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
//不存在time, 回傳null
|
|
460
|
+
res = null
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
}
|
|
464
|
+
catch (err) {
|
|
465
|
+
isErr = true
|
|
466
|
+
res = err
|
|
467
|
+
}
|
|
468
|
+
finally {
|
|
469
|
+
await client.end()
|
|
470
|
+
client = null
|
|
471
|
+
}
|
|
472
|
+
// console.log('res', res)
|
|
473
|
+
|
|
474
|
+
//check
|
|
475
|
+
if (isErr) {
|
|
476
|
+
return Promise.reject(res)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return res
|
|
480
|
+
}
|
|
481
|
+
|
|
370
482
|
/**
|
|
371
483
|
* 插入數據
|
|
372
484
|
*
|
|
@@ -436,18 +548,22 @@ function WOrmPostgresql(opt = {}) {
|
|
|
436
548
|
// console.log('mr.query', mr.query)
|
|
437
549
|
// console.log('mr.values', mr.values)
|
|
438
550
|
|
|
439
|
-
|
|
551
|
+
//添加conflict, 令已存在time者跳過而不中斷整批插入, 主鍵認定與genConflictSQL一致
|
|
552
|
+
//由PostgreSQL於單一語句內原子完成[檢查time未存在]與[寫入], 併發時同一time僅有一次成功,
|
|
553
|
+
//同批含重複time時亦僅首筆成功, 故不須逐筆插入即可取得實際插入筆數
|
|
554
|
+
let sql = `${mr.query} ON CONFLICT (time) DO NOTHING`
|
|
555
|
+
|
|
556
|
+
//nAll
|
|
440
557
|
let nAll = size(data)
|
|
441
|
-
let nInsert = nAll //一次插入全部數據加速, 但也因此沒法個別處理conflict, 無法個別計算已插入數量
|
|
442
558
|
|
|
443
|
-
//insert
|
|
444
|
-
await client.query(
|
|
445
|
-
.then(() => {
|
|
559
|
+
//insert, ON CONFLICT DO NOTHING時rowCount即為實際插入筆數
|
|
560
|
+
await client.query(sql, mr.values)
|
|
561
|
+
.then((r) => {
|
|
446
562
|
|
|
447
563
|
//res
|
|
448
564
|
res = {
|
|
449
565
|
n: nAll,
|
|
450
|
-
nInserted:
|
|
566
|
+
nInserted: r.rowCount,
|
|
451
567
|
ok: 1,
|
|
452
568
|
}
|
|
453
569
|
|
|
@@ -588,9 +704,10 @@ function WOrmPostgresql(opt = {}) {
|
|
|
588
704
|
|
|
589
705
|
}
|
|
590
706
|
|
|
591
|
-
|
|
707
|
+
//快速路徑, 先不寫入直接由前述預讀判斷內容是否相同, 內容相同者本就不須寫入,
|
|
708
|
+
//預讀值縱使已被其他寫入者更動而過期亦不影響正確性, 因內容相同時本次save等價於無操作,
|
|
709
|
+
//無操作可視為於預讀當下即已完成, 其後他人之寫入結果與[本次save先執行再輪到他人]相同
|
|
592
710
|
if (iseobj(_v)) {
|
|
593
|
-
//存在
|
|
594
711
|
|
|
595
712
|
//_vt
|
|
596
713
|
let _vt = omit(_v, 'time')
|
|
@@ -611,75 +728,125 @@ function WOrmPostgresql(opt = {}) {
|
|
|
611
728
|
}
|
|
612
729
|
// console.log('相同時不更新', rest, v)
|
|
613
730
|
|
|
731
|
+
return rest
|
|
614
732
|
}
|
|
615
|
-
else {
|
|
616
|
-
//不相同時須更新
|
|
617
|
-
|
|
618
|
-
//mr
|
|
619
|
-
let mr = mongoSql.sql({
|
|
620
|
-
type: 'insert',
|
|
621
|
-
table: cl,
|
|
622
|
-
values: v,
|
|
623
|
-
})
|
|
624
|
-
// console.log('mr', mr)
|
|
625
|
-
// console.log('mr.query', mr.query)
|
|
626
|
-
// console.log('mr.values', mr.values)
|
|
627
733
|
|
|
628
|
-
|
|
629
|
-
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
|
|
737
|
+
//check
|
|
738
|
+
if (!autoInsert) {
|
|
739
|
+
//不存在且未開啟autoInsert則不寫入
|
|
740
|
+
|
|
741
|
+
//rest
|
|
742
|
+
rest = {
|
|
743
|
+
n: 0,
|
|
744
|
+
nModified: 0,
|
|
745
|
+
ok: 1,
|
|
746
|
+
}
|
|
747
|
+
// console.log('不存在', rest, v)
|
|
748
|
+
|
|
749
|
+
return rest
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
//check
|
|
755
|
+
if (autoInsert) {
|
|
756
|
+
//由PostgreSQL於單一語句內原子完成[查找time]與[插入或更新],
|
|
757
|
+
//併發時不會因他方已先插入同一time而報錯, 亦不會有兩方各自讀到同一舊值再各自寫入
|
|
758
|
+
|
|
759
|
+
//mr
|
|
760
|
+
let mr = mongoSql.sql({
|
|
761
|
+
type: 'insert',
|
|
762
|
+
table: cl,
|
|
763
|
+
values: v,
|
|
764
|
+
})
|
|
765
|
+
// console.log('mr', mr)
|
|
766
|
+
// console.log('mr.query', mr.query)
|
|
767
|
+
// console.log('mr.values', mr.values)
|
|
630
768
|
|
|
631
|
-
|
|
632
|
-
|
|
769
|
+
//添加conflict, 僅有time欄位時genConflictSQL無可更新欄位而回傳空字串,
|
|
770
|
+
//此時退回DO NOTHING, 以免併發插入同一time者報錯
|
|
771
|
+
let conflict = genConflictSQL(v)
|
|
772
|
+
if (!isestr(conflict)) {
|
|
773
|
+
conflict = 'ON CONFLICT (time) DO NOTHING'
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
//sql, xmax為0表該列由本語句插入, 非0表係由DO UPDATE更新而來,
|
|
777
|
+
//衝突且落入DO NOTHING時不回傳任何列, 代表該time已存在且未變更
|
|
778
|
+
let sql = `${mr.query} ${conflict} RETURNING (xmax = 0) AS inserted`
|
|
633
779
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
780
|
+
//save
|
|
781
|
+
await client.query(sql, mr.values)
|
|
782
|
+
.then((r) => {
|
|
783
|
+
|
|
784
|
+
//rows
|
|
785
|
+
let rows = get(r, 'rows')
|
|
786
|
+
|
|
787
|
+
//check
|
|
788
|
+
if (!isearr(rows)) {
|
|
789
|
+
//衝突且未更新
|
|
637
790
|
|
|
638
791
|
//rest
|
|
639
792
|
rest = {
|
|
640
793
|
n: 1,
|
|
641
|
-
nModified:
|
|
794
|
+
nModified: 0,
|
|
642
795
|
ok: 1,
|
|
643
796
|
}
|
|
644
|
-
// console.log('不相同時須更新', rest, vt)
|
|
645
797
|
|
|
646
|
-
}
|
|
647
|
-
|
|
798
|
+
}
|
|
799
|
+
else if (get(rows, '0.inserted') === true) {
|
|
800
|
+
//原不存在而插入, 回傳形狀同insert
|
|
648
801
|
|
|
649
802
|
//rest
|
|
650
803
|
rest = {
|
|
651
804
|
n: 1,
|
|
652
|
-
|
|
653
|
-
ok:
|
|
654
|
-
err: err.message,
|
|
805
|
+
nInserted: 1,
|
|
806
|
+
ok: 1,
|
|
655
807
|
}
|
|
808
|
+
// console.log('之前不存在且可自動插入', rest, v)
|
|
656
809
|
|
|
657
|
-
}
|
|
810
|
+
}
|
|
811
|
+
else {
|
|
812
|
+
//原已存在而更新
|
|
658
813
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
814
|
+
//rest
|
|
815
|
+
rest = {
|
|
816
|
+
n: 1,
|
|
817
|
+
nModified: 1,
|
|
818
|
+
ok: 1,
|
|
819
|
+
}
|
|
820
|
+
// console.log('不相同時須更新', rest, v)
|
|
663
821
|
|
|
664
|
-
|
|
665
|
-
rest = {
|
|
666
|
-
n: 0,
|
|
667
|
-
nModified: 0,
|
|
668
|
-
ok: 1,
|
|
669
|
-
}
|
|
670
|
-
// console.log('不存在', rest, v)
|
|
822
|
+
}
|
|
671
823
|
|
|
672
|
-
|
|
824
|
+
})
|
|
825
|
+
.catch((err) => {
|
|
826
|
+
|
|
827
|
+
//rest
|
|
828
|
+
rest = {
|
|
829
|
+
n: 1,
|
|
830
|
+
nInserted: 0,
|
|
831
|
+
ok: 0,
|
|
832
|
+
err: err.message,
|
|
833
|
+
}
|
|
673
834
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
835
|
+
})
|
|
836
|
+
|
|
837
|
+
}
|
|
838
|
+
else {
|
|
839
|
+
//未開啟autoInsert則僅能更新既有數據, 不可用INSERT以免無中生有,
|
|
840
|
+
//由UPDATE...WHERE time原子完成[查找time]與[更新], 併發時不會遺失更新
|
|
677
841
|
|
|
678
842
|
//mr
|
|
679
843
|
let mr = mongoSql.sql({
|
|
680
|
-
type: '
|
|
844
|
+
type: 'update',
|
|
681
845
|
table: cl,
|
|
682
|
-
|
|
846
|
+
updates: omit(v, 'time'),
|
|
847
|
+
where: {
|
|
848
|
+
time: v.time,
|
|
849
|
+
},
|
|
683
850
|
})
|
|
684
851
|
// console.log('mr', mr)
|
|
685
852
|
// console.log('mr.query', mr.query)
|
|
@@ -687,15 +854,32 @@ function WOrmPostgresql(opt = {}) {
|
|
|
687
854
|
|
|
688
855
|
//save
|
|
689
856
|
await client.query(mr.query, mr.values)
|
|
690
|
-
.then(() => {
|
|
857
|
+
.then((r) => {
|
|
858
|
+
|
|
859
|
+
//check
|
|
860
|
+
if (r.rowCount > 0) {
|
|
861
|
+
//已更新
|
|
862
|
+
|
|
863
|
+
//rest
|
|
864
|
+
rest = {
|
|
865
|
+
n: 1,
|
|
866
|
+
nModified: 1,
|
|
867
|
+
ok: 1,
|
|
868
|
+
}
|
|
869
|
+
// console.log('不相同時須更新', rest, v)
|
|
870
|
+
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
//預讀後至更新前已被他人刪除, 視為不存在
|
|
874
|
+
|
|
875
|
+
//rest
|
|
876
|
+
rest = {
|
|
877
|
+
n: 0,
|
|
878
|
+
nModified: 0,
|
|
879
|
+
ok: 1,
|
|
880
|
+
}
|
|
691
881
|
|
|
692
|
-
//rest
|
|
693
|
-
rest = {
|
|
694
|
-
n: 1,
|
|
695
|
-
nInserted: 1,
|
|
696
|
-
ok: 1,
|
|
697
882
|
}
|
|
698
|
-
// console.log('之前不存在且可自動插入', rest, v)
|
|
699
883
|
|
|
700
884
|
})
|
|
701
885
|
.catch((err) => {
|
|
@@ -703,7 +887,7 @@ function WOrmPostgresql(opt = {}) {
|
|
|
703
887
|
//rest
|
|
704
888
|
rest = {
|
|
705
889
|
n: 1,
|
|
706
|
-
|
|
890
|
+
nModified: 0,
|
|
707
891
|
ok: 0,
|
|
708
892
|
err: err.message,
|
|
709
893
|
}
|
|
@@ -996,6 +1180,7 @@ function WOrmPostgresql(opt = {}) {
|
|
|
996
1180
|
//save
|
|
997
1181
|
ee.createTable = createTable
|
|
998
1182
|
ee.select = select
|
|
1183
|
+
ee.selectByTime = selectByTime
|
|
999
1184
|
ee.insert = insert
|
|
1000
1185
|
ee.save = save
|
|
1001
1186
|
ee.del = del
|