159 lines
4.6 KiB
TypeScript
159 lines
4.6 KiB
TypeScript
import { createClient } from '@clickhouse/client';
|
|
import { EventsQueryParams } from './analytics';
|
|
|
|
// ClickHouse 客户端配置
|
|
const clickhouse = createClient({
|
|
url: process.env.CLICKHOUSE_URL,
|
|
username: process.env.CLICKHOUSE_USER ,
|
|
password: process.env.CLICKHOUSE_PASSWORD ,
|
|
database: process.env.CLICKHOUSE_DATABASE
|
|
});
|
|
|
|
// 构建日期过滤条件
|
|
function buildDateFilter(startTime?: string, endTime?: string): string {
|
|
const filters = [];
|
|
|
|
if (startTime) {
|
|
filters.push(`event_time >= parseDateTimeBestEffort('${startTime}')`);
|
|
}
|
|
|
|
if (endTime) {
|
|
filters.push(`event_time <= parseDateTimeBestEffort('${endTime}')`);
|
|
}
|
|
|
|
return filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '';
|
|
}
|
|
|
|
// 构建通用过滤条件
|
|
export function buildFilter(params: Partial<EventsQueryParams>): string {
|
|
console.log('buildFilter received params:', JSON.stringify(params));
|
|
const filters = [];
|
|
|
|
// 添加日期过滤条件
|
|
if (params.startTime || params.endTime) {
|
|
const dateFilter = buildDateFilter(params.startTime, params.endTime);
|
|
if (dateFilter) {
|
|
filters.push(dateFilter.replace('WHERE ', ''));
|
|
}
|
|
}
|
|
|
|
// 添加事件类型过滤条件
|
|
if (params.eventType) {
|
|
filters.push(`event_type = '${params.eventType}'`);
|
|
}
|
|
|
|
// 添加链接ID过滤条件
|
|
if (params.linkId) {
|
|
console.log('Adding link_id filter:', params.linkId);
|
|
filters.push(`link_id = '${params.linkId}'`);
|
|
}
|
|
|
|
// 添加链接Slug过滤条件
|
|
if (params.linkSlug) {
|
|
filters.push(`link_slug = '${params.linkSlug}'`);
|
|
}
|
|
|
|
// 添加用户ID过滤条件
|
|
if (params.userId) {
|
|
filters.push(`user_id = '${params.userId}'`);
|
|
}
|
|
|
|
// 添加子路径过滤条件
|
|
if (params.subpath) {
|
|
console.log('====== SUBPATH DEBUG ======');
|
|
console.log('Raw subpath param:', params.subpath);
|
|
console.log('Subpath type:', typeof params.subpath);
|
|
console.log('Subpath length:', params.subpath.length);
|
|
|
|
// 确保子路径没有前导斜杠,避免双斜杠问题
|
|
const cleanSubpath = params.subpath.startsWith('/')
|
|
? params.subpath.substring(1)
|
|
: params.subpath;
|
|
|
|
console.log('Cleaned subpath:', cleanSubpath);
|
|
|
|
// 在event_attributes JSON的full_url字段中查找subpath
|
|
const condition = `JSONExtractString(event_attributes, 'full_url') LIKE '%${cleanSubpath}%'`;
|
|
console.log('Final SQL condition:', condition);
|
|
console.log('==========================');
|
|
|
|
filters.push(condition);
|
|
}
|
|
|
|
// 添加团队ID过滤条件
|
|
if (params.teamId) {
|
|
filters.push(`team_id = '${params.teamId}'`);
|
|
}
|
|
|
|
// 处理多个团队ID
|
|
if (params.teamIds && params.teamIds.length > 0) {
|
|
filters.push(`team_id IN (${params.teamIds.map(id => `'${id}'`).join(', ')})`);
|
|
}
|
|
|
|
// 添加项目ID过滤条件
|
|
if (params.projectId) {
|
|
filters.push(`project_id = '${params.projectId}'`);
|
|
}
|
|
|
|
// 处理多个项目ID
|
|
if (params.projectIds && params.projectIds.length > 0) {
|
|
filters.push(`project_id IN (${params.projectIds.map(id => `'${id}'`).join(', ')})`);
|
|
}
|
|
|
|
// 处理标签过滤 - 使用LIKE来匹配标签字符串
|
|
if (params.tagIds && params.tagIds.length > 0) {
|
|
const tagConditions = params.tagIds.map(tag =>
|
|
`link_tags LIKE '%${tag}%'`
|
|
);
|
|
filters.push(`(${tagConditions.join(' OR ')})`);
|
|
}
|
|
|
|
return filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '';
|
|
}
|
|
|
|
// 构建分页条件
|
|
export function buildPagination(page: number = 1, pageSize: number = 20): string {
|
|
const offset = (page - 1) * pageSize;
|
|
return `LIMIT ${pageSize} OFFSET ${offset}`;
|
|
}
|
|
|
|
// 构建排序条件
|
|
export function buildOrderBy(sortBy: string = 'event_time', sortOrder: string = 'desc'): string {
|
|
return `ORDER BY ${sortBy} ${sortOrder}`;
|
|
}
|
|
|
|
// 执行查询
|
|
export async function executeQuery(query: string) {
|
|
console.log('Executing query:', query); // 查询日志
|
|
try {
|
|
const resultSet = await clickhouse.query({
|
|
query,
|
|
format: 'JSONEachRow',
|
|
});
|
|
|
|
const rows = await resultSet.json();
|
|
return rows;
|
|
} catch (error) {
|
|
console.error('查询执行错误:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// 执行返回单一结果的查询
|
|
export async function executeQuerySingle(query: string) {
|
|
console.log('Executing single result query:', query); // 查询日志
|
|
try {
|
|
const resultSet = await clickhouse.query({
|
|
query,
|
|
format: 'JSONEachRow',
|
|
});
|
|
|
|
const rows = await resultSet.json();
|
|
return rows.length > 0 ? rows[0] : null;
|
|
} catch (error) {
|
|
console.error('单一结果查询执行错误:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
export default clickhouse;
|