feat: introduce intersection calculation of charts

This commit is contained in:
syuilo
2022-02-09 03:46:58 +09:00
parent eb894c330f
commit 7fcd9435f3
15 changed files with 188 additions and 18 deletions

View File

@ -80,7 +80,7 @@ export default define(meta, async (ps, user) => {
const timeline = await query.take(ps.limit!).getMany();
if (user) activeUsersChart.update(user);
if (user) activeUsersChart.read(user);
return await Notes.packMany(timeline, user);
});

View File

@ -96,7 +96,7 @@ export default define(meta, async (ps, user) => {
process.nextTick(() => {
if (user) {
activeUsersChart.update(user);
activeUsersChart.read(user);
}
});

View File

@ -153,7 +153,7 @@ export default define(meta, async (ps, user) => {
process.nextTick(() => {
if (user) {
activeUsersChart.update(user);
activeUsersChart.read(user);
}
});

View File

@ -122,7 +122,7 @@ export default define(meta, async (ps, user) => {
process.nextTick(() => {
if (user) {
activeUsersChart.update(user);
activeUsersChart.read(user);
}
});

View File

@ -145,7 +145,7 @@ export default define(meta, async (ps, user) => {
process.nextTick(() => {
if (user) {
activeUsersChart.update(user);
activeUsersChart.read(user);
}
});

View File

@ -142,7 +142,7 @@ export default define(meta, async (ps, user) => {
const timeline = await query.take(ps.limit!).getMany();
activeUsersChart.update(user);
activeUsersChart.read(user);
return await Notes.packMany(timeline, user);
});

View File

@ -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],
});
}
}

View File

@ -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' },

View File

@ -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);

View File

@ -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],
});
}
}

View File

@ -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()

View File

@ -297,7 +297,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
}
if (!silent) {
if (Users.isLocalUser(user)) activeUsersChart.noted(user);
if (Users.isLocalUser(user)) activeUsersChart.write(user);
// 未読通知を作成
if (data.visibility === 'specified') {