feat: introduce intersection calculation of charts
This commit is contained in:
		@@ -23,9 +23,9 @@ export default class ActiveUsersChart extends Chart<typeof schema> {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@autobind
 | 
			
		||||
	public async update(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> {
 | 
			
		||||
	public async read(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> {
 | 
			
		||||
		await this.commit({
 | 
			
		||||
			'users': [user.id],
 | 
			
		||||
			'read': [user.id],
 | 
			
		||||
			'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [],
 | 
			
		||||
			'registeredWithinMonth': (Date.now() - user.createdAt.getTime() < month) ? [user.id] : [],
 | 
			
		||||
			'registeredWithinYear': (Date.now() - user.createdAt.getTime() < year) ? [user.id] : [],
 | 
			
		||||
@@ -36,9 +36,9 @@ export default class ActiveUsersChart extends Chart<typeof schema> {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@autobind
 | 
			
		||||
	public async noted(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> {
 | 
			
		||||
	public async write(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> {
 | 
			
		||||
		await this.commit({
 | 
			
		||||
			'notedUsers': [user.id],
 | 
			
		||||
			'write': [user.id],
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,9 @@ import Chart from '../../core';
 | 
			
		||||
export const name = 'activeUsers';
 | 
			
		||||
 | 
			
		||||
export const schema = {
 | 
			
		||||
	'users': { uniqueIncrement: true },
 | 
			
		||||
	'notedUsers': { uniqueIncrement: true, range: 'small' },
 | 
			
		||||
	'readWrite': { intersection: ['read', 'write'], range: 'small' },
 | 
			
		||||
	'read': { uniqueIncrement: true, range: 'small' },
 | 
			
		||||
	'write': { uniqueIncrement: true, range: 'small' },
 | 
			
		||||
	'registeredWithinWeek': { uniqueIncrement: true, range: 'small' },
 | 
			
		||||
	'registeredWithinMonth': { uniqueIncrement: true, range: 'small' },
 | 
			
		||||
	'registeredWithinYear': { uniqueIncrement: true, range: 'small' },
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,11 @@
 | 
			
		||||
import Chart from '../../core';
 | 
			
		||||
 | 
			
		||||
export const name = 'testIntersection';
 | 
			
		||||
 | 
			
		||||
export const schema = {
 | 
			
		||||
	'a': { uniqueIncrement: true },
 | 
			
		||||
	'b': { uniqueIncrement: true },
 | 
			
		||||
	'aAndB': { intersection: ['a', 'b'] },
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
export const entity = Chart.schemaToEntity(name, schema);
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
import autobind from 'autobind-decorator';
 | 
			
		||||
import Chart, { KVs } from '../core';
 | 
			
		||||
import { name, schema } from './entities/test-intersection';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * For testing
 | 
			
		||||
 */
 | 
			
		||||
// eslint-disable-next-line import/no-default-export
 | 
			
		||||
export default class TestIntersectionChart extends Chart<typeof schema> {
 | 
			
		||||
	constructor() {
 | 
			
		||||
		super(name, schema);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@autobind
 | 
			
		||||
	protected async queryCurrentState(): Promise<Partial<KVs<typeof schema>>> {
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@autobind
 | 
			
		||||
	public async addA(key: string): Promise<void> {
 | 
			
		||||
		await this.commit({
 | 
			
		||||
			a: [key],
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@autobind
 | 
			
		||||
	public async addB(key: string): Promise<void> {
 | 
			
		||||
		await this.commit({
 | 
			
		||||
			b: [key],
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -46,6 +46,8 @@ const removeDuplicates = (array: any[]) => Array.from(new Set(array));
 | 
			
		||||
type Schema = Record<string, {
 | 
			
		||||
	uniqueIncrement?: boolean;
 | 
			
		||||
 | 
			
		||||
	intersection?: string[] | ReadonlyArray<string>;
 | 
			
		||||
 | 
			
		||||
	range?: 'big' | 'small' | 'medium';
 | 
			
		||||
 | 
			
		||||
	// previousな値を引き継ぐかどうか
 | 
			
		||||
@@ -384,6 +386,33 @@ export default abstract class Chart<T extends Schema> {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// compute intersection
 | 
			
		||||
			// TODO: intersectionに指定されたカラムがintersectionだった場合の対応
 | 
			
		||||
			for (const [k, v] of Object.entries(this.schema)) {
 | 
			
		||||
				const intersection = v.intersection;
 | 
			
		||||
				if (intersection) {
 | 
			
		||||
					const name = columnPrefix + k.replaceAll('.', columnDot);
 | 
			
		||||
					const firstKey = intersection[0];
 | 
			
		||||
					const firstTempColumnName = uniqueTempColumnPrefix + firstKey.replaceAll('.', columnDot);
 | 
			
		||||
					const currentValuesForHour = new Set([...(finalDiffs[firstKey] ?? []), ...logHour[firstTempColumnName]]);
 | 
			
		||||
					const currentValuesForDay = new Set([...(finalDiffs[firstKey] ?? []), ...logDay[firstTempColumnName]]);
 | 
			
		||||
					for (let i = 1; i < intersection.length; i++) {
 | 
			
		||||
						const targetKey = intersection[i];
 | 
			
		||||
						const targetTempColumnName = uniqueTempColumnPrefix + targetKey.replaceAll('.', columnDot);
 | 
			
		||||
						const targetValuesForHour = new Set([...(finalDiffs[targetKey] ?? []), ...logHour[targetTempColumnName]]);
 | 
			
		||||
						const targetValuesForDay = new Set([...(finalDiffs[targetKey] ?? []), ...logDay[targetTempColumnName]]);
 | 
			
		||||
						currentValuesForHour.forEach(v => {
 | 
			
		||||
							if (!targetValuesForHour.has(v)) currentValuesForHour.delete(v);
 | 
			
		||||
						});
 | 
			
		||||
						currentValuesForDay.forEach(v => {
 | 
			
		||||
							if (!targetValuesForDay.has(v)) currentValuesForDay.delete(v);
 | 
			
		||||
						});
 | 
			
		||||
					}
 | 
			
		||||
					queryForHour[name] = currentValuesForHour.size;
 | 
			
		||||
					queryForDay[name] = currentValuesForDay.size;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// ログ更新
 | 
			
		||||
			await Promise.all([
 | 
			
		||||
				this.repositoryForHour.createQueryBuilder()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user