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