datafolder 1.0.0
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 +169 -0
- package/index.js +3 -0
- package/package.json +27 -0
- package/sample.js +37 -0
- package/scripts/airbnb.js +73 -0
- package/scripts/airbnb2.js +74 -0
- package/scripts/airbnb2_queries.js +231 -0
- package/scripts/airbnb_queries.js +264 -0
- package/scripts/downloadFile.js +42 -0
- package/scripts/express.js +46 -0
- package/scripts/fastify.js +79 -0
- package/scripts/flights.js +75 -0
- package/scripts/flights2.js +81 -0
- package/scripts/flights2_queries.js +453 -0
- package/scripts/flights_queries.js +403 -0
- package/src/QyAcl.js +252 -0
- package/src/QyAclSaver.js +14 -0
- package/src/QyAclSaver_Worker.js +29 -0
- package/src/QyAclWriter.js +19 -0
- package/src/QyBatch.js +501 -0
- package/src/QyBinWriter.js +140 -0
- package/src/QyCD.js +61 -0
- package/src/QyCache.js +212 -0
- package/src/QyCompressor.js +60 -0
- package/src/QyCompressor_Worker.js +13 -0
- package/src/QyDB.js +122 -0
- package/src/QyDefaultOptions.js +59 -0
- package/src/QyDir.js +192 -0
- package/src/QyFetch.js +38 -0
- package/src/QyFile.js +124 -0
- package/src/QyFileContentUtils.js +175 -0
- package/src/QyFileLogger.js +58 -0
- package/src/QyIndex.js +237 -0
- package/src/QyIndexPropTree.js +92 -0
- package/src/QyIntersectNode.js +48 -0
- package/src/QyItem.js +105 -0
- package/src/QyJsonSaver.js +20 -0
- package/src/QyKVData.js +289 -0
- package/src/QyKVDataCleaner.js +17 -0
- package/src/QyKVDataCleaner_Worker.js +94 -0
- package/src/QyKVDataLoader.js +65 -0
- package/src/QyKVDataSaver.js +17 -0
- package/src/QyKVDataSaver_Worker.js +81 -0
- package/src/QyLogFileSaver.js +14 -0
- package/src/QyLogFileSaver_Worker.js +26 -0
- package/src/QyLogger.js +76 -0
- package/src/QyMessageWorker.js +41 -0
- package/src/QyMessager.js +84 -0
- package/src/QyMessager_Run.js +13 -0
- package/src/QyMod.js +15 -0
- package/src/QyOpRunner.js +73 -0
- package/src/QyQueue.js +60 -0
- package/src/QySaver.js +83 -0
- package/src/QySnapshotCompactor.js +17 -0
- package/src/QySnapshotCompactor_Worker.js +151 -0
- package/src/QySnapshotSaver.js +17 -0
- package/src/QySnapshotSaver_Worker.js +164 -0
- package/src/QySnapshots.js +354 -0
- package/src/QyStructure.js +50 -0
- package/src/QyTrigger.js +133 -0
- package/src/QyUnionNode.js +105 -0
- package/src/QyUtils.js +250 -0
- package/src/QyWorker.js +33 -0
- package/src/hackernews.js +93 -0
- package/src/rpc/evalCode.js +9 -0
- package/src/rpc/getFileContent.js +16 -0
- package/src/rpc/getSubItems.js +32 -0
- package/src/rpc/queryFiles.js +33 -0
- package/src/rpc/reloadRpc.js +1 -0
- package/src/simulate.js +114 -0
- package/src/simulate2.js +110 -0
- package/src/soc_pokec.js +43 -0
- package/static/bootstrap-5.3.8-dist/css/bootstrap.min.css +6 -0
- package/static/bootstrap-5.3.8-dist/js/bootstrap.bundle.js +6312 -0
- package/static/datafolderui/QyStyledElement.js +30 -0
- package/static/datafolderui/QyUIFolderTree.js +203 -0
- package/static/datafolderui/QyUIUtils.js +42 -0
- package/static/datafolderui/display-tree.js +108 -0
- package/static/datafolderui/explore-tree.js +122 -0
- package/static/datafolderui/folder-tree.js +5 -0
- package/static/datafolderui/hscrpt.js +25 -0
- package/static/datafolderui/index.css +6 -0
- package/static/datafolderui/index.html +20 -0
- package/static/datafolderui/json-view.js +74 -0
- package/static/datafolderui/kv-input.js +66 -0
- package/static/datafolderui/list-input.js +52 -0
- package/static/datafolderui/main-page.js +98 -0
- package/static/datafolderui/num-input.js +27 -0
- package/static/datafolderui/query-editor.js +229 -0
- package/static/datafolderui/querySelectorShadowRoot.js +24 -0
- package/static/datafolderui/resizable-input.js +67 -0
- package/static/favicon.ico +0 -0
- package/static/icons-1.13.1/font/bootstrap-icons.min.css +5 -0
- package/static/icons-1.13.1/font/fonts/bootstrap-icons.woff +0 -0
- package/static/icons-1.13.1/font/fonts/bootstrap-icons.woff2 +0 -0
- package/static/json-view/dist/jsonview.js +433 -0
- package/static/tree.js/tree.css +106 -0
- package/static/tree.js/tree.js +374 -0
- package/static/w2ui/dist/w2ui.min.css +2 -0
- package/static/w2ui/src/less/icons/icons.json +1 -0
- package/static/w2ui/src/less/icons/preview.html +33 -0
- package/static/w2ui/src/less/icons/readme.md +15 -0
- package/static/w2ui/src/less/icons/svg/box.svg +3 -0
- package/static/w2ui/src/less/icons/svg/check.svg +3 -0
- package/static/w2ui/src/less/icons/svg/colors.svg +3 -0
- package/static/w2ui/src/less/icons/svg/columns.svg +5 -0
- package/static/w2ui/src/less/icons/svg/cross.svg +4 -0
- package/static/w2ui/src/less/icons/svg/drop.svg +6 -0
- package/static/w2ui/src/less/icons/svg/empty.svg +3 -0
- package/static/w2ui/src/less/icons/svg/eye-dropper.svg +4 -0
- package/static/w2ui/src/less/icons/svg/info.svg +8 -0
- package/static/w2ui/src/less/icons/svg/paste.svg +4 -0
- package/static/w2ui/src/less/icons/svg/pencil.svg +5 -0
- package/static/w2ui/src/less/icons/svg/plus.svg +3 -0
- package/static/w2ui/src/less/icons/svg/reload.svg +5 -0
- package/static/w2ui/src/less/icons/svg/search.svg +3 -0
- package/static/w2ui/src/less/icons/svg/settings.svg +8 -0
- package/static/w2ui/src/less/icons/w2ui-font.css +36 -0
- package/static/w2ui/src/less/icons/w2ui-font.woff +0 -0
- package/static/w2ui/src/less/src/buttons.less +184 -0
- package/static/w2ui/src/less/src/common.less +541 -0
- package/static/w2ui/src/less/src/fields.less +302 -0
- package/static/w2ui/src/less/src/form.less +319 -0
- package/static/w2ui/src/less/src/grid.less +1057 -0
- package/static/w2ui/src/less/src/icons.less +37 -0
- package/static/w2ui/src/less/src/layout.less +86 -0
- package/static/w2ui/src/less/src/mixins.less +87 -0
- package/static/w2ui/src/less/src/popup.less +277 -0
- package/static/w2ui/src/less/src/sidebar.less +343 -0
- package/static/w2ui/src/less/src/tabs.less +145 -0
- package/static/w2ui/src/less/src/toolbar.less +219 -0
- package/static/w2ui/src/less/src/tooltip.less +948 -0
- package/static/w2ui/src/less/w2ui.less +196 -0
- package/static/w2ui/src/locale/az-az.json +114 -0
- package/static/w2ui/src/locale/ba-ba.json +114 -0
- package/static/w2ui/src/locale/bg-bg.json +114 -0
- package/static/w2ui/src/locale/ca-es.json +114 -0
- package/static/w2ui/src/locale/da-dk.json +114 -0
- package/static/w2ui/src/locale/de-de.json +114 -0
- package/static/w2ui/src/locale/en-gb.json +17 -0
- package/static/w2ui/src/locale/en-us.json +17 -0
- package/static/w2ui/src/locale/es-ar.json +115 -0
- package/static/w2ui/src/locale/es-cr.json +119 -0
- package/static/w2ui/src/locale/es-es.json +114 -0
- package/static/w2ui/src/locale/es-mx.json +114 -0
- package/static/w2ui/src/locale/fr-fr.json +114 -0
- package/static/w2ui/src/locale/gl-es.json +114 -0
- package/static/w2ui/src/locale/hr-hr.json +114 -0
- package/static/w2ui/src/locale/hu-hu.json +114 -0
- package/static/w2ui/src/locale/id-id.json +114 -0
- package/static/w2ui/src/locale/it-it.json +114 -0
- package/static/w2ui/src/locale/ja-jp.json +114 -0
- package/static/w2ui/src/locale/kk-kz.json +116 -0
- package/static/w2ui/src/locale/ko-kr.json +114 -0
- package/static/w2ui/src/locale/lt-lt.json +114 -0
- package/static/w2ui/src/locale/nl-nl.json +114 -0
- package/static/w2ui/src/locale/no-no.json +114 -0
- package/static/w2ui/src/locale/pl-pl.json +114 -0
- package/static/w2ui/src/locale/pt-br.json +114 -0
- package/static/w2ui/src/locale/ru-ru.json +116 -0
- package/static/w2ui/src/locale/sk-sk.json +114 -0
- package/static/w2ui/src/locale/sl-si.json +116 -0
- package/static/w2ui/src/locale/sr-rs.json +114 -0
- package/static/w2ui/src/locale/tr-tr.json +114 -0
- package/static/w2ui/src/locale/uk-ua.json +114 -0
- package/static/w2ui/src/locale/zh-cn.json +114 -0
- package/static/w2ui/src/locale/zh-tw.json +114 -0
- package/static/w2ui/src/query.js +583 -0
- package/static/w2ui/src/readme.md +11 -0
- package/static/w2ui/src/w2base.js +311 -0
- package/static/w2ui/src/w2compat.js +207 -0
- package/static/w2ui/src/w2field.js +1854 -0
- package/static/w2ui/src/w2form.js +2411 -0
- package/static/w2ui/src/w2grid.js +8916 -0
- package/static/w2ui/src/w2layout.js +1194 -0
- package/static/w2ui/src/w2locale.js +129 -0
- package/static/w2ui/src/w2popup.js +909 -0
- package/static/w2ui/src/w2sidebar.js +1769 -0
- package/static/w2ui/src/w2tabs.js +669 -0
- package/static/w2ui/src/w2toolbar.js +1180 -0
- package/static/w2ui/src/w2tooltip.js +3134 -0
- package/static/w2ui/src/w2utils.js +2457 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
class QueryCounter {
|
|
2
|
+
constructor(e = 1 / 0, i = 0) {
|
|
3
|
+
Object.assign(this, {
|
|
4
|
+
limit: e,
|
|
5
|
+
cursor: i,
|
|
6
|
+
added: 0,
|
|
7
|
+
count: 0,
|
|
8
|
+
encounteredMap: {}
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
get isFull() {
|
|
12
|
+
return this.limit <= this.added;
|
|
13
|
+
}
|
|
14
|
+
encounterOne(e) {
|
|
15
|
+
var {
|
|
16
|
+
encounteredMap: i,
|
|
17
|
+
isFull: r
|
|
18
|
+
} = this;
|
|
19
|
+
if (!r && !i[e]) return i[e] = 1, this.countOne();
|
|
20
|
+
}
|
|
21
|
+
countOne() {
|
|
22
|
+
if (this.count++ >= this.cursor) return ++this.added, !0;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class QyQueryNode {
|
|
27
|
+
constructor() {
|
|
28
|
+
Object.assign(this, {
|
|
29
|
+
fileCount: 0,
|
|
30
|
+
nodeList: []
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
encounterOne(e, i, r) {
|
|
34
|
+
if (r) {
|
|
35
|
+
if (r.encounterOne(i)) return e[i] = 1, r.isFull;
|
|
36
|
+
} else e[i] = 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class QyUnionNode extends QyQueryNode {
|
|
41
|
+
constructor(e) {
|
|
42
|
+
super(), Object.assign(this, {
|
|
43
|
+
dirMap: {},
|
|
44
|
+
checkedDirMap: {}
|
|
45
|
+
}), e && this.addDir(e);
|
|
46
|
+
}
|
|
47
|
+
containsFile(i) {
|
|
48
|
+
var {
|
|
49
|
+
fileCount: e,
|
|
50
|
+
dirMap: r,
|
|
51
|
+
nodeList: t
|
|
52
|
+
} = this;
|
|
53
|
+
if (0 < e) {
|
|
54
|
+
for (var n in r) if (null != r[n].getFile(i)) return !0;
|
|
55
|
+
for (let e = 0; e < t.length; ++e) if (t[e].containsFile(i)) return !0;
|
|
56
|
+
}
|
|
57
|
+
return !1;
|
|
58
|
+
}
|
|
59
|
+
getFileMap(e) {
|
|
60
|
+
var i, r, t, {
|
|
61
|
+
dirMap: n,
|
|
62
|
+
nodeList: o
|
|
63
|
+
} = this, s = {};
|
|
64
|
+
for (i in n) {
|
|
65
|
+
var u, d = n[i].fileMap;
|
|
66
|
+
for (u in d) if (d[u].created && !s[u] && this.encounterOne(s, u, e)) return s;
|
|
67
|
+
}
|
|
68
|
+
for (r of o) for (t in r.getFileMap()) if (!s[t] && this.encounterOne(s, t, e)) return s;
|
|
69
|
+
return s;
|
|
70
|
+
}
|
|
71
|
+
addNode(e) {
|
|
72
|
+
if (e) {
|
|
73
|
+
var {
|
|
74
|
+
dirMap: i,
|
|
75
|
+
checkedDirMap: r,
|
|
76
|
+
nodeList: t
|
|
77
|
+
} = this;
|
|
78
|
+
if (e instanceof QyUnionNode) {
|
|
79
|
+
for (var n in e.dirMap) i[n] = e.dirMap[n];
|
|
80
|
+
for (var o in t.push(...e.nodeList), e.checkedDirMap) r[o] = 1;
|
|
81
|
+
} else t.push(e);
|
|
82
|
+
this.fileCount += e.fileCount;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
addDir(e) {
|
|
86
|
+
var {
|
|
87
|
+
dirMap: i,
|
|
88
|
+
checkedDirMap: r
|
|
89
|
+
} = this, {
|
|
90
|
+
fileCount: t,
|
|
91
|
+
subdirCount: n,
|
|
92
|
+
fullPathHash: o
|
|
93
|
+
} = e;
|
|
94
|
+
if (!i[o] && !r[o]) {
|
|
95
|
+
if (0 < n) for (var s of e.subdirList) this.addDir(s);
|
|
96
|
+
0 < t ? (i[o] = e, this.fileCount += t) : r[o] = 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Object.assign(module.exports, {
|
|
102
|
+
QyUnionNode: QyUnionNode,
|
|
103
|
+
QyQueryNode: QyQueryNode,
|
|
104
|
+
QueryCounter: QueryCounter
|
|
105
|
+
});
|
package/src/QyUtils.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
let crypto = require("node:crypto"), path = require("node:path"), process = require("node:process"), fsPromises = require("node:fs/promises"), fs = require("node:fs"), timers = require("node:timers/promises"), SpecialOperators = Object.fromEntries([ "$del", "$link", "$rpl", "$spec", "$raw", "$all", "$clone", "$ext", "$or", "$and", "$gt", "$lt", "$gte", "$lte" ].map(e => [ e, e ])), HiddenFolderName = "%", SpecialFilePaths = {
|
|
2
|
+
HiddenFolderName: HiddenFolderName,
|
|
3
|
+
TriggerDirPath: [ HiddenFolderName, "trigger" ],
|
|
4
|
+
RpcDirPath: [ HiddenFolderName, "rpc" ],
|
|
5
|
+
IndexDirPath: [ HiddenFolderName, "index" ],
|
|
6
|
+
IndexPropPathsFileName: "propPaths"
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function isMatchName(e) {
|
|
10
|
+
return e.startsWith("{") && e.endsWith("}");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getMatchParam(e) {
|
|
14
|
+
return isMatchName(e) && e.substring(1, e.length - 1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getSizeStr(e) {
|
|
18
|
+
return 1e12 < e ? Math.round(100 * e / 1e12) / 100 + "TB" : 1e9 < e ? Math.round(100 * e / 1e9) / 100 + "GB" : 1e6 < e ? Math.round(100 * e / 1e6) / 100 + "MB" : 1e3 < e ? Math.round(100 * e / 1e3) / 100 + "KB" : e;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let Hash_Length = 10;
|
|
22
|
+
|
|
23
|
+
function calHash(e, t = Hash_Length) {
|
|
24
|
+
return crypto.createHash("sha256").update(e, "utf8").digest("base64url").slice(0, t);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let Hash_Key_Length = Hash_Length + 1;
|
|
28
|
+
|
|
29
|
+
function getSubdirMapKey(e) {
|
|
30
|
+
return "d" + e;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getFileMapKey(e) {
|
|
34
|
+
return "f" + e;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getContentKey(e) {
|
|
38
|
+
return "c" + e;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getRandomStr(e = 8) {
|
|
42
|
+
return crypto.randomBytes(e).toString("hex");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function arrayLast(e) {
|
|
46
|
+
return e[e.length - 1];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isEmptyObj(e) {
|
|
50
|
+
if (null != e) for (var t in e) if (null != e[t]) return !1;
|
|
51
|
+
return !0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isNotNullObj(e) {
|
|
55
|
+
return null != e && "object" == typeof e && !(e instanceof Date);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isString(e) {
|
|
59
|
+
return "string" == typeof e;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isNumber(e) {
|
|
63
|
+
return "number" == typeof e;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isBoolean(e) {
|
|
67
|
+
return "boolean" == typeof e;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isFunction(e) {
|
|
71
|
+
return "function" == typeof e;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isSimpleType(e) {
|
|
75
|
+
var t = typeof e;
|
|
76
|
+
return "string" == t || "number" == t || "boolean" == t || e instanceof Date;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function trimArrayTail(e) {
|
|
80
|
+
let t = e.length;
|
|
81
|
+
for (;null == e[t - 1] && 0 < t; ) --t;
|
|
82
|
+
return e.length = t, e;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function setPromise(r) {
|
|
86
|
+
return r.promise || (r.promise = new Promise((e, t) => Object.assign(r, {
|
|
87
|
+
resolve: e,
|
|
88
|
+
reject: t
|
|
89
|
+
}))), r;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function toBuffer(e) {
|
|
93
|
+
return Buffer.isBuffer(e) ? e : Buffer.from(e);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getSuffix(e) {
|
|
97
|
+
return 1 < e ? "s" : "";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function ensureDir(e) {
|
|
101
|
+
return fsPromises.mkdir(e, {
|
|
102
|
+
recursive: !0
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function ensureParentDir(e) {
|
|
107
|
+
return ensureDir(path.dirname(e));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function getSize(e) {
|
|
111
|
+
try {
|
|
112
|
+
return (await fsPromises.stat(e)).size;
|
|
113
|
+
} catch (e) {
|
|
114
|
+
return !1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function folderOrFileExists(e) {
|
|
119
|
+
return !1 !== await getSize(e);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function sleep(e, t) {
|
|
123
|
+
return timers.setTimeout(1e3 * e, !0, {
|
|
124
|
+
signal: t
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let exclusiveFilePathMap = void 0;
|
|
129
|
+
|
|
130
|
+
function unlockPathMap() {
|
|
131
|
+
for (var e of Object.keys(exclusiveFilePathMap)) unlockExclusiveFilePath(e);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function lockExclusiveFilePath(t) {
|
|
135
|
+
try {
|
|
136
|
+
await ensureParentDir(t);
|
|
137
|
+
var e = new Date().toISOString() + ": " + process.argv.join(" ");
|
|
138
|
+
await fsPromises.writeFile(t, e, {
|
|
139
|
+
flag: "wx",
|
|
140
|
+
flush: !0
|
|
141
|
+
}), logger.info("Locked " + t), exclusiveFilePathMap ? exclusiveFilePathMap[t] = 1 : (exclusiveFilePathMap = {
|
|
142
|
+
[t]: 1
|
|
143
|
+
}, process.on("exit", unlockPathMap), process.on("SIGINT", unlockPathMap));
|
|
144
|
+
} catch (e) {
|
|
145
|
+
throw "EEXIST" == e.code && logger.error(`Already started by ${(await fsPromises.readFile(t)).toString()}:`, e),
|
|
146
|
+
e;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function unlockExclusiveFilePath(e) {
|
|
151
|
+
try {
|
|
152
|
+
fs.unlinkSync(e), logger.info("Unlocked " + e);
|
|
153
|
+
} catch (e) {}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function fromSingleName(e) {
|
|
157
|
+
return e.startsWith("[") && e.endsWith("]") ? JSON.parse(e) : e;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function toSingleName(e) {
|
|
161
|
+
return Array.isArray(e) ? 1 == e.length ? e[0] : JSON.stringify(e) : e;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function pathSplit(e) {
|
|
165
|
+
e = e.split("/");
|
|
166
|
+
return e[0] || (e[0] = "/"), e;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function propNameSplit(e) {
|
|
170
|
+
return e.split(".");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function propNameJoin(e) {
|
|
174
|
+
return e.join(".");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function randomInt(e, t = 0) {
|
|
178
|
+
return Math.floor(Math.random() * (e - t)) + t;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function listToMap(e) {
|
|
182
|
+
var t, r = {};
|
|
183
|
+
for (t of e) r[t] = 1;
|
|
184
|
+
return r;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function readBufferAsync(t, r, n, i) {
|
|
188
|
+
var a = Buffer.allocUnsafe(i);
|
|
189
|
+
for (let e = 0; e < i; ) {
|
|
190
|
+
var o = (await r.read(a, e, i - e, n + e)).bytesRead;
|
|
191
|
+
if (0 == o) return void logger.error(`read ${t} at ${n + e} returning 0 byte`);
|
|
192
|
+
e += o;
|
|
193
|
+
}
|
|
194
|
+
return a;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function memoryPercent() {
|
|
198
|
+
var e = process.availableMemory();
|
|
199
|
+
return Math.round(e / (process.memoryUsage.rss() + e) * 100);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function generatorToList(e) {
|
|
203
|
+
var t, r = [];
|
|
204
|
+
for (t of e) r.push(t);
|
|
205
|
+
return r;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
Object.assign(module.exports, {
|
|
209
|
+
SpecialOperators: SpecialOperators,
|
|
210
|
+
calHash: calHash,
|
|
211
|
+
Hash_Length: Hash_Length,
|
|
212
|
+
Hash_Key_Length: Hash_Key_Length,
|
|
213
|
+
pathSplit: pathSplit,
|
|
214
|
+
isMatchName: isMatchName,
|
|
215
|
+
getMatchParam: getMatchParam,
|
|
216
|
+
isNumber: isNumber,
|
|
217
|
+
isString: isString,
|
|
218
|
+
isBoolean: isBoolean,
|
|
219
|
+
isFunction: isFunction,
|
|
220
|
+
isNotNullObj: isNotNullObj,
|
|
221
|
+
isSimpleType: isSimpleType,
|
|
222
|
+
isEmptyObj: isEmptyObj,
|
|
223
|
+
getSubdirMapKey: getSubdirMapKey,
|
|
224
|
+
getFileMapKey: getFileMapKey,
|
|
225
|
+
getContentKey: getContentKey,
|
|
226
|
+
arrayLast: arrayLast,
|
|
227
|
+
getRandomStr: getRandomStr,
|
|
228
|
+
getSizeStr: getSizeStr,
|
|
229
|
+
setPromise: setPromise,
|
|
230
|
+
trimArrayTail: trimArrayTail,
|
|
231
|
+
toBuffer: toBuffer,
|
|
232
|
+
getSuffix: getSuffix,
|
|
233
|
+
sleep: sleep,
|
|
234
|
+
ensureDir: ensureDir,
|
|
235
|
+
ensureParentDir: ensureParentDir,
|
|
236
|
+
getSize: getSize,
|
|
237
|
+
lockExclusiveFilePath: lockExclusiveFilePath,
|
|
238
|
+
unlockExclusiveFilePath: unlockExclusiveFilePath,
|
|
239
|
+
folderOrFileExists: folderOrFileExists,
|
|
240
|
+
SpecialFilePaths: SpecialFilePaths,
|
|
241
|
+
fromSingleName: fromSingleName,
|
|
242
|
+
toSingleName: toSingleName,
|
|
243
|
+
randomInt: randomInt,
|
|
244
|
+
propNameSplit: propNameSplit,
|
|
245
|
+
propNameJoin: propNameJoin,
|
|
246
|
+
listToMap: listToMap,
|
|
247
|
+
readBufferAsync: readBufferAsync,
|
|
248
|
+
memoryPercent: memoryPercent,
|
|
249
|
+
generatorToList: generatorToList
|
|
250
|
+
});
|
package/src/QyWorker.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
let path = require("node:path"), {
|
|
2
|
+
Worker,
|
|
3
|
+
setEnvironmentData
|
|
4
|
+
} = require("node:worker_threads"), logger = require("./QyLogger.js").logger, setPromise = require("./QyUtils.js").setPromise;
|
|
5
|
+
|
|
6
|
+
function runOnceWithWorker(e, r) {
|
|
7
|
+
setEnvironmentData("logLevel", logger.level);
|
|
8
|
+
var o = r.operation;
|
|
9
|
+
let t = e + " " + o, {
|
|
10
|
+
promise: i,
|
|
11
|
+
resolve: n
|
|
12
|
+
} = (logger.info("Starting worker " + t), setPromise({}));
|
|
13
|
+
path.isAbsolute(e) || (e = path.join(__dirname, e));
|
|
14
|
+
o = new Worker(e, {
|
|
15
|
+
workerData: r
|
|
16
|
+
});
|
|
17
|
+
let s;
|
|
18
|
+
return o.on("message", e => {
|
|
19
|
+
s = !0, n(e);
|
|
20
|
+
}), o.on("error", e => {
|
|
21
|
+
s = !0, n(e);
|
|
22
|
+
}), o.on("exit", e => {
|
|
23
|
+
s ? logger.info(`Worker ${t} exited.`) : (logger.log(`Worker ${t} stopped with exitCode ` + e),
|
|
24
|
+
n());
|
|
25
|
+
}), {
|
|
26
|
+
worker: o,
|
|
27
|
+
promise: i
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
Object.assign(module.exports, {
|
|
32
|
+
runOnceWithWorker: runOnceWithWorker
|
|
33
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
let path = require("node:path"), fsPromises = require("node:fs/promises"), QyDB = require("./QyDB.js").QyDB, folderOrFileExists = require("./QyUtils.js").folderOrFileExists, shardRootFolder = (logger.level = "info",
|
|
2
|
+
"D:\\exp\\HackerBook\\static-shards_json"), dataFolder = "./tmp/hackernews", qyDB = new QyDB(dataFolder, {
|
|
3
|
+
fileLogLevel: "basic",
|
|
4
|
+
maxInMemSnapshotCount: 1,
|
|
5
|
+
snapshotMaxChangeCount: 2e3,
|
|
6
|
+
unloadMemInterval: 2e4
|
|
7
|
+
}), batchSize = 100;
|
|
8
|
+
|
|
9
|
+
function insertShard(e, t) {
|
|
10
|
+
var a, r = {}, o = {};
|
|
11
|
+
for (a of e) {
|
|
12
|
+
var {
|
|
13
|
+
parent_id: n,
|
|
14
|
+
child_id: s
|
|
15
|
+
} = a;
|
|
16
|
+
let e = o[s], t = (e = e || (o[s] = {}), o[n]), r = (t = t || (o[n] = {})).children;
|
|
17
|
+
(r = r || (t.children = {}))[s] = e;
|
|
18
|
+
}
|
|
19
|
+
var i, l, c, d = {};
|
|
20
|
+
let h = 0, f = 0;
|
|
21
|
+
for (i of t) {
|
|
22
|
+
var {
|
|
23
|
+
id: m,
|
|
24
|
+
time: u,
|
|
25
|
+
parent: g
|
|
26
|
+
} = i;
|
|
27
|
+
if (null == g) {
|
|
28
|
+
u = getDatePath(u);
|
|
29
|
+
r[m] = u;
|
|
30
|
+
let e = o[m];
|
|
31
|
+
e ? e.item = i : e = o[m] = {
|
|
32
|
+
item: i
|
|
33
|
+
}, d[m] = e, ++h;
|
|
34
|
+
} else {
|
|
35
|
+
u = o[g];
|
|
36
|
+
if (u) {
|
|
37
|
+
let e = u.children, t = (e = e || (u.children = {}))[m];
|
|
38
|
+
t ? t.item = i : t = e[m] = {
|
|
39
|
+
item: i
|
|
40
|
+
}, ++f;
|
|
41
|
+
} else logger.warn(m + `'s parent ${g} not exist.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let p = 0;
|
|
45
|
+
for (l in d) {
|
|
46
|
+
var v = r[l];
|
|
47
|
+
qyDB.batch.insert([ "hackernews", ...v, l ], d[l]), ++p >= batchSize && (qyDB.batch.run(),
|
|
48
|
+
p = 0);
|
|
49
|
+
}
|
|
50
|
+
for (c in 0 < p && qyDB.batch.run(), d) cleanContent(d[c]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function cleanContent(e) {
|
|
54
|
+
delete e.item;
|
|
55
|
+
var t, r = e.children;
|
|
56
|
+
for (t in r) cleanContent(r[t]);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getDatePath(e) {
|
|
60
|
+
e = new Date(1e3 * e);
|
|
61
|
+
return [ e.getUTCFullYear(), e.getUTCMonth() + 1, e.getUTCDate() ];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function insertShards() {
|
|
65
|
+
var e;
|
|
66
|
+
for (e of (await fsPromises.readdir(shardRootFolder)).map(e => /([0-9]+).json/.exec(e)[1]).sort((e, t) => e - t)) {
|
|
67
|
+
var t = `shard_${e}.json`, {
|
|
68
|
+
edges: r,
|
|
69
|
+
items: a
|
|
70
|
+
} = JSON.parse(await fsPromises.readFile(path.join(shardRootFolder, t)));
|
|
71
|
+
logger.warn(t), insertShard(r, a);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function run() {
|
|
76
|
+
console.time("accu"), process.argv[3] && (logger.level = process.argv[3]);
|
|
77
|
+
let e = !0;
|
|
78
|
+
if ("true" == process.argv[2]) {
|
|
79
|
+
try {
|
|
80
|
+
await fsPromises.rm(dataFolder, {
|
|
81
|
+
recursive: !0
|
|
82
|
+
});
|
|
83
|
+
} catch (e) {
|
|
84
|
+
logger.warn(e);
|
|
85
|
+
}
|
|
86
|
+
e = !0;
|
|
87
|
+
} else await folderOrFileExists(dataFolder) || (e = !0);
|
|
88
|
+
console.time("run"), console.time("start"), await qyDB.start(), console.timeEnd("start"),
|
|
89
|
+
e && await insertShards(), console.time("stop"), await qyDB.stop(), console.timeEnd("stop"),
|
|
90
|
+
console.timeEnd("run"), console.timeEnd("accu");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
run();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
let Error_No_Such_File = new Error("No such file");
|
|
2
|
+
|
|
3
|
+
module.exports = ({
|
|
4
|
+
filePath: e,
|
|
5
|
+
cChangeId: n
|
|
6
|
+
}) => {
|
|
7
|
+
var l, r, t, e = qyDB.fileP(e);
|
|
8
|
+
return e ? ({
|
|
9
|
+
fileContent: e,
|
|
10
|
+
cChangeId: l,
|
|
11
|
+
fileContentKey: r
|
|
12
|
+
} = e, t = {}, n != l && (Object.assign(t, {
|
|
13
|
+
fileContent: e,
|
|
14
|
+
cChangeId: l
|
|
15
|
+
}), null == n) && (t.fileContentKey = r), t) : Error_No_Such_File;
|
|
16
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
let Error_no_Such_Dir = new Error("No such dir");
|
|
2
|
+
|
|
3
|
+
module.exports = ({
|
|
4
|
+
dirPath: r,
|
|
5
|
+
dChangeId: e,
|
|
6
|
+
fChangeId: a
|
|
7
|
+
}) => {
|
|
8
|
+
r = qyDB.dirP(r);
|
|
9
|
+
if (!r) return Error_no_Such_Dir;
|
|
10
|
+
var d = {};
|
|
11
|
+
if (e || a || (d.fullPathHash = r.fullPathHash), e != r.dChangeId) {
|
|
12
|
+
var h, e = r.sortedSubdirList, i = [];
|
|
13
|
+
for ({
|
|
14
|
+
name: h
|
|
15
|
+
} of e) i.push(h);
|
|
16
|
+
Object.assign(d, {
|
|
17
|
+
subdirList: i,
|
|
18
|
+
dChangeId: r.dChangeId
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
if (a != r.fChangeId) {
|
|
22
|
+
var n, e = r.sortedFileList, s = [];
|
|
23
|
+
for ({
|
|
24
|
+
name: n
|
|
25
|
+
} of e) s.push(n);
|
|
26
|
+
Object.assign(d, {
|
|
27
|
+
fileList: s,
|
|
28
|
+
fChangeId: r.fChangeId
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return d;
|
|
32
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module.exports = queryText => {
|
|
2
|
+
let {
|
|
3
|
+
folder,
|
|
4
|
+
value,
|
|
5
|
+
view,
|
|
6
|
+
limit,
|
|
7
|
+
cursor
|
|
8
|
+
} = eval("((x)=>x)(" + queryText + ")"), fileList = qyDB.queryFiles(folder, value, limit, cursor), tree = {};
|
|
9
|
+
for (var file of fileList) {
|
|
10
|
+
let {
|
|
11
|
+
parentList
|
|
12
|
+
} = file, node = tree;
|
|
13
|
+
for (var dir of parentList) {
|
|
14
|
+
let {
|
|
15
|
+
name,
|
|
16
|
+
fullPathHash
|
|
17
|
+
} = dir, subdirMap = node.subdirMap;
|
|
18
|
+
subdirMap = subdirMap || (node.subdirMap = {}), node = subdirMap[name],
|
|
19
|
+
node = node || (subdirMap[name] = {
|
|
20
|
+
fullPathHash: fullPathHash
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
let fileMap = node.fileMap;
|
|
24
|
+
fileMap = fileMap || (node.fileMap = {}), fileMap[file.name] = {
|
|
25
|
+
fileContent: file.view(view, !0),
|
|
26
|
+
fileContentKey: file.fileContentKey
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
tree: tree,
|
|
31
|
+
count: fileList.length
|
|
32
|
+
};
|
|
33
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = () => {};
|
package/src/simulate.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
let path = require("node:path"), fsPromises = require("node:fs/promises"), QyDB = require("./QyDB.js").QyDB, {
|
|
2
|
+
folderOrFileExists,
|
|
3
|
+
randomInt,
|
|
4
|
+
sleep
|
|
5
|
+
} = require("./QyUtils.js"), runQueries = require("../scripts/airbnb2_queries.js").runQueries, airbnbDataFolder = (logger.level = "info",
|
|
6
|
+
"../airbnb"), dataFolder = "./tmp/simulate", hosts, listings, reviews, hostIdList, listingIdList, reviewIdList;
|
|
7
|
+
|
|
8
|
+
async function loadJsonFile(t) {
|
|
9
|
+
return JSON.parse(await fsPromises.readFile(path.join(airbnbDataFolder, t)));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function loadData() {
|
|
13
|
+
console.time("loadData"), [ hosts, listings, reviews ] = await Promise.all([ loadJsonFile("hosts.json"), loadJsonFile("listings.json"), loadJsonFile("reviews.json") ]),
|
|
14
|
+
hostIdList = Object.keys(hosts), listingIdList = Object.keys(listings), reviewIdList = Object.keys(reviews),
|
|
15
|
+
console.timeEnd("loadData");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function insertData(t) {
|
|
19
|
+
console.time("insertData");
|
|
20
|
+
var e, s, i, o = t.batch;
|
|
21
|
+
for (e of hostIdList) o.insert([ "hosts", e ], hosts[e]);
|
|
22
|
+
for (s of listingIdList) {
|
|
23
|
+
var n = listings[s], {
|
|
24
|
+
property_type: r,
|
|
25
|
+
room_type: a,
|
|
26
|
+
bed_type: l
|
|
27
|
+
} = n;
|
|
28
|
+
o.insert([ "airbnb", r, a, l, s ], n);
|
|
29
|
+
}
|
|
30
|
+
for (i of reviewIdList) {
|
|
31
|
+
var d = reviews[i];
|
|
32
|
+
o.insert([ "reviews", d.listing_id, i ], d);
|
|
33
|
+
}
|
|
34
|
+
o.run(), console.timeEnd("insertData");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getRandomHost() {
|
|
38
|
+
return hosts[hostIdList[randomInt(hostIdList.length)]];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getRandomListing() {
|
|
42
|
+
return listings[listingIdList[randomInt(listingIdList.length)]];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getRandomReview() {
|
|
46
|
+
return reviews[reviewIdList[randomInt(reviewIdList.length)]];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function modifyHost(t) {
|
|
50
|
+
var e, s = getRandomHost(), i = getRandomHost(), o = {};
|
|
51
|
+
for (e in i) 1 == randomInt(2) && (o[e] = i[e]);
|
|
52
|
+
t.insert([ "hosts", s.host_id ], o);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function modifyListing(t) {
|
|
56
|
+
var e, s = getRandomListing(), i = getRandomListing(), o = {};
|
|
57
|
+
for (e in i) 1 == randomInt(2) && (o[e] = i[e]);
|
|
58
|
+
var {
|
|
59
|
+
property_type: s,
|
|
60
|
+
room_type: n,
|
|
61
|
+
bed_type: r,
|
|
62
|
+
_id: a
|
|
63
|
+
} = s;
|
|
64
|
+
t.insert([ "airbnb", s, n, r, a ], o);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function modifyReview(t) {
|
|
68
|
+
var e, s = getRandomReview(), i = getRandomReview(), o = {};
|
|
69
|
+
for (e in i) 1 == randomInt(2) && (o[e] = i[e]);
|
|
70
|
+
t.insert([ "reviews", s.listing_id, s._id ], o);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function simulateOne(t, e) {
|
|
74
|
+
for (;0 < e--; ) {
|
|
75
|
+
switch (randomInt(3)) {
|
|
76
|
+
case 0:
|
|
77
|
+
modifyHost(t);
|
|
78
|
+
break;
|
|
79
|
+
|
|
80
|
+
case 1:
|
|
81
|
+
modifyListing(t);
|
|
82
|
+
break;
|
|
83
|
+
|
|
84
|
+
case 2:
|
|
85
|
+
modifyReview(t);
|
|
86
|
+
}
|
|
87
|
+
await sleep(randomInt(1e3) / 1e3);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function run() {
|
|
92
|
+
console.time("total");
|
|
93
|
+
var e = process.argv[2] || 1e3, s = process.argv[3] || 100;
|
|
94
|
+
let t;
|
|
95
|
+
if ("true" == process.argv[4]) {
|
|
96
|
+
try {
|
|
97
|
+
await fsPromises.rm(dataFolder, {
|
|
98
|
+
recursive: !0
|
|
99
|
+
});
|
|
100
|
+
} catch (t) {}
|
|
101
|
+
t = !0;
|
|
102
|
+
} else await folderOrFileExists(dataFolder) || (t = !0);
|
|
103
|
+
process.argv[5] && (logger.level = process.argv[5]), await loadData(), console.log(`hosts:${hostIdList.length}, listings:${listingIdList.length}, reviews:` + reviewIdList.length);
|
|
104
|
+
var i = new QyDB(dataFolder, {
|
|
105
|
+
fileLogLevel: "basic"
|
|
106
|
+
}), o = (t && (await i.start(), insertData(i), await i.stop()), console.time("simulate " + s),
|
|
107
|
+
await i.start(), runQueries(i), []);
|
|
108
|
+
for (let t = 0; t < e; ++t) o.push(simulateOne(i, s));
|
|
109
|
+
await Promise.all(o), insertData(i), await i.stop(), console.timeEnd("simulate " + s),
|
|
110
|
+
console.time("start"), await i.start(), console.timeEnd("start"), runQueries(i),
|
|
111
|
+
await i.stop(), console.timeEnd("total");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
run();
|