inibase 1.0.0-rc.57 → 1.0.0-rc.58
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 +34 -18
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +98 -0
- package/dist/file.js +58 -38
- package/dist/index.d.ts +4 -1
- package/dist/index.js +137 -119
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- **Super-Fast** :zap: (built-in caching system)
|
|
15
15
|
- **ATOMIC** :lock: File lock for writing
|
|
16
16
|
- **Built-in form-validation** included :sunglasses:
|
|
17
|
-
- **Suitable for large data** :page_with_curl: (tested with
|
|
17
|
+
- **Suitable for large data** :page_with_curl: (tested with 4M records)
|
|
18
18
|
- **Support Compression** :eight_spoked_asterisk: (using built-in nodejs zlib)
|
|
19
19
|
- **Support Table Joins** :link:
|
|
20
20
|
- **Low memory-usage** :chart_with_downwards_trend: (3-5mb)
|
|
@@ -55,6 +55,21 @@ If you like Inibase, please sponsor: [GitHub Sponsors](https://github.com/sponso
|
|
|
55
55
|
|
|
56
56
|
To simplify the idea, each database has tables, each table has columns, each column will be stored in a seperated file. When **POST**ing new data, it will be appended to the _head_ of each file as new line. When **GET**ing data, the file will be readed line-by-line so it can handle large data (without consuming a lot of resources), when **PUT**ing(updating) in a specific column, only one file will be opened and updated
|
|
57
57
|
|
|
58
|
+
## Config (.env)
|
|
59
|
+
|
|
60
|
+
The `.env` file supports the following parameters (make sure to run commands with flag --env-file=.env)
|
|
61
|
+
|
|
62
|
+
```ini
|
|
63
|
+
# Auto generated secret key, will be using for encrypting the IDs
|
|
64
|
+
INIBASE_SECRET=
|
|
65
|
+
|
|
66
|
+
INIBASE_COMPRESSION=true
|
|
67
|
+
INIBASE_CACHE=true
|
|
68
|
+
|
|
69
|
+
# Prepend new items to the beginning of file
|
|
70
|
+
INIBASE_REVERSE=true
|
|
71
|
+
```
|
|
72
|
+
|
|
58
73
|
## Benchmark
|
|
59
74
|
|
|
60
75
|
### Bulk
|
|
@@ -77,22 +92,6 @@ To simplify the idea, each database has tables, each table has columns, each col
|
|
|
77
92
|
|
|
78
93
|
Ps: Testing by default with `user` table, with username, email, password fields _so results include password encryption process_
|
|
79
94
|
|
|
80
|
-
|
|
81
|
-
## Config (.env)
|
|
82
|
-
|
|
83
|
-
The `.env` file supports the following parameters (make sure to run commands with flag --env-file=.env)
|
|
84
|
-
|
|
85
|
-
```ini
|
|
86
|
-
# Auto generated secret key, will be using for encrypting the IDs
|
|
87
|
-
INIBASE_SECRET=
|
|
88
|
-
|
|
89
|
-
INIBASE_COMPRESSION=true
|
|
90
|
-
INIBASE_CACHE=true
|
|
91
|
-
|
|
92
|
-
# Prepend new items to the beginning of file
|
|
93
|
-
INIBASE_REVERSE=true
|
|
94
|
-
```
|
|
95
|
-
|
|
96
95
|
## Roadmap
|
|
97
96
|
|
|
98
97
|
- [x] Actions:
|
|
@@ -100,7 +99,7 @@ INIBASE_REVERSE=true
|
|
|
100
99
|
- [x] Pagination
|
|
101
100
|
- [x] Criteria
|
|
102
101
|
- [x] Columns
|
|
103
|
-
- [x]
|
|
102
|
+
- [x] Sort (using UNIX commands)
|
|
104
103
|
- [x] POST
|
|
105
104
|
- [x] PUT
|
|
106
105
|
- [x] DELETE
|
|
@@ -512,6 +511,23 @@ await db.min("user", ["age", ...], { isActive: false });
|
|
|
512
511
|
|
|
513
512
|
</details>
|
|
514
513
|
|
|
514
|
+
<details>
|
|
515
|
+
<summary>SORT</summary>
|
|
516
|
+
|
|
517
|
+
```js
|
|
518
|
+
import Inibase from "inibase";
|
|
519
|
+
const db = new Inibase("/databaseName");
|
|
520
|
+
|
|
521
|
+
// order users by the age column
|
|
522
|
+
await db.sort("user", "age");
|
|
523
|
+
|
|
524
|
+
// order users by the age and username columns
|
|
525
|
+
await db.sort("user", ["age","username"]);
|
|
526
|
+
await db.sort("user", {age: -1, username: "asc"});
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
</details>
|
|
530
|
+
|
|
515
531
|
## License
|
|
516
532
|
|
|
517
533
|
[MIT](./LICENSE)
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
import { createInterface } from "node:readline/promises";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
import Inibase from "./index.js";
|
|
6
|
+
import { basename } from "node:path";
|
|
7
|
+
import { isJSON, isNumber } from "./utils.js";
|
|
8
|
+
import Inison from "inison";
|
|
9
|
+
const { path } = parseArgs({
|
|
10
|
+
options: {
|
|
11
|
+
path: { type: "string", short: "p" },
|
|
12
|
+
},
|
|
13
|
+
}).values;
|
|
14
|
+
if (!path)
|
|
15
|
+
throw new Error("Please specify database folder path --path <databasePath> or -p <databasePath>");
|
|
16
|
+
const db = new Inibase(basename(path));
|
|
17
|
+
const rl = createInterface({
|
|
18
|
+
input: process.stdin,
|
|
19
|
+
output: process.stdout,
|
|
20
|
+
});
|
|
21
|
+
rl.prompt();
|
|
22
|
+
rl.on("line", async (input) => {
|
|
23
|
+
const trimedInput = input.trim();
|
|
24
|
+
if (trimedInput === "clear") {
|
|
25
|
+
console.clear();
|
|
26
|
+
rl.prompt();
|
|
27
|
+
}
|
|
28
|
+
if (trimedInput === "info") {
|
|
29
|
+
console.warn("war");
|
|
30
|
+
console.error("err");
|
|
31
|
+
}
|
|
32
|
+
const splitedInput = trimedInput.match(/[^\s"']+|"([^"]*)"|'([^']*)'/g);
|
|
33
|
+
if (["get", "post", "delete", "put"].includes(splitedInput[0].toLocaleLowerCase())) {
|
|
34
|
+
const table = splitedInput[1];
|
|
35
|
+
if (!table)
|
|
36
|
+
throw new Error("Please specify table name");
|
|
37
|
+
let { where, page, perPage, columns, data, returnData } = parseArgs({
|
|
38
|
+
args: splitedInput.toSpliced(0, 2),
|
|
39
|
+
options: {
|
|
40
|
+
where: { type: "string", short: "w" },
|
|
41
|
+
page: { type: "string", short: "p" },
|
|
42
|
+
perPage: { type: "string", short: "l" },
|
|
43
|
+
columns: { type: "string", short: "c", multiple: true },
|
|
44
|
+
data: { type: "string", short: "d" },
|
|
45
|
+
returnData: { type: "boolean", short: "r" },
|
|
46
|
+
},
|
|
47
|
+
}).values;
|
|
48
|
+
if (where) {
|
|
49
|
+
if (isNumber(where))
|
|
50
|
+
where = Number(where);
|
|
51
|
+
else if (isJSON(where))
|
|
52
|
+
where = Inison.unstringify(where);
|
|
53
|
+
}
|
|
54
|
+
if (data) {
|
|
55
|
+
if (isJSON(data))
|
|
56
|
+
where = Inison.unstringify(data);
|
|
57
|
+
else
|
|
58
|
+
data = undefined;
|
|
59
|
+
}
|
|
60
|
+
switch (splitedInput[0].toLocaleLowerCase()) {
|
|
61
|
+
case "get":
|
|
62
|
+
console.log(await db.get(table, where, {
|
|
63
|
+
page: Number(page) ?? 1,
|
|
64
|
+
perPage: Number(perPage) ?? 15,
|
|
65
|
+
columns,
|
|
66
|
+
}));
|
|
67
|
+
break;
|
|
68
|
+
case "post":
|
|
69
|
+
{
|
|
70
|
+
const postReturn = await db.post(table, data, {
|
|
71
|
+
page: Number(page) ?? 1,
|
|
72
|
+
perPage: Number(perPage) ?? 15,
|
|
73
|
+
columns,
|
|
74
|
+
}, returnData);
|
|
75
|
+
console.log(postReturn !== null && typeof postReturn === "object"
|
|
76
|
+
? "Item(s) Posted Successfully"
|
|
77
|
+
: postReturn);
|
|
78
|
+
}
|
|
79
|
+
break;
|
|
80
|
+
case "put": {
|
|
81
|
+
const putReturn = await db.put(table, data, where, {
|
|
82
|
+
page: Number(page) ?? 1,
|
|
83
|
+
perPage: Number(perPage) ?? 15,
|
|
84
|
+
columns,
|
|
85
|
+
}, returnData);
|
|
86
|
+
console.log(putReturn !== null && typeof putReturn === "object"
|
|
87
|
+
? "Item(s) Updated Successfully"
|
|
88
|
+
: putReturn);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
case "delete":
|
|
92
|
+
console.log(await db.delete(table, where));
|
|
93
|
+
break;
|
|
94
|
+
default:
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
package/dist/file.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { open, access, writeFile, readFile, constants as fsConstants, unlink, copyFile, appendFile, } from "node:fs/promises";
|
|
1
|
+
import { open, access, writeFile, readFile, constants as fsConstants, unlink, copyFile, appendFile, mkdir, } from "node:fs/promises";
|
|
2
2
|
import { createInterface } from "node:readline";
|
|
3
3
|
import { Transform } from "node:stream";
|
|
4
4
|
import { pipeline } from "node:stream/promises";
|
|
5
5
|
import { createGzip, createGunzip, gunzipSync, gzipSync } from "node:zlib";
|
|
6
|
-
import { join } from "node:path";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
7
|
import { detectFieldType, isArrayOfObjects, isJSON, isNumber, isObject, } from "./utils.js";
|
|
8
|
-
import { encodeID, compare } from "./utils.server.js";
|
|
8
|
+
import { encodeID, compare, exec } from "./utils.server.js";
|
|
9
9
|
import * as Config from "./config.js";
|
|
10
10
|
import Inison from "inison";
|
|
11
11
|
export const lock = async (folderPath, prefix) => {
|
|
@@ -30,6 +30,7 @@ export const unlock = async (folderPath, prefix) => {
|
|
|
30
30
|
catch { }
|
|
31
31
|
};
|
|
32
32
|
export const write = async (filePath, data, disableCompression = false) => {
|
|
33
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
33
34
|
await writeFile(filePath, Config.isCompressionEnabled && !disableCompression
|
|
34
35
|
? gzipSync(String(data))
|
|
35
36
|
: String(data));
|
|
@@ -203,18 +204,28 @@ export async function get(filePath, lineNumbers, fieldType, fieldChildrenType, s
|
|
|
203
204
|
lines[linesCount] = decode(lastLine, fieldType, fieldChildrenType, secretKey);
|
|
204
205
|
}
|
|
205
206
|
else {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
207
|
+
lineNumbers = Array.isArray(lineNumbers) ? lineNumbers : [lineNumbers];
|
|
208
|
+
if (readWholeFile) {
|
|
209
|
+
const lineNumbersArray = new Set(lineNumbers);
|
|
210
|
+
for await (const line of rl) {
|
|
211
|
+
linesCount++;
|
|
212
|
+
if (!lineNumbersArray.has(linesCount))
|
|
213
|
+
continue;
|
|
214
|
+
lines[linesCount] = decode(line, fieldType, fieldChildrenType, secretKey);
|
|
215
|
+
lineNumbersArray.delete(linesCount);
|
|
216
|
+
}
|
|
217
|
+
return [lines, linesCount];
|
|
218
|
+
}
|
|
219
|
+
const command = Config.isCompressionEnabled
|
|
220
|
+
? `zcat ${filePath} | sed -n '${lineNumbers.join("p;")}p'`
|
|
221
|
+
: `sed -n '${lineNumbers.join("p;")}p' ${filePath}`, foundedLines = (await exec(command)).stdout.trim().split(/\r?\n/);
|
|
222
|
+
let index = 0;
|
|
223
|
+
for (const line of foundedLines) {
|
|
224
|
+
lines[lineNumbers[index]] = decode(line, fieldType, fieldChildrenType, secretKey);
|
|
225
|
+
index++;
|
|
215
226
|
}
|
|
216
227
|
}
|
|
217
|
-
return
|
|
228
|
+
return lines;
|
|
218
229
|
}
|
|
219
230
|
finally {
|
|
220
231
|
// Ensure that file handles are closed, even if an error occurred
|
|
@@ -334,32 +345,41 @@ export const append = async (filePath, data) => {
|
|
|
334
345
|
* Note: Creates a temporary file during the process and replaces the original file with it after removing lines.
|
|
335
346
|
*/
|
|
336
347
|
export const remove = async (filePath, linesToDelete) => {
|
|
337
|
-
|
|
338
|
-
let deletedCount = 0;
|
|
339
|
-
const fileHandle = await open(filePath, "r");
|
|
340
|
-
const fileTempPath = filePath.replace(/([^/]+)\/?$/, ".tmp/$1");
|
|
341
|
-
const fileTempHandle = await open(fileTempPath, "w");
|
|
342
|
-
const linesToDeleteArray = new Set(Array.isArray(linesToDelete)
|
|
348
|
+
linesToDelete = Array.isArray(linesToDelete)
|
|
343
349
|
? linesToDelete.map(Number)
|
|
344
|
-
: [Number(linesToDelete)]
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
350
|
+
: [Number(linesToDelete)];
|
|
351
|
+
const fileTempPath = filePath.replace(/([^/]+)\/?$/, ".tmp/$1");
|
|
352
|
+
if (linesToDelete.length < 1000) {
|
|
353
|
+
const command = Config.isCompressionEnabled
|
|
354
|
+
? `zcat ${filePath} | sed "${linesToDelete.join("d;")}d" | gzip > ${fileTempPath}`
|
|
355
|
+
: `sed "${linesToDelete.join("d;")}d" ${filePath} > ${fileTempPath}`;
|
|
356
|
+
await exec(command);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
let linesCount = 0;
|
|
360
|
+
let deletedCount = 0;
|
|
361
|
+
const fileHandle = await open(filePath, "r");
|
|
362
|
+
const fileTempHandle = await open(fileTempPath, "w");
|
|
363
|
+
const linesToDeleteArray = new Set(linesToDelete);
|
|
364
|
+
const rl = readLineInternface(fileHandle);
|
|
365
|
+
await _pipeline(rl, fileTempHandle.createWriteStream(), new Transform({
|
|
366
|
+
transform(line, _, callback) {
|
|
367
|
+
linesCount++;
|
|
368
|
+
if (linesToDeleteArray.has(linesCount)) {
|
|
369
|
+
deletedCount++;
|
|
370
|
+
return callback();
|
|
371
|
+
}
|
|
372
|
+
return callback(null, `${line}\n`);
|
|
373
|
+
},
|
|
374
|
+
final(callback) {
|
|
375
|
+
if (deletedCount === linesCount)
|
|
376
|
+
this.push("\n");
|
|
351
377
|
return callback();
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
this.push("\n");
|
|
358
|
-
return callback();
|
|
359
|
-
},
|
|
360
|
-
}));
|
|
361
|
-
await fileTempHandle.close();
|
|
362
|
-
await fileHandle.close();
|
|
378
|
+
},
|
|
379
|
+
}));
|
|
380
|
+
await fileTempHandle.close();
|
|
381
|
+
await fileHandle.close();
|
|
382
|
+
}
|
|
363
383
|
return [fileTempPath, filePath];
|
|
364
384
|
};
|
|
365
385
|
/**
|
|
@@ -447,7 +467,7 @@ export const search = async (filePath, operator, comparedAtValue, logicalOperato
|
|
|
447
467
|
* Note: Reads through the file line by line to count the total number of lines.
|
|
448
468
|
*/
|
|
449
469
|
export const count = async (filePath) => {
|
|
450
|
-
//
|
|
470
|
+
// Number((await exec(`wc -l < ${filePath}`)).stdout.trim());
|
|
451
471
|
let linesCount = 0;
|
|
452
472
|
if (await isExists(filePath)) {
|
|
453
473
|
let fileHandle = null;
|
package/dist/index.d.ts
CHANGED
|
@@ -47,12 +47,13 @@ export default class Inibase {
|
|
|
47
47
|
database: string;
|
|
48
48
|
table: string | null;
|
|
49
49
|
pageInfo: Record<string, pageInfo>;
|
|
50
|
+
private fileExtension;
|
|
50
51
|
private checkIFunique;
|
|
51
|
-
private isThreadEnabled;
|
|
52
52
|
private totalItems;
|
|
53
53
|
salt: Buffer;
|
|
54
54
|
constructor(database: string, mainFolder?: string, _table?: string | null, _totalItems?: Record<string, number>, _pageInfo?: Record<string, pageInfo>, _isThreadEnabled?: boolean);
|
|
55
55
|
private throwError;
|
|
56
|
+
private getFileExtension;
|
|
56
57
|
private _schemaToIdsPath;
|
|
57
58
|
setTableSchema(tableName: string, schema: Schema): Promise<void>;
|
|
58
59
|
getTableSchema(tableName: string, encodeIDs?: boolean): Promise<Schema | undefined>;
|
|
@@ -81,6 +82,8 @@ export default class Inibase {
|
|
|
81
82
|
put(tableName: string, data: Data[], where: number | string | (number | string)[] | Criteria | undefined, options: Options | undefined, returnUpdatedData: true): Promise<Data[] | null>;
|
|
82
83
|
delete(tableName: string, where?: number | string, _id?: string | string[]): Promise<string | null>;
|
|
83
84
|
delete(tableName: string, where?: (number | string)[] | Criteria, _id?: string | string[]): Promise<string[] | null>;
|
|
85
|
+
delete(tableName: string, where?: number, _id?: string | string[]): Promise<number | null>;
|
|
86
|
+
delete(tableName: string, where?: number[], _id?: string | string[]): Promise<number[] | null>;
|
|
84
87
|
sum(tableName: string, columns: string, where?: number | string | (number | string)[] | Criteria): Promise<number>;
|
|
85
88
|
sum(tableName: string, columns: string[], where?: number | string | (number | string)[] | Criteria): Promise<Record<string, number>>;
|
|
86
89
|
max(tableName: string, columns: string, where?: number | string | (number | string)[] | Criteria): Promise<number>;
|
package/dist/index.js
CHANGED
|
@@ -13,8 +13,8 @@ export default class Inibase {
|
|
|
13
13
|
database;
|
|
14
14
|
table;
|
|
15
15
|
pageInfo;
|
|
16
|
+
fileExtension = ".txt";
|
|
16
17
|
checkIFunique;
|
|
17
|
-
isThreadEnabled = false;
|
|
18
18
|
totalItems;
|
|
19
19
|
salt;
|
|
20
20
|
constructor(database, mainFolder = ".", _table = null, _totalItems = {}, _pageInfo = {}, _isThreadEnabled = false) {
|
|
@@ -23,7 +23,6 @@ export default class Inibase {
|
|
|
23
23
|
this.table = _table;
|
|
24
24
|
this.totalItems = _totalItems;
|
|
25
25
|
this.pageInfo = _pageInfo;
|
|
26
|
-
this.isThreadEnabled = _isThreadEnabled;
|
|
27
26
|
this.checkIFunique = {};
|
|
28
27
|
if (!process.env.INIBASE_SECRET) {
|
|
29
28
|
if (existsSync(".env") &&
|
|
@@ -34,6 +33,19 @@ export default class Inibase {
|
|
|
34
33
|
}
|
|
35
34
|
else
|
|
36
35
|
this.salt = Buffer.from(process.env.INIBASE_SECRET, "hex");
|
|
36
|
+
// try {
|
|
37
|
+
// if (Config.isCompressionEnabled)
|
|
38
|
+
// execSync(
|
|
39
|
+
// `gzip -r ${join(this.folder, this.database)}/*/*.txt 2>/dev/null`,
|
|
40
|
+
// );
|
|
41
|
+
// else
|
|
42
|
+
// execSync(
|
|
43
|
+
// `gunzip -r ${join(
|
|
44
|
+
// this.folder,
|
|
45
|
+
// this.database,
|
|
46
|
+
// )}/*/*.txt.gz 2>/dev/null`,
|
|
47
|
+
// );
|
|
48
|
+
// } catch {}
|
|
37
49
|
}
|
|
38
50
|
throwError(code, variable, language = "en") {
|
|
39
51
|
const errorMessages = {
|
|
@@ -61,6 +73,15 @@ export default class Inibase {
|
|
|
61
73
|
: errorMessage.replaceAll("{variable}", `'${variable.toString()}'`)
|
|
62
74
|
: errorMessage.replaceAll("{variable}", ""));
|
|
63
75
|
}
|
|
76
|
+
getFileExtension = () => {
|
|
77
|
+
let mainExtension = this.fileExtension;
|
|
78
|
+
// TODO: ADD ENCRYPTION
|
|
79
|
+
// if(Config.isEncryptionEnabled)
|
|
80
|
+
// mainExtension += ".enc"
|
|
81
|
+
if (Config.isCompressionEnabled)
|
|
82
|
+
mainExtension += ".gz";
|
|
83
|
+
return mainExtension;
|
|
84
|
+
};
|
|
64
85
|
_schemaToIdsPath = (schema, prefix = "") => {
|
|
65
86
|
const RETURN = {};
|
|
66
87
|
for (const field of schema)
|
|
@@ -70,7 +91,7 @@ export default class Inibase {
|
|
|
70
91
|
Utils.deepMerge(RETURN, this._schemaToIdsPath(field.children, `${(prefix ?? "") + field.key}.`));
|
|
71
92
|
}
|
|
72
93
|
else if (field.id)
|
|
73
|
-
RETURN[field.id] = `${(prefix ?? "") + field.key}.
|
|
94
|
+
RETURN[field.id] = `${(prefix ?? "") + field.key}${this.getFileExtension()}`;
|
|
74
95
|
return RETURN;
|
|
75
96
|
};
|
|
76
97
|
async setTableSchema(tableName, schema) {
|
|
@@ -140,7 +161,7 @@ export default class Inibase {
|
|
|
140
161
|
schema = await this.getTableSchema(tableName);
|
|
141
162
|
if (!schema)
|
|
142
163
|
throw this.throwError("NO_SCHEMA", tableName);
|
|
143
|
-
if (!(await File.isExists(join(tablePath,
|
|
164
|
+
if (!(await File.isExists(join(tablePath, `id${this.getFileExtension()}`))))
|
|
144
165
|
throw this.throwError("NO_ITEMS", tableName);
|
|
145
166
|
return schema;
|
|
146
167
|
}
|
|
@@ -268,7 +289,7 @@ export default class Inibase {
|
|
|
268
289
|
const field = Utils.getField(key, schema);
|
|
269
290
|
if (!field)
|
|
270
291
|
continue;
|
|
271
|
-
const [searchResult, totalLines] = await File.search(join(tablePath, `${key}.
|
|
292
|
+
const [searchResult, totalLines] = await File.search(join(tablePath, `${key}${this.getFileExtension()}`), Array.isArray(values) ? "=" : "[]", values, undefined, field.type, field.children, 1, undefined, false, this.salt);
|
|
272
293
|
if (searchResult && totalLines > 0)
|
|
273
294
|
throw this.throwError("FIELD_UNIQUE", [
|
|
274
295
|
field.key,
|
|
@@ -357,7 +378,7 @@ export default class Inibase {
|
|
|
357
378
|
_addPathToKeys = (obj, path) => {
|
|
358
379
|
const newObject = {};
|
|
359
380
|
for (const key in obj)
|
|
360
|
-
newObject[join(path, `${key}.
|
|
381
|
+
newObject[join(path, `${key}${this.getFileExtension()}`)] = obj[key];
|
|
361
382
|
return newObject;
|
|
362
383
|
};
|
|
363
384
|
joinPathesContents(mainPath, data) {
|
|
@@ -407,7 +428,7 @@ export default class Inibase {
|
|
|
407
428
|
async getItemsFromSchema(tableName, schema, linesNumber, options, prefix) {
|
|
408
429
|
const tablePath = join(this.folder, this.database, tableName);
|
|
409
430
|
let RETURN = {};
|
|
410
|
-
await
|
|
431
|
+
for await (const field of schema) {
|
|
411
432
|
if ((field.type === "array" ||
|
|
412
433
|
(Array.isArray(field.type) && field.type.includes("array"))) &&
|
|
413
434
|
field.children) {
|
|
@@ -415,82 +436,86 @@ export default class Inibase {
|
|
|
415
436
|
if (field.children.filter((children) => children.type === "array" &&
|
|
416
437
|
Utils.isArrayOfObjects(children.children)).length) {
|
|
417
438
|
// one of children has array field type and has children array of object = Schema
|
|
418
|
-
|
|
419
|
-
Utils.isArrayOfObjects(children.children)), linesNumber, options, `${(prefix ?? "") + field.key}.`)
|
|
420
|
-
|
|
439
|
+
const childItems = await this.getItemsFromSchema(tableName, field.children.filter((children) => children.type === "array" &&
|
|
440
|
+
Utils.isArrayOfObjects(children.children)), linesNumber, options, `${(prefix ?? "") + field.key}.`);
|
|
441
|
+
if (childItems)
|
|
442
|
+
for (const [index, item] of Object.entries(childItems))
|
|
443
|
+
this._getItemsFromSchemaHelper(RETURN, item, index, field);
|
|
421
444
|
field.children = field.children.filter((children) => children.type !== "array" ||
|
|
422
445
|
!Utils.isArrayOfObjects(children.children));
|
|
423
446
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
for (let _i = 0; _i < value.length; _i++) {
|
|
453
|
-
if (value[_i] === null ||
|
|
454
|
-
(Array.isArray(value[_i]) &&
|
|
455
|
-
Utils.isArrayOfNulls(value[_i])))
|
|
456
|
-
continue;
|
|
457
|
-
if (!RETURN[index][field.key][_i])
|
|
458
|
-
RETURN[index][field.key][_i] = {};
|
|
459
|
-
RETURN[index][field.key][_i][key] = value[_i];
|
|
447
|
+
const fieldItems = await this.getItemsFromSchema(tableName, field.children, linesNumber, options, `${(prefix ?? "") + field.key}.`);
|
|
448
|
+
if (fieldItems)
|
|
449
|
+
for (const [index, item] of Object.entries(fieldItems)) {
|
|
450
|
+
if (!RETURN[index])
|
|
451
|
+
RETURN[index] = {};
|
|
452
|
+
if (Utils.isObject(item)) {
|
|
453
|
+
if (!Utils.isArrayOfNulls(Object.values(item))) {
|
|
454
|
+
if (RETURN[index][field.key])
|
|
455
|
+
Object.entries(item).forEach(([key, value], _index) => {
|
|
456
|
+
for (let _index = 0; _index < value.length; _index++)
|
|
457
|
+
if (RETURN[index][field.key][_index])
|
|
458
|
+
Object.assign(RETURN[index][field.key][_index], {
|
|
459
|
+
[key]: value[_index],
|
|
460
|
+
});
|
|
461
|
+
else
|
|
462
|
+
RETURN[index][field.key][_index] = {
|
|
463
|
+
[key]: value[_index],
|
|
464
|
+
};
|
|
465
|
+
});
|
|
466
|
+
else if (Object.values(item).every((_i) => Utils.isArrayOfArrays(_i) || Array.isArray(_i)) &&
|
|
467
|
+
prefix)
|
|
468
|
+
RETURN[index][field.key] = item;
|
|
469
|
+
else {
|
|
470
|
+
RETURN[index][field.key] = [];
|
|
471
|
+
Object.entries(item).forEach(([key, value], _ind) => {
|
|
472
|
+
if (!Array.isArray(value)) {
|
|
473
|
+
RETURN[index][field.key][_ind] = {};
|
|
474
|
+
RETURN[index][field.key][_ind][key] = value;
|
|
460
475
|
}
|
|
461
|
-
|
|
476
|
+
else
|
|
477
|
+
for (let _i = 0; _i < value.length; _i++) {
|
|
478
|
+
if (value[_i] === null ||
|
|
479
|
+
(Array.isArray(value[_i]) &&
|
|
480
|
+
Utils.isArrayOfNulls(value[_i])))
|
|
481
|
+
continue;
|
|
482
|
+
if (!RETURN[index][field.key][_i])
|
|
483
|
+
RETURN[index][field.key][_i] = {};
|
|
484
|
+
RETURN[index][field.key][_i][key] = value[_i];
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
}
|
|
462
488
|
}
|
|
489
|
+
else
|
|
490
|
+
RETURN[index][field.key] = null;
|
|
463
491
|
}
|
|
464
492
|
else
|
|
465
|
-
RETURN[index][field.key] =
|
|
493
|
+
RETURN[index][field.key] = item;
|
|
466
494
|
}
|
|
467
|
-
else
|
|
468
|
-
RETURN[index][field.key] = item;
|
|
469
|
-
}
|
|
470
495
|
}
|
|
471
496
|
else if (field.children === "table" ||
|
|
472
497
|
(Array.isArray(field.type) && field.type.includes("table")) ||
|
|
473
498
|
(Array.isArray(field.children) && field.children.includes("table"))) {
|
|
474
499
|
if (field.table &&
|
|
475
500
|
(await File.isExists(join(this.folder, this.database, field.table))) &&
|
|
476
|
-
(await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}.
|
|
501
|
+
(await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`)))) {
|
|
477
502
|
if (options.columns)
|
|
478
503
|
options.columns = options.columns
|
|
479
504
|
.filter((column) => column.includes(`${field.key}.`))
|
|
480
505
|
.map((column) => column.replace(`${field.key}.`, ""));
|
|
481
|
-
const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}.
|
|
506
|
+
const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, field.type, field.children, this.salt);
|
|
482
507
|
if (items)
|
|
483
|
-
await
|
|
508
|
+
for await (const [index, item] of Object.entries(items)) {
|
|
484
509
|
if (!RETURN[index])
|
|
485
510
|
RETURN[index] = {};
|
|
486
511
|
RETURN[index][field.key] = item
|
|
487
512
|
? await this.get(field.table, item, options)
|
|
488
513
|
: this.getDefaultValue(field);
|
|
489
|
-
}
|
|
514
|
+
}
|
|
490
515
|
}
|
|
491
516
|
}
|
|
492
|
-
else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}.
|
|
493
|
-
const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}.
|
|
517
|
+
else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`))) {
|
|
518
|
+
const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, field.type, field.children, this.salt);
|
|
494
519
|
if (items)
|
|
495
520
|
for (const [index, item] of Object.entries(items)) {
|
|
496
521
|
if (!RETURN[index])
|
|
@@ -500,40 +525,42 @@ export default class Inibase {
|
|
|
500
525
|
}
|
|
501
526
|
}
|
|
502
527
|
else if (field.type === "object") {
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
528
|
+
const fieldItems = await this.getItemsFromSchema(tableName, field.children, linesNumber, options, `${(prefix ?? "") + field.key}.`);
|
|
529
|
+
if (fieldItems)
|
|
530
|
+
for (const [index, item] of Object.entries(fieldItems)) {
|
|
531
|
+
if (!RETURN[index])
|
|
532
|
+
RETURN[index] = {};
|
|
533
|
+
if (Utils.isObject(item)) {
|
|
534
|
+
if (!Object.values(item).every((i) => i === null))
|
|
535
|
+
RETURN[index][field.key] = item;
|
|
536
|
+
else
|
|
537
|
+
RETURN[index][field.key] = null;
|
|
538
|
+
}
|
|
509
539
|
else
|
|
510
540
|
RETURN[index][field.key] = null;
|
|
511
541
|
}
|
|
512
|
-
else
|
|
513
|
-
RETURN[index][field.key] = null;
|
|
514
|
-
}
|
|
515
542
|
}
|
|
516
543
|
else if (field.type === "table") {
|
|
517
544
|
if (field.table &&
|
|
518
545
|
(await File.isExists(join(this.folder, this.database, field.table))) &&
|
|
519
|
-
(await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}.
|
|
546
|
+
(await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`)))) {
|
|
520
547
|
if (options.columns)
|
|
521
548
|
options.columns = options.columns
|
|
522
549
|
.filter((column) => column.includes(`${field.key}.`))
|
|
523
550
|
.map((column) => column.replace(`${field.key}.`, ""));
|
|
524
|
-
const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}.
|
|
551
|
+
const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, "number", undefined, this.salt);
|
|
525
552
|
if (items)
|
|
526
|
-
await
|
|
553
|
+
for await (const [index, item] of Object.entries(items)) {
|
|
527
554
|
if (!RETURN[index])
|
|
528
555
|
RETURN[index] = {};
|
|
529
556
|
RETURN[index][field.key] = item
|
|
530
557
|
? await this.get(field.table, item, options)
|
|
531
558
|
: this.getDefaultValue(field);
|
|
532
|
-
}
|
|
559
|
+
}
|
|
533
560
|
}
|
|
534
561
|
}
|
|
535
|
-
else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}.
|
|
536
|
-
const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}.
|
|
562
|
+
else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`))) {
|
|
563
|
+
const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension()}`), linesNumber, field.type, field.children, this.salt);
|
|
537
564
|
if (items)
|
|
538
565
|
for (const [index, item] of Object.entries(items)) {
|
|
539
566
|
if (!RETURN[index])
|
|
@@ -546,7 +573,7 @@ export default class Inibase {
|
|
|
546
573
|
{ ...data, [field.key]: this.getDefaultValue(field) },
|
|
547
574
|
]));
|
|
548
575
|
}
|
|
549
|
-
}
|
|
576
|
+
}
|
|
550
577
|
return RETURN;
|
|
551
578
|
}
|
|
552
579
|
async applyCriteria(tableName, schema, options, criteria, allTrue) {
|
|
@@ -634,7 +661,7 @@ export default class Inibase {
|
|
|
634
661
|
searchOperator = "=";
|
|
635
662
|
searchComparedAtValue = value;
|
|
636
663
|
}
|
|
637
|
-
const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, `${key}.
|
|
664
|
+
const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, `${key}${this.getFileExtension()}`), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, field?.type, field?.children, options.perPage, options.page - 1 * options.perPage + 1, true, this.salt);
|
|
638
665
|
if (searchResult) {
|
|
639
666
|
RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).map(([id, value]) => [
|
|
640
667
|
id,
|
|
@@ -679,7 +706,7 @@ export default class Inibase {
|
|
|
679
706
|
}
|
|
680
707
|
async clearCache(tablePath) {
|
|
681
708
|
await Promise.all((await readdir(join(tablePath, ".cache")))
|
|
682
|
-
?.filter((fileName) => fileName !==
|
|
709
|
+
?.filter((fileName) => fileName !== `pagination${this.fileExtension}`)
|
|
683
710
|
.map(async (file) => unlink(join(tablePath, ".cache", file))));
|
|
684
711
|
}
|
|
685
712
|
async get(tableName, where, options = {
|
|
@@ -713,16 +740,14 @@ export default class Inibase {
|
|
|
713
740
|
RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
|
|
714
741
|
index +
|
|
715
742
|
1), options));
|
|
716
|
-
if (
|
|
717
|
-
(await File.
|
|
718
|
-
this.totalItems[`${tableName}-*`] = Number((await File.read(join(tablePath, ".cache", "pagination.inib"), true)).split(",")[1]);
|
|
743
|
+
if (await File.isExists(join(tablePath, ".cache", `pagination${this.fileExtension}`)))
|
|
744
|
+
this.totalItems[`${tableName}-*`] = Number((await File.read(join(tablePath, ".cache", `pagination${this.fileExtension}`), true)).split(",")[1]);
|
|
719
745
|
else {
|
|
720
|
-
let [lastId, totalItems] = await File.get(join(tablePath,
|
|
746
|
+
let [lastId, totalItems] = await File.get(join(tablePath, `id${this.getFileExtension()}`), -1, "number", undefined, this.salt, true);
|
|
721
747
|
if (lastId)
|
|
722
748
|
lastId = Number(Object.keys(lastId)?.[0] ?? 0);
|
|
723
749
|
this.totalItems[`${tableName}-*`] = totalItems;
|
|
724
|
-
|
|
725
|
-
await File.write(join(tablePath, ".cache", "pagination.inib"), `${lastId},${totalItems}`, true);
|
|
750
|
+
await File.write(join(tablePath, ".cache", `pagination${this.fileExtension}`), `${lastId},${totalItems}`, true);
|
|
726
751
|
}
|
|
727
752
|
}
|
|
728
753
|
else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
|
|
@@ -745,7 +770,7 @@ export default class Inibase {
|
|
|
745
770
|
let Ids = where;
|
|
746
771
|
if (!Array.isArray(Ids))
|
|
747
772
|
Ids = [Ids];
|
|
748
|
-
const [lineNumbers, countItems] = await File.search(join(tablePath,
|
|
773
|
+
const [lineNumbers, countItems] = await File.search(join(tablePath, `id${this.getFileExtension()}`), "[]", Ids.map((id) => Utils.isNumber(id) ? Number(id) : UtilsServer.decodeID(id, this.salt)), undefined, "number", undefined, Ids.length, 0, !this.totalItems[`${tableName}-*`], this.salt);
|
|
749
774
|
if (!lineNumbers)
|
|
750
775
|
throw this.throwError("NO_RESULTS", tableName);
|
|
751
776
|
if (onlyLinesNumbers)
|
|
@@ -762,7 +787,7 @@ export default class Inibase {
|
|
|
762
787
|
let cachedFilePath = "";
|
|
763
788
|
// Criteria
|
|
764
789
|
if (Config.isCacheEnabled)
|
|
765
|
-
cachedFilePath = join(tablePath, ".cache", `${UtilsServer.hashString(inspect(where, { sorted: true }))}.
|
|
790
|
+
cachedFilePath = join(tablePath, ".cache", `${UtilsServer.hashString(inspect(where, { sorted: true }))}${this.fileExtension}`);
|
|
766
791
|
if (Config.isCacheEnabled && (await File.isExists(cachedFilePath))) {
|
|
767
792
|
const cachedItems = (await File.read(cachedFilePath, true)).split(",");
|
|
768
793
|
this.totalItems[`${tableName}-*`] = cachedItems.length;
|
|
@@ -815,15 +840,14 @@ export default class Inibase {
|
|
|
815
840
|
let lastId = 0, totalItems = 0, renameList = [];
|
|
816
841
|
try {
|
|
817
842
|
await File.lock(join(tablePath, ".tmp"), keys);
|
|
818
|
-
if (await File.isExists(join(tablePath,
|
|
819
|
-
if (
|
|
820
|
-
(await File.
|
|
821
|
-
[lastId, totalItems] = (await File.read(join(tablePath, ".cache", "pagination.inib"), true))
|
|
843
|
+
if (await File.isExists(join(tablePath, `id${this.getFileExtension()}`))) {
|
|
844
|
+
if (await File.isExists(join(tablePath, ".cache", `pagination${this.fileExtension}`)))
|
|
845
|
+
[lastId, totalItems] = (await File.read(join(tablePath, ".cache", `pagination${this.fileExtension}`), true))
|
|
822
846
|
.split(",")
|
|
823
847
|
.map(Number);
|
|
824
848
|
else {
|
|
825
849
|
let lastIdObj = null;
|
|
826
|
-
[lastIdObj, totalItems] = await File.get(join(tablePath,
|
|
850
|
+
[lastIdObj, totalItems] = await File.get(join(tablePath, `id${this.getFileExtension()}`), -1, "number", undefined, this.salt, true);
|
|
827
851
|
if (lastIdObj)
|
|
828
852
|
lastId = Number(Object.keys(lastIdObj)?.[0] ?? 0);
|
|
829
853
|
}
|
|
@@ -852,10 +876,9 @@ export default class Inibase {
|
|
|
852
876
|
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
853
877
|
renameList = [];
|
|
854
878
|
totalItems += Array.isArray(RETURN) ? RETURN.length : 1;
|
|
855
|
-
if (Config.isCacheEnabled)
|
|
879
|
+
if (Config.isCacheEnabled)
|
|
856
880
|
await this.clearCache(tablePath);
|
|
857
|
-
|
|
858
|
-
}
|
|
881
|
+
await File.write(join(tablePath, ".cache", `pagination${this.fileExtension}`), `${lastId},${totalItems}`, true);
|
|
859
882
|
if (returnPostedData)
|
|
860
883
|
return this.get(tableName, Config.isReverseEnabled
|
|
861
884
|
? Array.isArray(RETURN)
|
|
@@ -895,13 +918,12 @@ export default class Inibase {
|
|
|
895
918
|
return this.put(tableName, data, data.id);
|
|
896
919
|
}
|
|
897
920
|
let totalItems;
|
|
898
|
-
if (
|
|
899
|
-
(await File.
|
|
900
|
-
totalItems = (await File.read(join(tablePath, ".cache", "pagination.inib"), true))
|
|
921
|
+
if (await File.isExists(join(tablePath, ".cache", `pagination${this.fileExtension}`)))
|
|
922
|
+
totalItems = (await File.read(join(tablePath, ".cache", `pagination${this.fileExtension}`), true))
|
|
901
923
|
.split(",")
|
|
902
924
|
.map(Number)[1];
|
|
903
925
|
else
|
|
904
|
-
totalItems = await File.count(join(tablePath,
|
|
926
|
+
totalItems = await File.count(join(tablePath, `id${this.getFileExtension()}`));
|
|
905
927
|
const pathesContents = this.joinPathesContents(tablePath, {
|
|
906
928
|
...(({ id, ...restOfData }) => restOfData)(data),
|
|
907
929
|
updatedAt: Date.now(),
|
|
@@ -998,26 +1020,23 @@ export default class Inibase {
|
|
|
998
1020
|
if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
|
|
999
1021
|
Utils.isNumber(where)) {
|
|
1000
1022
|
// "where" in this case, is the line(s) number(s) and not id(s)
|
|
1001
|
-
const files = (await readdir(tablePath))?.filter((fileName) => fileName.endsWith(
|
|
1023
|
+
const files = (await readdir(tablePath))?.filter((fileName) => fileName.endsWith(this.getFileExtension()));
|
|
1002
1024
|
if (files.length) {
|
|
1003
|
-
if (!_id)
|
|
1004
|
-
_id = Object.entries((await File.get(join(tablePath, "id.inib"), where, "number", undefined, this.salt)) ?? {}).map(([_, id]) => UtilsServer.encodeID(id, this.salt));
|
|
1005
|
-
if (!_id.length)
|
|
1006
|
-
throw this.throwError("NO_RESULTS", tableName);
|
|
1007
1025
|
try {
|
|
1008
1026
|
await File.lock(join(tablePath, ".tmp"));
|
|
1009
1027
|
await Promise.all(files.map(async (file) => renameList.push(await File.remove(join(tablePath, file), where))));
|
|
1010
1028
|
await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
1011
|
-
if (Config.isCacheEnabled)
|
|
1029
|
+
if (Config.isCacheEnabled)
|
|
1012
1030
|
await this.clearCache(tablePath);
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
}
|
|
1031
|
+
if (await File.isExists(join(tablePath, ".cache", `pagination${this.fileExtension}`))) {
|
|
1032
|
+
const [lastId, totalItems] = (await File.read(join(tablePath, ".cache", `pagination${this.fileExtension}`), true))
|
|
1033
|
+
.split(",")
|
|
1034
|
+
.map(Number);
|
|
1035
|
+
await File.write(join(tablePath, ".cache", `pagination${this.fileExtension}`), `${lastId},${totalItems - (Array.isArray(where) ? where.length : 1)}`, true);
|
|
1019
1036
|
}
|
|
1020
|
-
|
|
1037
|
+
if (_id)
|
|
1038
|
+
return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
|
|
1039
|
+
return where;
|
|
1021
1040
|
}
|
|
1022
1041
|
finally {
|
|
1023
1042
|
if (renameList.length)
|
|
@@ -1040,7 +1059,7 @@ export default class Inibase {
|
|
|
1040
1059
|
if (!Array.isArray(columns))
|
|
1041
1060
|
columns = [columns];
|
|
1042
1061
|
for await (const column of columns) {
|
|
1043
|
-
const columnPath = join(tablePath, `${column}.
|
|
1062
|
+
const columnPath = join(tablePath, `${column}${this.getFileExtension()}`);
|
|
1044
1063
|
if (await File.isExists(columnPath)) {
|
|
1045
1064
|
if (where) {
|
|
1046
1065
|
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
@@ -1060,7 +1079,7 @@ export default class Inibase {
|
|
|
1060
1079
|
if (!Array.isArray(columns))
|
|
1061
1080
|
columns = [columns];
|
|
1062
1081
|
for await (const column of columns) {
|
|
1063
|
-
const columnPath = join(tablePath, `${column}.
|
|
1082
|
+
const columnPath = join(tablePath, `${column}${this.getFileExtension()}`);
|
|
1064
1083
|
if (await File.isExists(columnPath)) {
|
|
1065
1084
|
if (where) {
|
|
1066
1085
|
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
@@ -1080,7 +1099,7 @@ export default class Inibase {
|
|
|
1080
1099
|
if (!Array.isArray(columns))
|
|
1081
1100
|
columns = [columns];
|
|
1082
1101
|
for await (const column of columns) {
|
|
1083
|
-
const columnPath = join(tablePath, `${column}.
|
|
1102
|
+
const columnPath = join(tablePath, `${column}${this.getFileExtension()}`);
|
|
1084
1103
|
if (await File.isExists(columnPath)) {
|
|
1085
1104
|
if (where) {
|
|
1086
1105
|
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
@@ -1098,7 +1117,6 @@ export default class Inibase {
|
|
|
1098
1117
|
page: 1,
|
|
1099
1118
|
perPage: 15,
|
|
1100
1119
|
}) {
|
|
1101
|
-
// TO-DO: Cache Results based on "Columns and Sort Direction"
|
|
1102
1120
|
const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
|
|
1103
1121
|
// Default values for page and perPage
|
|
1104
1122
|
options.page = options.page || 1;
|
|
@@ -1122,7 +1140,7 @@ export default class Inibase {
|
|
|
1122
1140
|
cacheKey = UtilsServer.hashString(inspect(sortArray, { sorted: true }));
|
|
1123
1141
|
if (where) {
|
|
1124
1142
|
const lineNumbers = await this.get(tableName, where, undefined, undefined, true, schema);
|
|
1125
|
-
keepItems = Object.values((await File.get(join(tablePath,
|
|
1143
|
+
keepItems = Object.values((await File.get(join(tablePath, `id${this.getFileExtension()}`), lineNumbers, "number", undefined, this.salt)) ?? {}).map(Number);
|
|
1126
1144
|
isLineNumbers = false;
|
|
1127
1145
|
if (!keepItems.length)
|
|
1128
1146
|
throw this.throwError("NO_RESULTS", tableName);
|
|
@@ -1132,7 +1150,7 @@ export default class Inibase {
|
|
|
1132
1150
|
keepItems = Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
|
|
1133
1151
|
index +
|
|
1134
1152
|
1);
|
|
1135
|
-
const filesPathes = [["id", true], ...sortArray].map((column) => join(tablePath, `${column[0]}.
|
|
1153
|
+
const filesPathes = [["id", true], ...sortArray].map((column) => join(tablePath, `${column[0]}${this.getFileExtension()}`));
|
|
1136
1154
|
// Construct the paste command to merge files and filter lines by IDs
|
|
1137
1155
|
const pasteCommand = `paste ${filesPathes.join(" ")}`;
|
|
1138
1156
|
// Construct the sort command dynamically based on the number of files for sorting
|
|
@@ -1157,10 +1175,10 @@ export default class Inibase {
|
|
|
1157
1175
|
await File.lock(join(tablePath, ".tmp"), cacheKey);
|
|
1158
1176
|
// Combine the commands
|
|
1159
1177
|
// Execute the command synchronously
|
|
1160
|
-
const { stdout
|
|
1161
|
-
? (await File.isExists(join(tablePath, ".cache", `${cacheKey}.
|
|
1162
|
-
? `${awkCommand} ${join(tablePath, ".cache", `${cacheKey}.
|
|
1163
|
-
: `${pasteCommand} | ${sortCommand} -o ${join(tablePath, ".cache", `${cacheKey}.
|
|
1178
|
+
const { stdout } = await UtilsServer.exec(Config.isCacheEnabled
|
|
1179
|
+
? (await File.isExists(join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)))
|
|
1180
|
+
? `${awkCommand} ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}`
|
|
1181
|
+
: `${pasteCommand} | ${sortCommand} -o ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)} && ${awkCommand} ${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}`
|
|
1164
1182
|
: `${pasteCommand} | ${sortCommand} | ${awkCommand}`, {
|
|
1165
1183
|
encoding: "utf-8",
|
|
1166
1184
|
});
|
|
@@ -1169,7 +1187,7 @@ export default class Inibase {
|
|
|
1169
1187
|
const outputArray = lines.map((line) => {
|
|
1170
1188
|
const splitedFileColumns = line.split("\t"); // Assuming tab-separated columns
|
|
1171
1189
|
const outputObject = {};
|
|
1172
|
-
// Extract values for each file, including
|
|
1190
|
+
// Extract values for each file, including `id${this.getFileExtension()}`
|
|
1173
1191
|
filesPathes.forEach((fileName, index) => {
|
|
1174
1192
|
const Field = Utils.getField(parse(fileName).name, schema);
|
|
1175
1193
|
if (Field)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "inibase",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.58",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Karim Amahtil",
|
|
6
6
|
"email": "karim.amahtil@gmail.com"
|
|
@@ -47,6 +47,9 @@
|
|
|
47
47
|
"pocketbase"
|
|
48
48
|
],
|
|
49
49
|
"license": "MIT",
|
|
50
|
+
"bin": {
|
|
51
|
+
"inibase": "./dist/cli.js"
|
|
52
|
+
},
|
|
50
53
|
"type": "module",
|
|
51
54
|
"types": "./dist",
|
|
52
55
|
"typesVersions": {
|
|
@@ -73,13 +76,14 @@
|
|
|
73
76
|
"tinybench": "^2.6.0"
|
|
74
77
|
},
|
|
75
78
|
"dependencies": {
|
|
79
|
+
"commander": "^12.0.0",
|
|
80
|
+
"dotenv": "^16.4.5",
|
|
76
81
|
"inison": "^1.0.0-rc.2"
|
|
77
82
|
},
|
|
78
83
|
"scripts": {
|
|
79
84
|
"build": "tsc",
|
|
80
|
-
"
|
|
81
|
-
"benchmark": "
|
|
82
|
-
"benchmark:
|
|
83
|
-
"benchmark:bulk": "npx tsx --expose-gc --env-file=.env ./benchmark/bulk"
|
|
85
|
+
"benchmark": "tsx --env-file=.env ./benchmark/index",
|
|
86
|
+
"benchmark:single": "tsx --expose-gc --env-file=.env ./benchmark/single",
|
|
87
|
+
"benchmark:bulk": "tsx --expose-gc --env-file=.env ./benchmark/bulk"
|
|
84
88
|
}
|
|
85
89
|
}
|