CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/database/user-query/user-get-query.ts
Views: 687
1
import {
2
OPERATORS,
3
UserOrProjectQuery,
4
isToOperand,
5
SUPPORTED_TIME_UNITS,
6
} from "@cocalc/util/schema";
7
import { quoteField } from "../postgres/schema/util";
8
import { is_object } from "@cocalc/util/misc";
9
10
export function queryIsCmp(val): false | string {
11
if (!is_object(val)) {
12
return false;
13
}
14
const keys = Object.keys(val);
15
if (keys.length != 1) {
16
return false;
17
}
18
if (OPERATORS.includes(keys[0] as any)) {
19
return keys[0];
20
}
21
return false;
22
}
23
24
// Additional where object condition imposed by user's get query
25
export function userGetQueryFilter(
26
user_query: object,
27
client_query: UserOrProjectQuery<any>,
28
): { [expr: string]: any } {
29
if (client_query.get == null) {
30
// no get queries allowed (this is mainly to make typescript happy below.)
31
return {};
32
}
33
34
// If the schema lists the value in a get query as 'null', then we remove it;
35
// nulls means it was only there to be used by the initial where filter
36
// part of the query.
37
for (const field in client_query.get.fields) {
38
const val = client_query.get.fields[field];
39
if (val === "null") {
40
delete user_query[field];
41
}
42
}
43
44
const where: { [expr: string]: any } = {};
45
for (const field in user_query) {
46
const val = user_query[field];
47
if (val == null) continue;
48
if (
49
client_query.get.remove_from_query != null &&
50
client_query.get.remove_from_query.includes(field)
51
) {
52
// do not include any field that explicitly excluded from the query
53
continue;
54
}
55
if (!queryIsCmp(val)) {
56
where[`${quoteField(field)} = $`] = val;
57
continue;
58
}
59
60
// It's a comparison query, e.g., {field : {'<=' : 5}}
61
for (let op in val) {
62
const v = val[op];
63
if (op === "==") {
64
// not in SQL, but natural for our clients to use it
65
op = "=";
66
}
67
if (op.toLowerCase().startsWith("is")) {
68
// hack to use same where format for now, since $ replacement
69
// doesn't work for "foo IS ...".
70
where[`${quoteField(field)} ${op} ${isToOperand(v)}`] = true;
71
} else if (op.toLowerCase() === "any") {
72
// PostgreSQL array contains $v: field=ANY(column)
73
where[`$ = ANY(${quoteField(field)})`] = v;
74
} else if (op.toLowerCase() === "minlen") {
75
// PostgreSQL array: minumum array length along first dimension
76
where[`array_length(${quoteField(field)}, 1) >= $`] = v;
77
} else if (op.toLowerCase() === "maxlen") {
78
// PostgreSQL array: minumum array length along first dimension
79
where[`array_length(${quoteField(field)}, 1) <= $`] = v;
80
} else if (
81
is_object(v) &&
82
v["relative_time"] != null &&
83
SUPPORTED_TIME_UNITS.includes(v["unit"])
84
) {
85
const time = parseInt(v["relative_time"]);
86
// ${v["unit"]} is safe because we checked that it is in SUPPORTED_TIME_UNITS above.
87
// Also see https://stackoverflow.com/questions/7796657/using-a-variable-period-in-an-interval-in-postgres
88
// for how we do this "interval" arithmetic.
89
const int = `NOW() + $ * INTERVAL '1 ${v["unit"]}'`;
90
const expr = `${quoteField(field)} ${op} (${int})`;
91
where[expr] = time;
92
} else {
93
where[`${quoteField(field)} ${op} $`] = v;
94
}
95
}
96
}
97
98
return where;
99
}
100
101