Skip to content

Commit 5c2a98d

Browse files
committed
Implement a list of operations <, >, =, <> for the size property
1 parent e09fd65 commit 5c2a98d

9 files changed

Lines changed: 8489 additions & 7 deletions

File tree

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,25 @@ export interface IDirEntry {
6363
}
6464
```
6565

66-
## Example queries
66+
## Queries
67+
68+
### Supported
6769

6870
`select * from .`
6971

7072
`select * from root`
7173

7274
`select * from root/sub-a`
7375

74-
## TODO
76+
`select * from root where size > 1000000`
77+
78+
`select * from root where size < 1000000`
79+
80+
`select * from root where size = 1000000`
81+
82+
`select * from root where size <> 1000000`
7583

76-
`select * from root where size > 0`
84+
### TODO
7785

7886
`select * from root where isDirectory = true`
7987

deps.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ export {
55
export {
66
assert,
77
assertExists,
8+
assertEquals,
9+
assertThrows
810
} from "https://deno.land/std@0.82.0/testing/asserts.ts";

mod_test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { fsselect } from "./mod.ts";
44

55
Deno.test("if 'select * from .' works", async () => {
66
const result = await fsselect("select * from .");
7-
assert(result.length === 16);
7+
assert(result.length === 18);
88
});
99

1010
Deno.test("if 'select * from root' works", async () => {
@@ -14,5 +14,11 @@ Deno.test("if 'select * from root' works", async () => {
1414

1515
Deno.test("if 'select * from root/test_folder_with_file' works", async () => {
1616
const result = await fsselect("select * from root/test_folder_with_file");
17-
assert(result.length === 1);
17+
assert(result.length === 2);
1818
});
19+
20+
Deno.test("if 'select * from root/test_folder_with_file where size > 1000000' works", async () => {
21+
const result = await fsselect("select * from root/test_folder_with_file where size > 1000000");
22+
assert(result.length === 1);
23+
assert(result[0].name === 'b-file-1MB.txt');
24+
});

root/test_folder_with_file/b-file-1MB.txt

Lines changed: 8192 additions & 0 deletions
Large diffs are not rendered by default.

select.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { IDirEntry, IQuery } from "./types.ts";
2+
import { where } from "./where.ts";
23

34
export async function select(query: IQuery): Promise<IDirEntry[]> {
45
const output: IDirEntry[] = [];
@@ -11,13 +12,17 @@ export async function select(query: IQuery): Promise<IDirEntry[]> {
1112
isDirectory: entry.isDirectory,
1213
isSymlink: entry.isSymlink,
1314
};
15+
1416
const path = `${query.from}/${entry.name}`;
1517
const info = await Deno.lstat(path);
1618
row.size = info.size;
1719
row.accessedAt = info.atime;
1820
row.createdAt = info.birthtime;
1921
row.modifiedAt = info.mtime;
20-
output.push(row);
22+
23+
if (where(row, query.where)) {
24+
output.push(row);
25+
}
2126
}
2227
} catch (err) {
2328
}

select_test.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Deno.test("if an empty array is returned when the folder is missing", async () =
1111
type: "select",
1212
fields: ["*"],
1313
from: "missing-folder",
14+
where: null,
1415
};
1516

1617
const result = await select(query);
@@ -23,6 +24,7 @@ Deno.test("if an empty array is returned when the path is a file", async () => {
2324
type: "select",
2425
fields: ["*"],
2526
from: "select_test.ts",
27+
where: null,
2628
};
2729

2830
const result = await select(query);
@@ -35,11 +37,12 @@ Deno.test("if an existing file entry is returned when the path is a correct fold
3537
type: "select",
3638
fields: ["*"],
3739
from: "root/test_folder_with_file",
40+
where: null,
3841
};
3942

4043
const result = await select(query);
4144

42-
assert(result.length === 1);
45+
assert(result.length === 2);
4346
assertEquals(result[0].name, "a-file.txt");
4447
assertEquals(result[0].size, 12);
4548
assertEquals(result[0].isFile, true);
@@ -55,6 +58,7 @@ Deno.test("if an existing directory entry is returned when the path is a correct
5558
type: "select",
5659
fields: ["*"],
5760
from: "root/test_folder_with_folder",
61+
where: null,
5862
};
5963

6064
const result = await select(query);
@@ -68,3 +72,31 @@ Deno.test("if an existing directory entry is returned when the path is a correct
6872
assert(result[0].createdAt);
6973
assert(result[0].modifiedAt);
7074
});
75+
76+
Deno.test("if a 1MB file is returned when the where clause is correct", async () => {
77+
const query: IQuery = {
78+
type: "select",
79+
fields: ["*"],
80+
from: "root/test_folder_with_file",
81+
where: {
82+
conditions: [
83+
{
84+
left: "size",
85+
op: "GreaterThan",
86+
right: 1000000,
87+
},
88+
],
89+
},
90+
};
91+
92+
const result = await select(query);
93+
94+
assert(result.length === 1);
95+
assertEquals(result[0].name, "b-file-1MB.txt");
96+
assertEquals(result[0].isFile, true);
97+
assertEquals(result[0].isDirectory, false);
98+
assertEquals(result[0].isSymlink, false);
99+
assert(result[0].accessedAt);
100+
assert(result[0].createdAt);
101+
assert(result[0].modifiedAt);
102+
});

types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export interface IQuery {
22
type: string;
33
fields: string[];
44
from: string;
5+
where: IWhereClause | null;
56
}
67

78
export interface IDirEntry {
@@ -14,3 +15,13 @@ export interface IDirEntry {
1415
isDirectory?: boolean;
1516
isSymlink?: boolean;
1617
}
18+
19+
export interface IWhereClause {
20+
conditions: IWhereCondition[];
21+
}
22+
23+
export interface IWhereCondition {
24+
left: string;
25+
op: 'Different' | 'GreaterThan' | 'LessThan' | 'Equal' | 'Like';
26+
right: string | number;
27+
}

where.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { IDirEntry, IWhereClause, IWhereCondition } from "./types.ts";
2+
3+
export function where(entry: IDirEntry, where: IWhereClause | null): boolean {
4+
const conditions = where?.conditions;
5+
if (!conditions?.length) {
6+
return true;
7+
}
8+
9+
for (const condition of conditions) {
10+
if (!meet(entry, condition)) {
11+
return false;
12+
}
13+
}
14+
15+
return true;
16+
}
17+
18+
function meet(entry: IDirEntry, condition: IWhereCondition): boolean {
19+
const { left, op, right } = condition;
20+
21+
if (left === "size") {
22+
const operations = {
23+
"GreaterThan": greaterThan,
24+
"LessThan": lessThan,
25+
"Equal": equal,
26+
"Different": different,
27+
"Like": null,
28+
};
29+
30+
const operation = operations[op];
31+
if (!operation) {
32+
throw new Error(
33+
`The operation '${op}' is not supported for the '${left}' property`,
34+
);
35+
}
36+
37+
return operation(entry.size, right);
38+
}
39+
40+
return false;
41+
}
42+
43+
function greaterThan(
44+
left: number | undefined,
45+
right: number | string,
46+
): boolean {
47+
return typeof left !== "undefined" && left > right;
48+
}
49+
50+
function lessThan(left: number | undefined, right: number | string): boolean {
51+
return typeof left !== "undefined" && left < right;
52+
}
53+
54+
function equal(left: number | undefined, right: number | string): boolean {
55+
return typeof left !== "undefined" && left === right;
56+
}
57+
58+
function different(left: number | undefined, right: number | string): boolean {
59+
return typeof left !== "undefined" && left !== right;
60+
}

0 commit comments

Comments
 (0)