userdo
Version:
A Durable Object base class for building applications on Cloudflare Workers.
143 lines (142 loc) • 5.47 kB
JavaScript
export class GenericQuery {
constructor(tableName, storage, schema, userId, getOrganizationContext) {
this.tableName = tableName;
this.storage = storage;
this.schema = schema;
this.userId = userId;
this.getOrganizationContext = getOrganizationContext;
this.conditions = [];
}
get organizationContext() {
return this.getOrganizationContext();
}
where(path, operator, value) {
this.conditions.push({ path, operator, value });
return this;
}
orderBy(field, direction = 'asc') {
this.orderByClause = { field, direction };
return this;
}
limit(count) {
this.limitCount = count;
return this;
}
offset(count) {
this.offsetCount = count;
return this;
}
async get() {
let sql;
let params;
if (this.organizationContext) {
sql = `SELECT * FROM "${this.tableName}" WHERE user_id = ? AND organization_id = ?`;
params = [this.userId, this.organizationContext];
}
else {
sql = `SELECT * FROM "${this.tableName}" WHERE user_id = ?`;
params = [this.userId];
}
// Add WHERE conditions
if (this.conditions.length > 0) {
const whereConditions = this.conditions.map((c) => {
const jsonPath = `$.${c.path}`;
switch (c.operator) {
case '==':
params.push(c.value);
return `json_extract(data, '${jsonPath}') = ?`;
case '!=':
params.push(c.value);
return `json_extract(data, '${jsonPath}') != ?`;
case '>':
params.push(c.value);
return `json_extract(data, '${jsonPath}') > ?`;
case '<':
params.push(c.value);
return `json_extract(data, '${jsonPath}') < ?`;
case 'includes':
params.push(`%${c.value}%`);
return `json_extract(data, '${jsonPath}') LIKE ?`;
default:
throw new Error(`Unsupported operator: ${c.operator}`);
}
});
sql += ` AND (${whereConditions.join(' AND ')})`;
}
// Add ORDER BY
if (this.orderByClause) {
const { field, direction } = this.orderByClause;
if (field === 'createdAt' || field === 'updatedAt') {
const dbField = field === 'createdAt' ? 'created_at' : 'updated_at';
sql += ` ORDER BY ${dbField} ${direction.toUpperCase()}`;
}
else {
const jsonPath = `$.${field}`;
sql += ` ORDER BY json_extract(data, '${jsonPath}') ${direction.toUpperCase()}`;
}
}
// Add LIMIT and OFFSET
if (this.limitCount) {
sql += ` LIMIT ${this.limitCount}`;
}
if (this.offsetCount) {
sql += ` OFFSET ${this.offsetCount}`;
}
const cursor = this.storage.sql.exec(sql, ...params);
const results = [];
for (const row of cursor) {
const data = JSON.parse(row.data);
results.push({
...data,
id: row.id,
createdAt: new Date(row.created_at),
updatedAt: new Date(row.updated_at),
});
}
return results;
}
async first() {
const results = await this.limit(1).get();
return results[0] || null;
}
async count() {
let sql;
let params;
if (this.organizationContext) {
sql = `SELECT COUNT(*) as count FROM "${this.tableName}" WHERE user_id = ? AND organization_id = ?`;
params = [this.userId, this.organizationContext];
}
else {
sql = `SELECT COUNT(*) as count FROM "${this.tableName}" WHERE user_id = ?`;
params = [this.userId];
}
if (this.conditions.length > 0) {
const whereConditions = this.conditions.map((c) => {
const jsonPath = `$.${c.path}`;
switch (c.operator) {
case '==':
params.push(c.value);
return `json_extract(data, '${jsonPath}') = ?`;
case '!=':
params.push(c.value);
return `json_extract(data, '${jsonPath}') != ?`;
case '>':
params.push(c.value);
return `json_extract(data, '${jsonPath}') > ?`;
case '<':
params.push(c.value);
return `json_extract(data, '${jsonPath}') < ?`;
case 'includes':
params.push(`%${c.value}%`);
return `json_extract(data, '${jsonPath}') LIKE ?`;
default:
throw new Error(`Unsupported operator: ${c.operator}`);
}
});
sql += ` AND (${whereConditions.join(' AND ')})`;
}
const cursor = this.storage.sql.exec(sql, ...params);
const results = cursor.toArray();
return results.length > 0 ? Number(results[0].count) : 0;
}
}