enhance(backend): improve chart engine
This commit is contained in:
parent
d95fafb5b3
commit
0be4e10462
36 changed files with 604 additions and 248 deletions
187
packages/backend/migration/1639325650583-chart-v3.js
Normal file
187
packages/backend/migration/1639325650583-chart-v3.js
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
const { MigrationInterface, QueryRunner } = require("typeorm");
|
||||||
|
|
||||||
|
module.exports = class chartV31639325650583 {
|
||||||
|
name = 'chartV31639325650583'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_dd907becf76104e4b656659e6b"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_eddfed8fb40305a04c6f941050"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_f09d543e3acb16c5976bdb31fa"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_e60c358aaced5aab8900a4af31"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_337e9599f278bd7537fe30876f"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_66feba81e1795d176d06c0b1e6"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_0a905b992fecd2b5c3fb98759e"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_2082327b2699ce924fa654afc5"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_9a3ed15a30ab7e3a37702e6e08"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_60c5c6e7e538c09aa274ecd1cf"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_8111b817b9818c04d7eb8475b1"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_583a157ed0cf0ed1b5ec2a833f"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_3313d7288855ec105b5bbf6c21"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_ceab80a6729f8e2e6f5b8a1a3d"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_3b7697a96f522d0478972e6d6f"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_53a3604b939e2b479eb2cfaac8"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_dabbb38a51ab86ee3cab291326"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_a9a806d466b314f253a1a611c4"`);
|
||||||
|
await queryRunner.query(`CREATE TABLE "__chart_day__federation" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___instance_total" bigint NOT NULL, "___instance_inc" bigint NOT NULL, "___instance_dec" bigint NOT NULL, CONSTRAINT "UQ_617a8fe225a6e701d89e02d2c74" UNIQUE ("date"), CONSTRAINT "PK_7ca721c769f31698e0e1331e8e6" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_617a8fe225a6e701d89e02d2c7" ON "__chart_day__federation" ("date") `);
|
||||||
|
await queryRunner.query(`CREATE TABLE "__chart_day__notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___local_diffs_normal" bigint NOT NULL, "___local_diffs_reply" bigint NOT NULL, "___local_diffs_renote" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, "___remote_diffs_normal" bigint NOT NULL, "___remote_diffs_reply" bigint NOT NULL, "___remote_diffs_renote" bigint NOT NULL, CONSTRAINT "UQ_1a527b423ad0858a1af5a056d43" UNIQUE ("date"), CONSTRAINT "PK_1fa4139e1f338272b758d05e090" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_1a527b423ad0858a1af5a056d4" ON "__chart_day__notes" ("date") `);
|
||||||
|
await queryRunner.query(`CREATE TABLE "__chart_day__users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, CONSTRAINT "UQ_cad6e07c20037f31cdba8a350c3" UNIQUE ("date"), CONSTRAINT "PK_d7f7185abb9851f70c4726c54bd" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_cad6e07c20037f31cdba8a350c" ON "__chart_day__users" ("date") `);
|
||||||
|
await queryRunner.query(`CREATE TABLE "__chart_day__network" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___incomingRequests" bigint NOT NULL, "___outgoingRequests" bigint NOT NULL, "___totalTime" bigint NOT NULL, "___incomingBytes" bigint NOT NULL, "___outgoingBytes" bigint NOT NULL, CONSTRAINT "UQ_8bfa548c2b31f9e07db113773ee" UNIQUE ("date"), CONSTRAINT "PK_cac499d6f471042dfed1e7e0132" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_8bfa548c2b31f9e07db113773e" ON "__chart_day__network" ("date") `);
|
||||||
|
await queryRunner.query(`CREATE TABLE "__chart_day__active_users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_users" character varying array NOT NULL, "___remote_users" character varying array NOT NULL, CONSTRAINT "UQ_d5954f3df5e5e3bdfc3c03f3906" UNIQUE ("date"), CONSTRAINT "PK_b1790489b14f005ae8f404f5795" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d5954f3df5e5e3bdfc3c03f390" ON "__chart_day__active_users" ("date") `);
|
||||||
|
await queryRunner.query(`CREATE TABLE "__chart_day__instance" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___requests_failed" bigint NOT NULL, "___requests_succeeded" bigint NOT NULL, "___requests_received" bigint NOT NULL, "___notes_total" bigint NOT NULL, "___notes_inc" bigint NOT NULL, "___notes_dec" bigint NOT NULL, "___notes_diffs_normal" bigint NOT NULL, "___notes_diffs_reply" bigint NOT NULL, "___notes_diffs_renote" bigint NOT NULL, "___users_total" bigint NOT NULL, "___users_inc" bigint NOT NULL, "___users_dec" bigint NOT NULL, "___following_total" bigint NOT NULL, "___following_inc" bigint NOT NULL, "___following_dec" bigint NOT NULL, "___followers_total" bigint NOT NULL, "___followers_inc" bigint NOT NULL, "___followers_dec" bigint NOT NULL, "___drive_totalFiles" bigint NOT NULL, "___drive_totalUsage" bigint NOT NULL, "___drive_incFiles" bigint NOT NULL, "___drive_incUsage" bigint NOT NULL, "___drive_decFiles" bigint NOT NULL, "___drive_decUsage" bigint NOT NULL, CONSTRAINT "UQ_fea7c0278325a1a2492f2d6acbf" UNIQUE ("date", "group"), CONSTRAINT "PK_479a8ff9d959274981087043023" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_fea7c0278325a1a2492f2d6acb" ON "__chart_day__instance" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE TABLE "__chart_day__per_user_notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___total" bigint NOT NULL, "___inc" bigint NOT NULL, "___dec" bigint NOT NULL, "___diffs_normal" bigint NOT NULL, "___diffs_reply" bigint NOT NULL, "___diffs_renote" bigint NOT NULL, CONSTRAINT "UQ_c5545d4b31cdc684034e33b81c3" UNIQUE ("date", "group"), CONSTRAINT "PK_58bab6b6d3ad9310cbc7460fd28" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_c5545d4b31cdc684034e33b81c" ON "__chart_day__per_user_notes" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE TABLE "__chart_day__drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_totalCount" bigint NOT NULL, "___local_totalSize" bigint NOT NULL, "___local_incCount" bigint NOT NULL, "___local_incSize" bigint NOT NULL, "___local_decCount" bigint NOT NULL, "___local_decSize" bigint NOT NULL, "___remote_totalCount" bigint NOT NULL, "___remote_totalSize" bigint NOT NULL, "___remote_incCount" bigint NOT NULL, "___remote_incSize" bigint NOT NULL, "___remote_decCount" bigint NOT NULL, "___remote_decSize" bigint NOT NULL, CONSTRAINT "UQ_0b60ebb3aa0065f10b0616c1171" UNIQUE ("date"), CONSTRAINT "PK_e7ec0de057c77c40fc8d8b62151" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0b60ebb3aa0065f10b0616c117" ON "__chart_day__drive" ("date") `);
|
||||||
|
await queryRunner.query(`CREATE TABLE "__chart_day__per_user_reaction" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "UQ_d54b653660d808b118e36c184c0" UNIQUE ("date", "group"), CONSTRAINT "PK_8af24e2d51ff781a354fe595eda" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d54b653660d808b118e36c184c" ON "__chart_day__per_user_reaction" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE TABLE "__chart_day__hashtag" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___local_users" character varying array NOT NULL, "___remote_users" character varying array NOT NULL, CONSTRAINT "UQ_8f589cf056ff51f09d6096f6450" UNIQUE ("date", "group"), CONSTRAINT "PK_13d5a3b089344e5557f8e0980b4" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_8f589cf056ff51f09d6096f645" ON "__chart_day__hashtag" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE TABLE "__chart_day__per_user_following" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___local_followings_total" bigint NOT NULL, "___local_followings_inc" bigint NOT NULL, "___local_followings_dec" bigint NOT NULL, "___local_followers_total" bigint NOT NULL, "___local_followers_inc" bigint NOT NULL, "___local_followers_dec" bigint NOT NULL, "___remote_followings_total" bigint NOT NULL, "___remote_followings_inc" bigint NOT NULL, "___remote_followings_dec" bigint NOT NULL, "___remote_followers_total" bigint NOT NULL, "___remote_followers_inc" bigint NOT NULL, "___remote_followers_dec" bigint NOT NULL, CONSTRAINT "UQ_e4849a3231f38281280ea4c0eee" UNIQUE ("date", "group"), CONSTRAINT "PK_68ce6b67da57166da66fc8fb27e" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e4849a3231f38281280ea4c0ee" ON "__chart_day__per_user_following" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE TABLE "__chart_day__per_user_drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___totalCount" bigint NOT NULL, "___totalSize" bigint NOT NULL, "___incCount" bigint NOT NULL, "___incSize" bigint NOT NULL, "___decCount" bigint NOT NULL, "___decSize" bigint NOT NULL, CONSTRAINT "UQ_62aa5047b5aec92524f24c701d7" UNIQUE ("date", "group"), CONSTRAINT "PK_1ae135254c137011645da7f4045" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_62aa5047b5aec92524f24c701d" ON "__chart_day__per_user_drive" ("date", "group") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "group"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "group"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__users" DROP COLUMN "group"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__network" DROP COLUMN "group"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "group"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "group"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__federation" ADD CONSTRAINT "UQ_36cb699c49580d4e6c2e6159f97" UNIQUE ("date")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__notes" ADD CONSTRAINT "UQ_42eb716a37d381cdf566192b2be" UNIQUE ("date")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__users" ADD CONSTRAINT "UQ_845254b3eaf708ae8a6cac30265" UNIQUE ("date")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__network" ADD CONSTRAINT "UQ_a1efd3e0048a5f2793a47360dc6" UNIQUE ("date")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD CONSTRAINT "UQ_0ad37b7ef50f4ddc84363d7ccca" UNIQUE ("date")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___local_users" DROP DEFAULT`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___remote_users" DROP DEFAULT`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_39ee857ab2f23493037c6b6631"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "group" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_5048e9daccbbbc6d567bb142d3"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "group" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__drive" ADD CONSTRAINT "UQ_13565815f618a1ff53886c5b28a" UNIQUE ("date")`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_229a41ad465f9205f1f5703291"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "group" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_25a97c02003338124b2b75fdbc"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "group" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___local_users" DROP DEFAULT`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___remote_users" DROP DEFAULT`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_b77d4dd9562c3a899d9a286fcd"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "group" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_30bf67687f483ace115c5ca642"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "group" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_36cb699c49580d4e6c2e6159f9" ON "__chart__federation" ("date") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_42eb716a37d381cdf566192b2b" ON "__chart__notes" ("date") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_845254b3eaf708ae8a6cac3026" ON "__chart__users" ("date") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a1efd3e0048a5f2793a47360dc" ON "__chart__network" ("date") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc" ON "__chart__active_users" ("date") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_13565815f618a1ff53886c5b28" ON "__chart__drive" ("date") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__instance" ADD CONSTRAINT "UQ_39ee857ab2f23493037c6b66311" UNIQUE ("date", "group")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ADD CONSTRAINT "UQ_5048e9daccbbbc6d567bb142d34" UNIQUE ("date", "group")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ADD CONSTRAINT "UQ_229a41ad465f9205f1f57032910" UNIQUE ("date", "group")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD CONSTRAINT "UQ_25a97c02003338124b2b75fdbc8" UNIQUE ("date", "group")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ADD CONSTRAINT "UQ_b77d4dd9562c3a899d9a286fcd7" UNIQUE ("date", "group")`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ADD CONSTRAINT "UQ_30bf67687f483ace115c5ca6429" UNIQUE ("date", "group")`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" DROP CONSTRAINT "UQ_30bf67687f483ace115c5ca6429"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_following" DROP CONSTRAINT "UQ_b77d4dd9562c3a899d9a286fcd7"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP CONSTRAINT "UQ_25a97c02003338124b2b75fdbc8"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" DROP CONSTRAINT "UQ_229a41ad465f9205f1f57032910"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" DROP CONSTRAINT "UQ_5048e9daccbbbc6d567bb142d34"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__instance" DROP CONSTRAINT "UQ_39ee857ab2f23493037c6b66311"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_30bf67687f483ace115c5ca642"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_b77d4dd9562c3a899d9a286fcd"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_25a97c02003338124b2b75fdbc"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_229a41ad465f9205f1f5703291"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_13565815f618a1ff53886c5b28"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_5048e9daccbbbc6d567bb142d3"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_39ee857ab2f23493037c6b6631"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_0ad37b7ef50f4ddc84363d7ccc"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_a1efd3e0048a5f2793a47360dc"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_845254b3eaf708ae8a6cac3026"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_42eb716a37d381cdf566192b2b"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_36cb699c49580d4e6c2e6159f9"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "group" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "group" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___remote_users" SET DEFAULT '{}'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___local_users" SET DEFAULT '{}'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "group" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "group" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__drive" DROP CONSTRAINT "UQ_13565815f618a1ff53886c5b28a"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "group" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "group" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___remote_users" SET DEFAULT '{}'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___local_users" SET DEFAULT '{}'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP CONSTRAINT "UQ_0ad37b7ef50f4ddc84363d7ccca"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__network" DROP CONSTRAINT "UQ_a1efd3e0048a5f2793a47360dc6"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__users" DROP CONSTRAINT "UQ_845254b3eaf708ae8a6cac30265"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__notes" DROP CONSTRAINT "UQ_42eb716a37d381cdf566192b2be"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__federation" DROP CONSTRAINT "UQ_36cb699c49580d4e6c2e6159f97"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "group" character varying(128)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "group" character varying(128)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__network" ADD "group" character varying(128)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__users" ADD "group" character varying(128)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "group" character varying(128)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "group" character varying(128)`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_62aa5047b5aec92524f24c701d"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "__chart_day__per_user_drive"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_e4849a3231f38281280ea4c0ee"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "__chart_day__per_user_following"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_8f589cf056ff51f09d6096f645"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "__chart_day__hashtag"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_d54b653660d808b118e36c184c"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "__chart_day__per_user_reaction"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_0b60ebb3aa0065f10b0616c117"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "__chart_day__drive"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_c5545d4b31cdc684034e33b81c"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "__chart_day__per_user_notes"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_fea7c0278325a1a2492f2d6acb"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "__chart_day__instance"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_d5954f3df5e5e3bdfc3c03f390"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "__chart_day__active_users"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_8bfa548c2b31f9e07db113773e"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "__chart_day__network"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_cad6e07c20037f31cdba8a350c"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "__chart_day__users"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_1a527b423ad0858a1af5a056d4"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "__chart_day__notes"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_617a8fe225a6e701d89e02d2c7"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "__chart_day__federation"`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a9a806d466b314f253a1a611c4" ON "__chart__per_user_drive" ("date") WHERE ("group" IS NULL)`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_dabbb38a51ab86ee3cab291326" ON "__chart__per_user_following" ("date") WHERE ("group" IS NULL)`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_53a3604b939e2b479eb2cfaac8" ON "__chart__hashtag" ("date") WHERE ("group" IS NULL)`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_3b7697a96f522d0478972e6d6f" ON "__chart__per_user_reaction" ("date") WHERE ("group" IS NULL)`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_ceab80a6729f8e2e6f5b8a1a3d" ON "__chart__drive" ("date") WHERE ("group" IS NULL)`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_3313d7288855ec105b5bbf6c21" ON "__chart__drive" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_583a157ed0cf0ed1b5ec2a833f" ON "__chart__per_user_notes" ("date") WHERE ("group" IS NULL)`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_8111b817b9818c04d7eb8475b1" ON "__chart__instance" ("date") WHERE ("group" IS NULL)`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_60c5c6e7e538c09aa274ecd1cf" ON "__chart__active_users" ("date") WHERE ("group" IS NULL)`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_9a3ed15a30ab7e3a37702e6e08" ON "__chart__active_users" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_2082327b2699ce924fa654afc5" ON "__chart__network" ("date") WHERE ("group" IS NULL)`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0a905b992fecd2b5c3fb98759e" ON "__chart__network" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_66feba81e1795d176d06c0b1e6" ON "__chart__users" ("date") WHERE ("group" IS NULL)`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_337e9599f278bd7537fe30876f" ON "__chart__users" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e60c358aaced5aab8900a4af31" ON "__chart__notes" ("date") WHERE ("group" IS NULL)`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_f09d543e3acb16c5976bdb31fa" ON "__chart__notes" ("date", "group") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_eddfed8fb40305a04c6f941050" ON "__chart__federation" ("date") WHERE ("group" IS NULL)`);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_dd907becf76104e4b656659e6b" ON "__chart__federation" ("date", "group") `);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,10 +3,10 @@ const types = require('pg').types;
|
||||||
types.setTypeParser(20, Number);
|
types.setTypeParser(20, Number);
|
||||||
|
|
||||||
import { createConnection, Logger, getConnection } from 'typeorm';
|
import { createConnection, Logger, getConnection } from 'typeorm';
|
||||||
import config from '@/config/index';
|
|
||||||
import { entities as charts } from '@/services/chart/entities';
|
|
||||||
import { dbLogger } from './logger';
|
|
||||||
import * as highlight from 'cli-highlight';
|
import * as highlight from 'cli-highlight';
|
||||||
|
import config from '@/config/index';
|
||||||
|
|
||||||
|
import { dbLogger } from './logger';
|
||||||
|
|
||||||
import { User } from '@/models/entities/user';
|
import { User } from '@/models/entities/user';
|
||||||
import { DriveFile } from '@/models/entities/drive-file';
|
import { DriveFile } from '@/models/entities/drive-file';
|
||||||
|
@ -74,6 +74,8 @@ import { Ad } from '@/models/entities/ad';
|
||||||
import { PasswordResetRequest } from '@/models/entities/password-reset-request';
|
import { PasswordResetRequest } from '@/models/entities/password-reset-request';
|
||||||
import { UserPending } from '@/models/entities/user-pending';
|
import { UserPending } from '@/models/entities/user-pending';
|
||||||
|
|
||||||
|
import { entities as charts } from '@/services/chart/entities';
|
||||||
|
|
||||||
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
|
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
|
||||||
|
|
||||||
class MyCustomLogger implements Logger {
|
class MyCustomLogger implements Logger {
|
||||||
|
@ -175,7 +177,7 @@ export const entities = [
|
||||||
Ad,
|
Ad,
|
||||||
PasswordResetRequest,
|
PasswordResetRequest,
|
||||||
UserPending,
|
UserPending,
|
||||||
...charts as any,
|
...charts,
|
||||||
];
|
];
|
||||||
|
|
||||||
export function initDb(justBorrow = false, sync = false, forceRecreate = false) {
|
export function initDb(justBorrow = false, sync = false, forceRecreate = false) {
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { Obj, DeepPartial } from '../../core';
|
import Chart, { Obj, DeepPartial } from '../core';
|
||||||
import { User } from '@/models/entities/user';
|
import { User } from '@/models/entities/user';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { Users } from '@/models/index';
|
import { Users } from '@/models/index';
|
||||||
import { name, schema } from '../schemas/active-users';
|
import { name, schema } from './entities/active-users';
|
||||||
|
|
||||||
type ActiveUsersLog = SchemaType<typeof schema>;
|
type ActiveUsersLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* アクティブユーザーに関するチャート
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class ActiveUsersChart extends Chart<ActiveUsersLog> {
|
export default class ActiveUsersChart extends Chart<ActiveUsersLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema);
|
super(name, schema);
|
||||||
|
@ -35,7 +39,7 @@ export default class ActiveUsersChart extends Chart<ActiveUsersLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async update(user: { id: User['id'], host: User['host'] }) {
|
public async update(user: { id: User['id'], host: User['host'] }): Promise<void> {
|
||||||
const update: Obj = {
|
const update: Obj = {
|
||||||
users: [user.id],
|
users: [user.id],
|
||||||
};
|
};
|
|
@ -1,13 +1,17 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { Obj, DeepPartial } from '../../core';
|
import Chart, { Obj, DeepPartial } from '../core';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { DriveFiles } from '@/models/index';
|
import { DriveFiles } from '@/models/index';
|
||||||
import { Not, IsNull } from 'typeorm';
|
import { Not, IsNull } from 'typeorm';
|
||||||
import { DriveFile } from '@/models/entities/drive-file';
|
import { DriveFile } from '@/models/entities/drive-file';
|
||||||
import { name, schema } from '../schemas/drive';
|
import { name, schema } from './entities/drive';
|
||||||
|
|
||||||
type DriveLog = SchemaType<typeof schema>;
|
type DriveLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ドライブに関するチャート
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class DriveChart extends Chart<DriveLog> {
|
export default class DriveChart extends Chart<DriveLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema);
|
super(name, schema);
|
||||||
|
@ -71,7 +75,7 @@ export default class DriveChart extends Chart<DriveLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async update(file: DriveFile, isAdditional: boolean) {
|
public async update(file: DriveFile, isAdditional: boolean): Promise<void> {
|
||||||
const update: Obj = {};
|
const update: Obj = {};
|
||||||
|
|
||||||
update.totalCount = isAdditional ? 1 : -1;
|
update.totalCount = isAdditional ? 1 : -1;
|
|
@ -1,4 +1,8 @@
|
||||||
export const logSchema = {
|
import Chart from '../../core';
|
||||||
|
|
||||||
|
export const name = 'activeUsers';
|
||||||
|
|
||||||
|
const logSchema = {
|
||||||
/**
|
/**
|
||||||
* アクティブユーザー
|
* アクティブユーザー
|
||||||
*/
|
*/
|
||||||
|
@ -12,9 +16,6 @@ export const logSchema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* アクティブユーザーに関するチャート
|
|
||||||
*/
|
|
||||||
export const schema = {
|
export const schema = {
|
||||||
type: 'object' as const,
|
type: 'object' as const,
|
||||||
optional: false as const, nullable: false as const,
|
optional: false as const, nullable: false as const,
|
||||||
|
@ -32,4 +33,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'activeUsers';
|
export const entity = Chart.schemaToEntity(name, schema);
|
|
@ -1,3 +1,7 @@
|
||||||
|
import Chart from '../../core';
|
||||||
|
|
||||||
|
export const name = 'drive';
|
||||||
|
|
||||||
const logSchema = {
|
const logSchema = {
|
||||||
/**
|
/**
|
||||||
* 集計期間時点での、全ドライブファイル数
|
* 集計期間時点での、全ドライブファイル数
|
||||||
|
@ -65,4 +69,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'drive';
|
export const entity = Chart.schemaToEntity(name, schema);
|
|
@ -1,6 +1,7 @@
|
||||||
/**
|
import Chart from '../../core';
|
||||||
* フェデレーションに関するチャート
|
|
||||||
*/
|
export const name = 'federation';
|
||||||
|
|
||||||
export const schema = {
|
export const schema = {
|
||||||
type: 'object' as const,
|
type: 'object' as const,
|
||||||
optional: false as const, nullable: false as const,
|
optional: false as const, nullable: false as const,
|
||||||
|
@ -26,4 +27,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'federation';
|
export const entity = Chart.schemaToEntity(name, schema);
|
|
@ -1,4 +1,8 @@
|
||||||
export const logSchema = {
|
import Chart from '../../core';
|
||||||
|
|
||||||
|
export const name = 'hashtag';
|
||||||
|
|
||||||
|
const logSchema = {
|
||||||
/**
|
/**
|
||||||
* 投稿したユーザー
|
* 投稿したユーザー
|
||||||
*/
|
*/
|
||||||
|
@ -12,9 +16,6 @@ export const logSchema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* ハッシュタグに関するチャート
|
|
||||||
*/
|
|
||||||
export const schema = {
|
export const schema = {
|
||||||
type: 'object' as const,
|
type: 'object' as const,
|
||||||
optional: false as const, nullable: false as const,
|
optional: false as const, nullable: false as const,
|
||||||
|
@ -32,4 +33,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'hashtag';
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
|
@ -1,6 +1,7 @@
|
||||||
/**
|
import Chart from '../../core';
|
||||||
* インスタンスごとのチャート
|
|
||||||
*/
|
export const name = 'instance';
|
||||||
|
|
||||||
export const schema = {
|
export const schema = {
|
||||||
type: 'object' as const,
|
type: 'object' as const,
|
||||||
optional: false as const, nullable: false as const,
|
optional: false as const, nullable: false as const,
|
||||||
|
@ -154,4 +155,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'instance';
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
|
@ -1,6 +1,7 @@
|
||||||
/**
|
import Chart from '../../core';
|
||||||
* ネットワークに関するチャート
|
|
||||||
*/
|
export const name = 'network';
|
||||||
|
|
||||||
export const schema = {
|
export const schema = {
|
||||||
type: 'object' as const,
|
type: 'object' as const,
|
||||||
optional: false as const, nullable: false as const,
|
optional: false as const, nullable: false as const,
|
||||||
|
@ -28,4 +29,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'network';
|
export const entity = Chart.schemaToEntity(name, schema);
|
|
@ -1,3 +1,7 @@
|
||||||
|
import Chart from '../../core';
|
||||||
|
|
||||||
|
export const name = 'notes';
|
||||||
|
|
||||||
const logSchema = {
|
const logSchema = {
|
||||||
total: {
|
total: {
|
||||||
type: 'number' as const,
|
type: 'number' as const,
|
||||||
|
@ -53,4 +57,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'notes';
|
export const entity = Chart.schemaToEntity(name, schema);
|
|
@ -1,3 +1,7 @@
|
||||||
|
import Chart from '../../core';
|
||||||
|
|
||||||
|
export const name = 'perUserDrive';
|
||||||
|
|
||||||
export const schema = {
|
export const schema = {
|
||||||
type: 'object' as const,
|
type: 'object' as const,
|
||||||
optional: false as const, nullable: false as const,
|
optional: false as const, nullable: false as const,
|
||||||
|
@ -52,4 +56,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'perUserDrive';
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
|
@ -1,4 +1,8 @@
|
||||||
export const logSchema = {
|
import Chart from '../../core';
|
||||||
|
|
||||||
|
export const name = 'perUserFollowing';
|
||||||
|
|
||||||
|
const logSchema = {
|
||||||
/**
|
/**
|
||||||
* フォローしている
|
* フォローしている
|
||||||
*/
|
*/
|
||||||
|
@ -83,4 +87,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'perUserFollowing';
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
|
@ -1,3 +1,7 @@
|
||||||
|
import Chart from '../../core';
|
||||||
|
|
||||||
|
export const name = 'perUserNotes';
|
||||||
|
|
||||||
export const schema = {
|
export const schema = {
|
||||||
type: 'object' as const,
|
type: 'object' as const,
|
||||||
optional: false as const, nullable: false as const,
|
optional: false as const, nullable: false as const,
|
||||||
|
@ -40,4 +44,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'perUserNotes';
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
|
@ -1,6 +1,10 @@
|
||||||
export const logSchema = {
|
import Chart from '../../core';
|
||||||
|
|
||||||
|
export const name = 'perUserReaction';
|
||||||
|
|
||||||
|
const logSchema = {
|
||||||
/**
|
/**
|
||||||
* フォローしている合計
|
* 被リアクション数
|
||||||
*/
|
*/
|
||||||
count: {
|
count: {
|
||||||
type: 'number' as const,
|
type: 'number' as const,
|
||||||
|
@ -8,9 +12,6 @@ export const logSchema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* ユーザーごとのリアクションに関するチャート
|
|
||||||
*/
|
|
||||||
export const schema = {
|
export const schema = {
|
||||||
type: 'object' as const,
|
type: 'object' as const,
|
||||||
optional: false as const, nullable: false as const,
|
optional: false as const, nullable: false as const,
|
||||||
|
@ -28,4 +29,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'perUserReaction';
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
|
@ -1,3 +1,7 @@
|
||||||
|
import Chart from '../../core';
|
||||||
|
|
||||||
|
export const name = 'testGrouped';
|
||||||
|
|
||||||
export const schema = {
|
export const schema = {
|
||||||
type: 'object' as const,
|
type: 'object' as const,
|
||||||
optional: false as const, nullable: false as const,
|
optional: false as const, nullable: false as const,
|
||||||
|
@ -25,4 +29,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'testGrouped';
|
export const entity = Chart.schemaToEntity(name, schema, true);
|
|
@ -1,3 +1,7 @@
|
||||||
|
import Chart from '../../core';
|
||||||
|
|
||||||
|
export const name = 'testUnique';
|
||||||
|
|
||||||
export const schema = {
|
export const schema = {
|
||||||
type: 'object' as const,
|
type: 'object' as const,
|
||||||
optional: false as const, nullable: false as const,
|
optional: false as const, nullable: false as const,
|
||||||
|
@ -13,4 +17,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'testUnique';
|
export const entity = Chart.schemaToEntity(name, schema);
|
|
@ -1,3 +1,7 @@
|
||||||
|
import Chart from '../../core';
|
||||||
|
|
||||||
|
export const name = 'test';
|
||||||
|
|
||||||
export const schema = {
|
export const schema = {
|
||||||
type: 'object' as const,
|
type: 'object' as const,
|
||||||
optional: false as const, nullable: false as const,
|
optional: false as const, nullable: false as const,
|
||||||
|
@ -25,4 +29,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'test';
|
export const entity = Chart.schemaToEntity(name, schema);
|
|
@ -1,3 +1,7 @@
|
||||||
|
import Chart from '../../core';
|
||||||
|
|
||||||
|
export const name = 'users';
|
||||||
|
|
||||||
const logSchema = {
|
const logSchema = {
|
||||||
/**
|
/**
|
||||||
* 集計期間時点での、全ユーザー数
|
* 集計期間時点での、全ユーザー数
|
||||||
|
@ -41,4 +45,4 @@ export const schema = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const name = 'users';
|
export const entity = Chart.schemaToEntity(name, schema);
|
|
@ -1,11 +1,15 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { Obj, DeepPartial } from '../../core';
|
import Chart, { Obj, DeepPartial } from '../core';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { Instances } from '@/models/index';
|
import { Instances } from '@/models/index';
|
||||||
import { name, schema } from '../schemas/federation';
|
import { name, schema } from './entities/federation';
|
||||||
|
|
||||||
type FederationLog = SchemaType<typeof schema>;
|
type FederationLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* フェデレーションに関するチャート
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class FederationChart extends Chart<FederationLog> {
|
export default class FederationChart extends Chart<FederationLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema);
|
super(name, schema);
|
||||||
|
@ -45,7 +49,7 @@ export default class FederationChart extends Chart<FederationLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async update(isAdditional: boolean) {
|
public async update(isAdditional: boolean): Promise<void> {
|
||||||
const update: Obj = {};
|
const update: Obj = {};
|
||||||
|
|
||||||
update.total = isAdditional ? 1 : -1;
|
update.total = isAdditional ? 1 : -1;
|
|
@ -1,12 +1,16 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { Obj, DeepPartial } from '../../core';
|
import Chart, { Obj, DeepPartial } from '../core';
|
||||||
import { User } from '@/models/entities/user';
|
import { User } from '@/models/entities/user';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { Users } from '@/models/index';
|
import { Users } from '@/models/index';
|
||||||
import { name, schema } from '../schemas/hashtag';
|
import { name, schema } from './entities/hashtag';
|
||||||
|
|
||||||
type HashtagLog = SchemaType<typeof schema>;
|
type HashtagLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ハッシュタグに関するチャート
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class HashtagChart extends Chart<HashtagLog> {
|
export default class HashtagChart extends Chart<HashtagLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema, true);
|
super(name, schema, true);
|
||||||
|
@ -35,7 +39,7 @@ export default class HashtagChart extends Chart<HashtagLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async update(hashtag: string, user: { id: User['id'], host: User['host'] }) {
|
public async update(hashtag: string, user: { id: User['id'], host: User['host'] }): Promise<void> {
|
||||||
const update: Obj = {
|
const update: Obj = {
|
||||||
users: [user.id],
|
users: [user.id],
|
||||||
};
|
};
|
|
@ -1,17 +1,21 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { Obj, DeepPartial } from '../../core';
|
import Chart, { Obj, DeepPartial } from '../core';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { DriveFiles, Followings, Users, Notes } from '@/models/index';
|
import { DriveFiles, Followings, Users, Notes } from '@/models/index';
|
||||||
import { DriveFile } from '@/models/entities/drive-file';
|
import { DriveFile } from '@/models/entities/drive-file';
|
||||||
import { name, schema } from '../schemas/instance';
|
|
||||||
import { Note } from '@/models/entities/note';
|
import { Note } from '@/models/entities/note';
|
||||||
import { toPuny } from '@/misc/convert-host';
|
import { toPuny } from '@/misc/convert-host';
|
||||||
|
import { name, schema } from './entities/instance';
|
||||||
|
|
||||||
type InstanceLog = SchemaType<typeof schema>;
|
type InstanceLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* インスタンスごとのチャート
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class InstanceChart extends Chart<InstanceLog> {
|
export default class InstanceChart extends Chart<InstanceLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema);
|
super(name, schema, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
|
@ -119,7 +123,7 @@ export default class InstanceChart extends Chart<InstanceLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async requestReceived(host: string) {
|
public async requestReceived(host: string): Promise<void> {
|
||||||
await this.inc({
|
await this.inc({
|
||||||
requests: {
|
requests: {
|
||||||
received: 1,
|
received: 1,
|
||||||
|
@ -128,7 +132,7 @@ export default class InstanceChart extends Chart<InstanceLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async requestSent(host: string, isSucceeded: boolean) {
|
public async requestSent(host: string, isSucceeded: boolean): Promise<void> {
|
||||||
const update: Obj = {};
|
const update: Obj = {};
|
||||||
|
|
||||||
if (isSucceeded) {
|
if (isSucceeded) {
|
||||||
|
@ -143,7 +147,7 @@ export default class InstanceChart extends Chart<InstanceLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async newUser(host: string) {
|
public async newUser(host: string): Promise<void> {
|
||||||
await this.inc({
|
await this.inc({
|
||||||
users: {
|
users: {
|
||||||
total: 1,
|
total: 1,
|
||||||
|
@ -153,8 +157,8 @@ export default class InstanceChart extends Chart<InstanceLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async updateNote(host: string, note: Note, isAdditional: boolean) {
|
public async updateNote(host: string, note: Note, isAdditional: boolean): Promise<void> {
|
||||||
const diffs = {} as any;
|
const diffs = {} as Record<string, unknown>;
|
||||||
|
|
||||||
if (note.replyId != null) {
|
if (note.replyId != null) {
|
||||||
diffs.reply = isAdditional ? 1 : -1;
|
diffs.reply = isAdditional ? 1 : -1;
|
||||||
|
@ -175,7 +179,7 @@ export default class InstanceChart extends Chart<InstanceLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async updateFollowing(host: string, isAdditional: boolean) {
|
public async updateFollowing(host: string, isAdditional: boolean): Promise<void> {
|
||||||
await this.inc({
|
await this.inc({
|
||||||
following: {
|
following: {
|
||||||
total: isAdditional ? 1 : -1,
|
total: isAdditional ? 1 : -1,
|
||||||
|
@ -186,7 +190,7 @@ export default class InstanceChart extends Chart<InstanceLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async updateFollowers(host: string, isAdditional: boolean) {
|
public async updateFollowers(host: string, isAdditional: boolean): Promise<void> {
|
||||||
await this.inc({
|
await this.inc({
|
||||||
followers: {
|
followers: {
|
||||||
total: isAdditional ? 1 : -1,
|
total: isAdditional ? 1 : -1,
|
||||||
|
@ -197,7 +201,7 @@ export default class InstanceChart extends Chart<InstanceLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async updateDrive(file: DriveFile, isAdditional: boolean) {
|
public async updateDrive(file: DriveFile, isAdditional: boolean): Promise<void> {
|
||||||
const update: Obj = {};
|
const update: Obj = {};
|
||||||
|
|
||||||
update.totalFiles = isAdditional ? 1 : -1;
|
update.totalFiles = isAdditional ? 1 : -1;
|
|
@ -1,10 +1,14 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { DeepPartial } from '../../core';
|
import Chart, { DeepPartial } from '../core';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { name, schema } from '../schemas/network';
|
import { name, schema } from './entities/network';
|
||||||
|
|
||||||
type NetworkLog = SchemaType<typeof schema>;
|
type NetworkLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ネットワークに関するチャート
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class NetworkChart extends Chart<NetworkLog> {
|
export default class NetworkChart extends Chart<NetworkLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema);
|
super(name, schema);
|
||||||
|
@ -32,7 +36,7 @@ export default class NetworkChart extends Chart<NetworkLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async update(incomingRequests: number, time: number, incomingBytes: number, outgoingBytes: number) {
|
public async update(incomingRequests: number, time: number, incomingBytes: number, outgoingBytes: number): Promise<void> {
|
||||||
const inc: DeepPartial<NetworkLog> = {
|
const inc: DeepPartial<NetworkLog> = {
|
||||||
incomingRequests: incomingRequests,
|
incomingRequests: incomingRequests,
|
||||||
totalTime: time,
|
totalTime: time,
|
|
@ -1,13 +1,17 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { Obj, DeepPartial } from '../../core';
|
import Chart, { Obj, DeepPartial } from '../core';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { Notes } from '@/models/index';
|
import { Notes } from '@/models/index';
|
||||||
import { Not, IsNull } from 'typeorm';
|
import { Not, IsNull } from 'typeorm';
|
||||||
import { Note } from '@/models/entities/note';
|
import { Note } from '@/models/entities/note';
|
||||||
import { name, schema } from '../schemas/notes';
|
import { name, schema } from './entities/notes';
|
||||||
|
|
||||||
type NotesLog = SchemaType<typeof schema>;
|
type NotesLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ノートに関するチャート
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class NotesChart extends Chart<NotesLog> {
|
export default class NotesChart extends Chart<NotesLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema);
|
super(name, schema);
|
||||||
|
@ -69,7 +73,7 @@ export default class NotesChart extends Chart<NotesLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async update(note: Note, isAdditional: boolean) {
|
public async update(note: Note, isAdditional: boolean): Promise<void> {
|
||||||
const update: Obj = {
|
const update: Obj = {
|
||||||
diffs: {},
|
diffs: {},
|
||||||
};
|
};
|
|
@ -1,12 +1,16 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { Obj, DeepPartial } from '../../core';
|
import Chart, { Obj, DeepPartial } from '../core';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { DriveFiles } from '@/models/index';
|
import { DriveFiles } from '@/models/index';
|
||||||
import { DriveFile } from '@/models/entities/drive-file';
|
import { DriveFile } from '@/models/entities/drive-file';
|
||||||
import { name, schema } from '../schemas/per-user-drive';
|
import { name, schema } from './entities/per-user-drive';
|
||||||
|
|
||||||
type PerUserDriveLog = SchemaType<typeof schema>;
|
type PerUserDriveLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーごとのドライブに関するチャート
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class PerUserDriveChart extends Chart<PerUserDriveLog> {
|
export default class PerUserDriveChart extends Chart<PerUserDriveLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema, true);
|
super(name, schema, true);
|
||||||
|
@ -46,7 +50,7 @@ export default class PerUserDriveChart extends Chart<PerUserDriveLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async update(file: DriveFile, isAdditional: boolean) {
|
public async update(file: DriveFile, isAdditional: boolean): Promise<void> {
|
||||||
const update: Obj = {};
|
const update: Obj = {};
|
||||||
|
|
||||||
update.totalCount = isAdditional ? 1 : -1;
|
update.totalCount = isAdditional ? 1 : -1;
|
|
@ -1,13 +1,17 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { Obj, DeepPartial } from '../../core';
|
import Chart, { Obj, DeepPartial } from '../core';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { Followings, Users } from '@/models/index';
|
import { Followings, Users } from '@/models/index';
|
||||||
import { Not, IsNull } from 'typeorm';
|
import { Not, IsNull } from 'typeorm';
|
||||||
import { User } from '@/models/entities/user';
|
import { User } from '@/models/entities/user';
|
||||||
import { name, schema } from '../schemas/per-user-following';
|
import { name, schema } from './entities/per-user-following';
|
||||||
|
|
||||||
type PerUserFollowingLog = SchemaType<typeof schema>;
|
type PerUserFollowingLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーごとのフォローに関するチャート
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class PerUserFollowingChart extends Chart<PerUserFollowingLog> {
|
export default class PerUserFollowingChart extends Chart<PerUserFollowingLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema, true);
|
super(name, schema, true);
|
||||||
|
@ -100,7 +104,7 @@ export default class PerUserFollowingChart extends Chart<PerUserFollowingLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean) {
|
public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean): Promise<void> {
|
||||||
const update: Obj = {};
|
const update: Obj = {};
|
||||||
|
|
||||||
update.total = isFollow ? 1 : -1;
|
update.total = isFollow ? 1 : -1;
|
|
@ -1,13 +1,17 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { Obj, DeepPartial } from '../../core';
|
import Chart, { Obj, DeepPartial } from '../core';
|
||||||
import { User } from '@/models/entities/user';
|
import { User } from '@/models/entities/user';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { Notes } from '@/models/index';
|
import { Notes } from '@/models/index';
|
||||||
import { Note } from '@/models/entities/note';
|
import { Note } from '@/models/entities/note';
|
||||||
import { name, schema } from '../schemas/per-user-notes';
|
import { name, schema } from './entities/per-user-notes';
|
||||||
|
|
||||||
type PerUserNotesLog = SchemaType<typeof schema>;
|
type PerUserNotesLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーごとのノートに関するチャート
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class PerUserNotesChart extends Chart<PerUserNotesLog> {
|
export default class PerUserNotesChart extends Chart<PerUserNotesLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema, true);
|
super(name, schema, true);
|
||||||
|
@ -46,7 +50,7 @@ export default class PerUserNotesChart extends Chart<PerUserNotesLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean) {
|
public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean): Promise<void> {
|
||||||
const update: Obj = {
|
const update: Obj = {
|
||||||
diffs: {},
|
diffs: {},
|
||||||
};
|
};
|
|
@ -1,13 +1,17 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { DeepPartial } from '../../core';
|
import Chart, { DeepPartial } from '../core';
|
||||||
import { User } from '@/models/entities/user';
|
import { User } from '@/models/entities/user';
|
||||||
import { Note } from '@/models/entities/note';
|
import { Note } from '@/models/entities/note';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { Users } from '@/models/index';
|
import { Users } from '@/models/index';
|
||||||
import { name, schema } from '../schemas/per-user-reactions';
|
import { name, schema } from './entities/per-user-reactions';
|
||||||
|
|
||||||
type PerUserReactionsLog = SchemaType<typeof schema>;
|
type PerUserReactionsLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザーごとのリアクションに関するチャート
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class PerUserReactionsChart extends Chart<PerUserReactionsLog> {
|
export default class PerUserReactionsChart extends Chart<PerUserReactionsLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema, true);
|
super(name, schema, true);
|
||||||
|
@ -36,7 +40,7 @@ export default class PerUserReactionsChart extends Chart<PerUserReactionsLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async update(user: { id: User['id'], host: User['host'] }, note: Note) {
|
public async update(user: { id: User['id'], host: User['host'] }, note: Note): Promise<void> {
|
||||||
this.inc({
|
this.inc({
|
||||||
[Users.isLocalUser(user) ? 'local' : 'remote']: { count: 1 },
|
[Users.isLocalUser(user) ? 'local' : 'remote']: { count: 1 },
|
||||||
}, note.userId);
|
}, note.userId);
|
|
@ -1,10 +1,14 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { Obj, DeepPartial } from '../../core';
|
import Chart, { Obj, DeepPartial } from '../core';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { name, schema } from '../schemas/test-grouped';
|
import { name, schema } from './entities/test-grouped';
|
||||||
|
|
||||||
type TestGroupedLog = SchemaType<typeof schema>;
|
type TestGroupedLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For testing
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class TestGroupedChart extends Chart<TestGroupedLog> {
|
export default class TestGroupedChart extends Chart<TestGroupedLog> {
|
||||||
private total = {} as Record<string, number>;
|
private total = {} as Record<string, number>;
|
||||||
|
|
||||||
|
@ -42,7 +46,7 @@ export default class TestGroupedChart extends Chart<TestGroupedLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async increment(group: string) {
|
public async increment(group: string): Promise<void> {
|
||||||
if (this.total[group] == null) this.total[group] = 0;
|
if (this.total[group] == null) this.total[group] = 0;
|
||||||
|
|
||||||
const update: Obj = {};
|
const update: Obj = {};
|
|
@ -1,10 +1,14 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { DeepPartial } from '../../core';
|
import Chart, { DeepPartial } from '../core';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { name, schema } from '../schemas/test-unique';
|
import { name, schema } from './entities/test-unique';
|
||||||
|
|
||||||
type TestUniqueLog = SchemaType<typeof schema>;
|
type TestUniqueLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For testing
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class TestUniqueChart extends Chart<TestUniqueLog> {
|
export default class TestUniqueChart extends Chart<TestUniqueLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema);
|
super(name, schema);
|
||||||
|
@ -28,7 +32,7 @@ export default class TestUniqueChart extends Chart<TestUniqueLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async uniqueIncrement(key: string) {
|
public async uniqueIncrement(key: string): Promise<void> {
|
||||||
await this.inc({
|
await this.inc({
|
||||||
foo: [key],
|
foo: [key],
|
||||||
});
|
});
|
|
@ -1,10 +1,14 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { Obj, DeepPartial } from '../../core';
|
import Chart, { Obj, DeepPartial } from '../core';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { name, schema } from '../schemas/test';
|
import { name, schema } from './entities/test';
|
||||||
|
|
||||||
type TestLog = SchemaType<typeof schema>;
|
type TestLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For testing
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class TestChart extends Chart<TestLog> {
|
export default class TestChart extends Chart<TestLog> {
|
||||||
public total = 0; // publicにするのはテストのため
|
public total = 0; // publicにするのはテストのため
|
||||||
|
|
||||||
|
@ -42,7 +46,7 @@ export default class TestChart extends Chart<TestLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async increment() {
|
public async increment(): Promise<void> {
|
||||||
const update: Obj = {};
|
const update: Obj = {};
|
||||||
|
|
||||||
update.total = 1;
|
update.total = 1;
|
||||||
|
@ -55,7 +59,7 @@ export default class TestChart extends Chart<TestLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async decrement() {
|
public async decrement(): Promise<void> {
|
||||||
const update: Obj = {};
|
const update: Obj = {};
|
||||||
|
|
||||||
update.total = -1;
|
update.total = -1;
|
|
@ -1,13 +1,17 @@
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Chart, { Obj, DeepPartial } from '../../core';
|
import Chart, { Obj, DeepPartial } from '../core';
|
||||||
import { SchemaType } from '@/misc/schema';
|
import { SchemaType } from '@/misc/schema';
|
||||||
import { Users } from '@/models/index';
|
import { Users } from '@/models/index';
|
||||||
import { Not, IsNull } from 'typeorm';
|
import { Not, IsNull } from 'typeorm';
|
||||||
import { User } from '@/models/entities/user';
|
import { User } from '@/models/entities/user';
|
||||||
import { name, schema } from '../schemas/users';
|
import { name, schema } from './entities/users';
|
||||||
|
|
||||||
type UsersLog = SchemaType<typeof schema>;
|
type UsersLog = SchemaType<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ユーザー数に関するチャート
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default class UsersChart extends Chart<UsersLog> {
|
export default class UsersChart extends Chart<UsersLog> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(name, schema);
|
super(name, schema);
|
||||||
|
@ -59,7 +63,7 @@ export default class UsersChart extends Chart<UsersLog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean) {
|
public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean): Promise<void> {
|
||||||
const update: Obj = {};
|
const update: Obj = {};
|
||||||
|
|
||||||
update.total = isAdditional ? 1 : -1;
|
update.total = isAdditional ? 1 : -1;
|
|
@ -30,7 +30,7 @@ type Log = {
|
||||||
/**
|
/**
|
||||||
* 集計のグループ
|
* 集計のグループ
|
||||||
*/
|
*/
|
||||||
group: string | null;
|
group?: string | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 集計日時のUnixタイムスタンプ(秒)
|
* 集計日時のUnixタイムスタンプ(秒)
|
||||||
|
@ -38,7 +38,7 @@ type Log = {
|
||||||
date: number;
|
date: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const camelToSnake = (str: string) => {
|
const camelToSnake = (str: string): string => {
|
||||||
return str.replace(/([A-Z])/g, s => '_' + s.charAt(0).toLowerCase());
|
return str.replace(/([A-Z])/g, s => '_' + s.charAt(0).toLowerCase());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ const removeDuplicates = (array: any[]) => Array.from(new Set(array));
|
||||||
/**
|
/**
|
||||||
* 様々なチャートの管理を司るクラス
|
* 様々なチャートの管理を司るクラス
|
||||||
*/
|
*/
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default abstract class Chart<T extends Record<string, any>> {
|
export default abstract class Chart<T extends Record<string, any>> {
|
||||||
private static readonly columnPrefix = '___';
|
private static readonly columnPrefix = '___';
|
||||||
private static readonly columnDot = '_';
|
private static readonly columnDot = '_';
|
||||||
|
@ -57,7 +58,8 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
group: string | null;
|
group: string | null;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
public schema: SimpleSchema;
|
public schema: SimpleSchema;
|
||||||
protected repository: Repository<Log>;
|
protected repositoryForHour: Repository<Log>;
|
||||||
|
protected repositoryForDay: Repository<Log>;
|
||||||
|
|
||||||
protected abstract genNewLog(latest: T): DeepPartial<T>;
|
protected abstract genNewLog(latest: T): DeepPartial<T>;
|
||||||
|
|
||||||
|
@ -181,9 +183,15 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public static schemaToEntity(name: string, schema: SimpleSchema): EntitySchema {
|
public static schemaToEntity(name: string, schema: SimpleSchema, grouped = false): {
|
||||||
return new EntitySchema({
|
hour: EntitySchema,
|
||||||
name: `__chart__${camelToSnake(name)}`,
|
day: EntitySchema,
|
||||||
|
} {
|
||||||
|
const createEntity = (span: 'hour' | 'day'): EntitySchema => new EntitySchema({
|
||||||
|
name:
|
||||||
|
span === 'hour' ? `__chart__${camelToSnake(name)}` :
|
||||||
|
span === 'day' ? `__chart_day__${camelToSnake(name)}` :
|
||||||
|
new Error('not happen') as never,
|
||||||
columns: {
|
columns: {
|
||||||
id: {
|
id: {
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
|
@ -193,37 +201,45 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
date: {
|
date: {
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
},
|
},
|
||||||
|
...(grouped ? {
|
||||||
group: {
|
group: {
|
||||||
type: 'varchar',
|
type: 'varchar',
|
||||||
length: 128,
|
length: 128,
|
||||||
nullable: true,
|
|
||||||
},
|
},
|
||||||
|
} : {}),
|
||||||
...Chart.convertSchemaToFlatColumnDefinitions(schema),
|
...Chart.convertSchemaToFlatColumnDefinitions(schema),
|
||||||
},
|
},
|
||||||
indices: [{
|
indices: [{
|
||||||
columns: ['date', 'group'],
|
columns: grouped ? ['date', 'group'] : ['date'],
|
||||||
unique: true,
|
unique: true,
|
||||||
}, { // groupにnullが含まれると↑のuniqueは機能しないので↓の部分インデックスでカバー
|
|
||||||
columns: ['date'],
|
|
||||||
unique: true,
|
|
||||||
where: '"group" IS NULL',
|
|
||||||
}],
|
}],
|
||||||
|
uniques: [{
|
||||||
|
columns: grouped ? ['date', 'group'] : ['date'],
|
||||||
|
}],
|
||||||
|
relations: {
|
||||||
|
/* TODO
|
||||||
|
group: {
|
||||||
|
target: () => Foo,
|
||||||
|
type: 'many-to-one',
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
hour: createEntity('hour'),
|
||||||
|
day: createEntity('day'),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(name: string, schema: SimpleSchema, grouped = false) {
|
constructor(name: string, schema: SimpleSchema, grouped = false) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.schema = schema;
|
this.schema = schema;
|
||||||
const entity = Chart.schemaToEntity(name, schema);
|
|
||||||
|
|
||||||
const keys = ['date'];
|
const { hour, day } = Chart.schemaToEntity(name, schema, grouped);
|
||||||
if (grouped) keys.push('group');
|
this.repositoryForHour = getRepository<Log>(hour);
|
||||||
|
this.repositoryForDay = getRepository<Log>(day);
|
||||||
entity.options.uniques = [{
|
|
||||||
columns: keys,
|
|
||||||
}];
|
|
||||||
|
|
||||||
this.repository = getRepository<Log>(entity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
|
@ -247,24 +263,40 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
private getLatestLog(group: string | null = null): Promise<Log | null> {
|
private getLatestLog(group: string | null, span: 'hour' | 'day'): Promise<Log | null> {
|
||||||
return this.repository.findOne({
|
const repository =
|
||||||
|
span === 'hour' ? this.repositoryForHour :
|
||||||
|
span === 'day' ? this.repositoryForDay :
|
||||||
|
new Error('not happen') as never;
|
||||||
|
|
||||||
|
return repository.findOne(group ? {
|
||||||
group: group,
|
group: group,
|
||||||
}, {
|
} : {}, {
|
||||||
order: {
|
order: {
|
||||||
date: -1,
|
date: -1,
|
||||||
},
|
},
|
||||||
}).then(x => x || null);
|
}).then(x => x || null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 現在(=今のHour or Day)のログをデータベースから探して、あればそれを返し、なければ作成して返します。
|
||||||
|
*/
|
||||||
@autobind
|
@autobind
|
||||||
private async getCurrentLog(group: string | null = null): Promise<Log> {
|
private async claimCurrentLog(group: string | null, span: 'hour' | 'day'): Promise<Log> {
|
||||||
const [y, m, d, h] = Chart.getCurrentDate();
|
const [y, m, d, h] = Chart.getCurrentDate();
|
||||||
|
|
||||||
const current = dateUTC([y, m, d, h]);
|
const current = dateUTC(
|
||||||
|
span === 'hour' ? [y, m, d, h] :
|
||||||
|
span === 'day' ? [y, m, d] :
|
||||||
|
new Error('not happen') as never);
|
||||||
|
|
||||||
// 現在(=今のHour)のログ
|
const repository =
|
||||||
const currentLog = await this.repository.findOne({
|
span === 'hour' ? this.repositoryForHour :
|
||||||
|
span === 'day' ? this.repositoryForDay :
|
||||||
|
new Error('not happen') as never;
|
||||||
|
|
||||||
|
// 現在(=今のHour or Day)のログ
|
||||||
|
const currentLog = await repository.findOne({
|
||||||
date: Chart.dateToTimestamp(current),
|
date: Chart.dateToTimestamp(current),
|
||||||
...(group ? { group: group } : {}),
|
...(group ? { group: group } : {}),
|
||||||
});
|
});
|
||||||
|
@ -283,7 +315,7 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
// * 昨日何もチャートを更新するような出来事がなかった場合は、
|
// * 昨日何もチャートを更新するような出来事がなかった場合は、
|
||||||
// * ログがそもそも作られずドキュメントが存在しないということがあり得るため、
|
// * ログがそもそも作られずドキュメントが存在しないということがあり得るため、
|
||||||
// * 「昨日の」と決め打ちせずに「もっとも最近の」とします
|
// * 「昨日の」と決め打ちせずに「もっとも最近の」とします
|
||||||
const latest = await this.getLatestLog(group);
|
const latest = await this.getLatestLog(group, span);
|
||||||
|
|
||||||
if (latest != null) {
|
if (latest != null) {
|
||||||
const obj = Chart.convertFlattenColumnsToObject(latest) as T;
|
const obj = Chart.convertFlattenColumnsToObject(latest) as T;
|
||||||
|
@ -297,16 +329,16 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
// 初期ログデータを作成
|
// 初期ログデータを作成
|
||||||
data = this.getNewLog(null);
|
data = this.getNewLog(null);
|
||||||
|
|
||||||
logger.info(`${this.name + (group ? `:${group}` : '')}: Initial commit created`);
|
logger.info(`${this.name + (group ? `:${group}` : '')}(${span}): Initial commit created`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const date = Chart.dateToTimestamp(current);
|
const date = Chart.dateToTimestamp(current);
|
||||||
const lockKey = `${this.name}:${date}:${group}`;
|
const lockKey = group ? `${this.name}:${date}:${span}:${group}` : `${this.name}:${date}:${span}`;
|
||||||
|
|
||||||
const unlock = await getChartInsertLock(lockKey);
|
const unlock = await getChartInsertLock(lockKey);
|
||||||
try {
|
try {
|
||||||
// ロック内でもう1回チェックする
|
// ロック内でもう1回チェックする
|
||||||
const currentLog = await this.repository.findOne({
|
const currentLog = await repository.findOne({
|
||||||
date: date,
|
date: date,
|
||||||
...(group ? { group: group } : {}),
|
...(group ? { group: group } : {}),
|
||||||
});
|
});
|
||||||
|
@ -315,13 +347,13 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
if (currentLog != null) return currentLog;
|
if (currentLog != null) return currentLog;
|
||||||
|
|
||||||
// 新規ログ挿入
|
// 新規ログ挿入
|
||||||
log = await this.repository.insert({
|
log = await repository.insert({
|
||||||
group: group,
|
|
||||||
date: date,
|
date: date,
|
||||||
|
...(group ? { group: group } : {}),
|
||||||
...Chart.convertObjectToFlattenColumns(data),
|
...Chart.convertObjectToFlattenColumns(data),
|
||||||
}).then(x => this.repository.findOneOrFail(x.identifiers[0]));
|
}).then(x => repository.findOneOrFail(x.identifiers[0]));
|
||||||
|
|
||||||
logger.info(`${this.name + (group ? `:${group}` : '')}: New commit created`);
|
logger.info(`${this.name + (group ? `:${group}` : '')}(${span}): New commit created`);
|
||||||
|
|
||||||
return log;
|
return log;
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -349,10 +381,10 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
// そのログは本来は 01:00~ のログとしてDBに保存されて欲しいのに、02:00~ のログ扱いになってしまう。
|
// そのログは本来は 01:00~ のログとしてDBに保存されて欲しいのに、02:00~ のログ扱いになってしまう。
|
||||||
// これを回避するための実装は複雑になりそうなため、一旦保留。
|
// これを回避するための実装は複雑になりそうなため、一旦保留。
|
||||||
|
|
||||||
const update = async (log: Log) => {
|
const update = async (logHour: Log, logDay: Log): Promise<void> => {
|
||||||
const finalDiffs = {} as Record<string, number | unknown[]>;
|
const finalDiffs = {} as Record<string, number | unknown[]>;
|
||||||
|
|
||||||
for (const diff of this.buffer.filter(q => q.group === log.group).map(q => q.diff)) {
|
for (const diff of this.buffer.filter(q => q.group == null || (q.group === logHour.group)).map(q => q.diff)) {
|
||||||
const columns = Chart.convertObjectToFlattenColumns(diff);
|
const columns = Chart.convertObjectToFlattenColumns(diff);
|
||||||
|
|
||||||
for (const [k, v] of Object.entries(columns)) {
|
for (const [k, v] of Object.entries(columns)) {
|
||||||
|
@ -371,36 +403,60 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
const query = Chart.convertQuery(finalDiffs);
|
const query = Chart.convertQuery(finalDiffs);
|
||||||
|
|
||||||
// ログ更新
|
// ログ更新
|
||||||
await this.repository.createQueryBuilder()
|
await Promise.all([
|
||||||
|
this.repositoryForHour.createQueryBuilder()
|
||||||
.update()
|
.update()
|
||||||
.set(query)
|
.set(query)
|
||||||
.where('id = :id', { id: log.id })
|
.where('id = :id', { id: logHour.id })
|
||||||
.execute();
|
.execute(),
|
||||||
|
this.repositoryForDay.createQueryBuilder()
|
||||||
|
.update()
|
||||||
|
.set(query)
|
||||||
|
.where('id = :id', { id: logDay.id })
|
||||||
|
.execute(),
|
||||||
|
]);
|
||||||
|
|
||||||
logger.info(`${this.name + (log.group ? `:${log.group}` : '')}: Updated`);
|
logger.info(`${this.name + (logHour.group ? `:${logHour.group}` : '')}: Updated`);
|
||||||
|
|
||||||
// TODO: この一連の処理が始まった後に新たにbufferに入ったものは消さないようにする
|
// TODO: この一連の処理が始まった後に新たにbufferに入ったものは消さないようにする
|
||||||
this.buffer = this.buffer.filter(q => q.group !== log.group);
|
this.buffer = this.buffer.filter(q => q.group != null && (q.group !== logHour.group));
|
||||||
};
|
};
|
||||||
|
|
||||||
const groups = removeDuplicates(this.buffer.map(log => log.group));
|
const groups = removeDuplicates(this.buffer.map(log => log.group));
|
||||||
|
|
||||||
await Promise.all(groups.map(group => this.getCurrentLog(group).then(log => update(log))));
|
await Promise.all(
|
||||||
|
groups.map(group =>
|
||||||
|
Promise.all([
|
||||||
|
this.claimCurrentLog(group, 'hour'),
|
||||||
|
this.claimCurrentLog(group, 'day'),
|
||||||
|
]).then(([logHour, logDay]) =>
|
||||||
|
update(logHour, logDay))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public async resync(group: string | null = null): Promise<any> {
|
public async resync(group: string | null = null): Promise<void> {
|
||||||
const data = await this.fetchActual(group);
|
const data = await this.fetchActual(group);
|
||||||
|
|
||||||
const update = async (log: Log) => {
|
const update = async (logHour: Log, logDay: Log): Promise<void> => {
|
||||||
await this.repository.createQueryBuilder()
|
await Promise.all([
|
||||||
|
this.repositoryForHour.createQueryBuilder()
|
||||||
.update()
|
.update()
|
||||||
.set(Chart.convertObjectToFlattenColumns(data))
|
.set(Chart.convertObjectToFlattenColumns(data))
|
||||||
.where('id = :id', { id: log.id })
|
.where('id = :id', { id: logHour.id })
|
||||||
.execute();
|
.execute(),
|
||||||
|
this.repositoryForDay.createQueryBuilder()
|
||||||
|
.update()
|
||||||
|
.set(Chart.convertObjectToFlattenColumns(data))
|
||||||
|
.where('id = :id', { id: logDay.id })
|
||||||
|
.execute(),
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.getCurrentLog(group).then(log => update(log));
|
return Promise.all([
|
||||||
|
this.claimCurrentLog(group, 'hour'),
|
||||||
|
this.claimCurrentLog(group, 'day'),
|
||||||
|
]).then(([logHour, logDay]) =>
|
||||||
|
update(logHour, logDay));
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
|
@ -418,13 +474,18 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
const gt =
|
const gt =
|
||||||
span === 'day' ? subtractTime(cursor ? dateUTC([y2, m2, d2, 0]) : dateUTC([y, m, d, 0]), amount - 1, 'day') :
|
span === 'day' ? subtractTime(cursor ? dateUTC([y2, m2, d2, 0]) : dateUTC([y, m, d, 0]), amount - 1, 'day') :
|
||||||
span === 'hour' ? subtractTime(cursor ? dateUTC([y2, m2, d2, h2]) : dateUTC([y, m, d, h]), amount - 1, 'hour') :
|
span === 'hour' ? subtractTime(cursor ? dateUTC([y2, m2, d2, h2]) : dateUTC([y, m, d, h]), amount - 1, 'hour') :
|
||||||
null as never;
|
new Error('not happen') as never;
|
||||||
|
|
||||||
|
const repository =
|
||||||
|
span === 'hour' ? this.repositoryForHour :
|
||||||
|
span === 'day' ? this.repositoryForDay :
|
||||||
|
new Error('not happen') as never;
|
||||||
|
|
||||||
// ログ取得
|
// ログ取得
|
||||||
let logs = await this.repository.find({
|
let logs = await repository.find({
|
||||||
where: {
|
where: {
|
||||||
group: group,
|
|
||||||
date: Between(Chart.dateToTimestamp(gt), Chart.dateToTimestamp(lt)),
|
date: Between(Chart.dateToTimestamp(gt), Chart.dateToTimestamp(lt)),
|
||||||
|
...(group ? { group: group } : {}),
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
date: -1,
|
date: -1,
|
||||||
|
@ -435,9 +496,9 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
if (logs.length === 0) {
|
if (logs.length === 0) {
|
||||||
// もっとも新しいログを持ってくる
|
// もっとも新しいログを持ってくる
|
||||||
// (すくなくともひとつログが無いと隙間埋めできないため)
|
// (すくなくともひとつログが無いと隙間埋めできないため)
|
||||||
const recentLog = await this.repository.findOne({
|
const recentLog = await repository.findOne(group ? {
|
||||||
group: group,
|
group: group,
|
||||||
}, {
|
} : {}, {
|
||||||
order: {
|
order: {
|
||||||
date: -1,
|
date: -1,
|
||||||
},
|
},
|
||||||
|
@ -451,9 +512,9 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
} else if (!isTimeSame(new Date(logs[logs.length - 1].date * 1000), gt)) {
|
} else if (!isTimeSame(new Date(logs[logs.length - 1].date * 1000), gt)) {
|
||||||
// 要求された範囲の最も古い箇所時点での最も新しいログを持ってきて末尾に追加する
|
// 要求された範囲の最も古い箇所時点での最も新しいログを持ってきて末尾に追加する
|
||||||
// (隙間埋めできないため)
|
// (隙間埋めできないため)
|
||||||
const outdatedLog = await this.repository.findOne({
|
const outdatedLog = await repository.findOne({
|
||||||
group: group,
|
|
||||||
date: LessThan(Chart.dateToTimestamp(gt)),
|
date: LessThan(Chart.dateToTimestamp(gt)),
|
||||||
|
...(group ? { group: group } : {}),
|
||||||
}, {
|
}, {
|
||||||
order: {
|
order: {
|
||||||
date: -1,
|
date: -1,
|
||||||
|
@ -467,9 +528,11 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
|
|
||||||
const chart: T[] = [];
|
const chart: T[] = [];
|
||||||
|
|
||||||
if (span === 'hour') {
|
|
||||||
for (let i = (amount - 1); i >= 0; i--) {
|
for (let i = (amount - 1); i >= 0; i--) {
|
||||||
const current = subtractTime(dateUTC([y, m, d, h]), i, 'hour');
|
const current =
|
||||||
|
span === 'hour' ? subtractTime(dateUTC([y, m, d, h]), i, 'hour') :
|
||||||
|
span === 'day' ? subtractTime(dateUTC([y, m, d]), i, 'day') :
|
||||||
|
new Error('not happen') as never;
|
||||||
|
|
||||||
const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current));
|
const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current));
|
||||||
|
|
||||||
|
@ -483,44 +546,8 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
chart.unshift(Chart.countUniqueFields(this.getNewLog(data)) as T);
|
chart.unshift(Chart.countUniqueFields(this.getNewLog(data)) as T);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (span === 'day') {
|
|
||||||
const logsForEachDays: T[][] = [];
|
|
||||||
let currentDay = -1;
|
|
||||||
let currentDayIndex = -1;
|
|
||||||
for (let i = ((amount - 1) * 24) + h; i >= 0; i--) {
|
|
||||||
const current = subtractTime(dateUTC([y, m, d, h]), i, 'hour');
|
|
||||||
const _currentDay = Chart.parseDate(current)[2];
|
|
||||||
if (currentDay != _currentDay) currentDayIndex++;
|
|
||||||
currentDay = _currentDay;
|
|
||||||
|
|
||||||
const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current));
|
const res = {} as Record<string, unknown>;
|
||||||
|
|
||||||
if (log) {
|
|
||||||
if (logsForEachDays[currentDayIndex]) {
|
|
||||||
logsForEachDays[currentDayIndex].unshift(Chart.convertFlattenColumnsToObject(log) as T);
|
|
||||||
} else {
|
|
||||||
logsForEachDays[currentDayIndex] = [Chart.convertFlattenColumnsToObject(log) as T];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 隙間埋め
|
|
||||||
const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current));
|
|
||||||
const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null;
|
|
||||||
const newLog = this.getNewLog(data);
|
|
||||||
if (logsForEachDays[currentDayIndex]) {
|
|
||||||
logsForEachDays[currentDayIndex].unshift(newLog);
|
|
||||||
} else {
|
|
||||||
logsForEachDays[currentDayIndex] = [newLog];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const logs of logsForEachDays) {
|
|
||||||
const log = this.aggregate(logs);
|
|
||||||
chart.unshift(Chart.countUniqueFields(log) as T);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const res: ArrayValue<T> = {} as any;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [{ foo: 1, bar: 5 }, { foo: 2, bar: 6 }, { foo: 3, bar: 7 }]
|
* [{ foo: 1, bar: 5 }, { foo: 2, bar: 6 }, { foo: 3, bar: 7 }]
|
||||||
|
@ -528,7 +555,7 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
* { foo: [1, 2, 3], bar: [5, 6, 7] }
|
* { foo: [1, 2, 3], bar: [5, 6, 7] }
|
||||||
* にする
|
* にする
|
||||||
*/
|
*/
|
||||||
const compact = (x: Obj, path?: string) => {
|
const compact = (x: Obj, path?: string): void => {
|
||||||
for (const [k, v] of Object.entries(x)) {
|
for (const [k, v] of Object.entries(x)) {
|
||||||
const p = path ? `${path}.${k}` : k;
|
const p = path ? `${path}.${k}` : k;
|
||||||
if (typeof v === 'object' && !Array.isArray(v)) {
|
if (typeof v === 'object' && !Array.isArray(v)) {
|
||||||
|
@ -542,7 +569,7 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
|
|
||||||
compact(chart[0]);
|
compact(chart[0]);
|
||||||
|
|
||||||
return res;
|
return res as ArrayValue<T>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,27 @@
|
||||||
import { fileURLToPath } from 'url';
|
import { entity as FederationChart } from './charts/entities/federation';
|
||||||
import { dirname } from 'path';
|
import { entity as NotesChart } from './charts/entities/notes';
|
||||||
import Chart from './core';
|
import { entity as UsersChart } from './charts/entities/users';
|
||||||
|
import { entity as NetworkChart } from './charts/entities/network';
|
||||||
|
import { entity as ActiveUsersChart } from './charts/entities/active-users';
|
||||||
|
import { entity as InstanceChart } from './charts/entities/instance';
|
||||||
|
import { entity as PerUserNotesChart } from './charts/entities/per-user-notes';
|
||||||
|
import { entity as DriveChart } from './charts/entities/drive';
|
||||||
|
import { entity as PerUserReactionsChart } from './charts/entities/per-user-reactions';
|
||||||
|
import { entity as HashtagChart } from './charts/entities/hashtag';
|
||||||
|
import { entity as PerUserFollowingChart } from './charts/entities/per-user-following';
|
||||||
|
import { entity as PerUserDriveChart } from './charts/entities/per-user-drive';
|
||||||
|
|
||||||
//const _filename = fileURLToPath(import.meta.url);
|
export const entities = [
|
||||||
const _filename = __filename;
|
FederationChart.hour, FederationChart.day,
|
||||||
const _dirname = dirname(_filename);
|
NotesChart.hour, NotesChart.day,
|
||||||
|
UsersChart.hour, UsersChart.day,
|
||||||
export const entities = Object.values(require('require-all')({
|
NetworkChart.hour, NetworkChart.day,
|
||||||
dirname: _dirname + '/charts/schemas',
|
ActiveUsersChart.hour, ActiveUsersChart.day,
|
||||||
filter: /^.+\.[jt]s$/,
|
InstanceChart.hour, InstanceChart.day,
|
||||||
resolve: (x: any) => {
|
PerUserNotesChart.hour, PerUserNotesChart.day,
|
||||||
return Chart.schemaToEntity(x.name, x.schema);
|
DriveChart.hour, DriveChart.day,
|
||||||
},
|
PerUserReactionsChart.hour, PerUserReactionsChart.day,
|
||||||
}));
|
HashtagChart.hour, HashtagChart.day,
|
||||||
|
PerUserFollowingChart.hour, PerUserFollowingChart.day,
|
||||||
|
PerUserDriveChart.hour, PerUserDriveChart.day,
|
||||||
|
];
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import FederationChart from './charts/classes/federation';
|
|
||||||
import NotesChart from './charts/classes/notes';
|
|
||||||
import UsersChart from './charts/classes/users';
|
|
||||||
import NetworkChart from './charts/classes/network';
|
|
||||||
import ActiveUsersChart from './charts/classes/active-users';
|
|
||||||
import InstanceChart from './charts/classes/instance';
|
|
||||||
import PerUserNotesChart from './charts/classes/per-user-notes';
|
|
||||||
import DriveChart from './charts/classes/drive';
|
|
||||||
import PerUserReactionsChart from './charts/classes/per-user-reactions';
|
|
||||||
import HashtagChart from './charts/classes/hashtag';
|
|
||||||
import PerUserFollowingChart from './charts/classes/per-user-following';
|
|
||||||
import PerUserDriveChart from './charts/classes/per-user-drive';
|
|
||||||
import { beforeShutdown } from '@/misc/before-shutdown';
|
import { beforeShutdown } from '@/misc/before-shutdown';
|
||||||
|
|
||||||
|
import FederationChart from './charts/federation';
|
||||||
|
import NotesChart from './charts/notes';
|
||||||
|
import UsersChart from './charts/users';
|
||||||
|
import NetworkChart from './charts/network';
|
||||||
|
import ActiveUsersChart from './charts/active-users';
|
||||||
|
import InstanceChart from './charts/instance';
|
||||||
|
import PerUserNotesChart from './charts/per-user-notes';
|
||||||
|
import DriveChart from './charts/drive';
|
||||||
|
import PerUserReactionsChart from './charts/per-user-reactions';
|
||||||
|
import HashtagChart from './charts/hashtag';
|
||||||
|
import PerUserFollowingChart from './charts/per-user-following';
|
||||||
|
import PerUserDriveChart from './charts/per-user-drive';
|
||||||
|
|
||||||
export const federationChart = new FederationChart();
|
export const federationChart = new FederationChart();
|
||||||
export const notesChart = new NotesChart();
|
export const notesChart = new NotesChart();
|
||||||
export const usersChart = new UsersChart();
|
export const usersChart = new UsersChart();
|
||||||
|
|
|
@ -3,13 +3,12 @@ process.env.NODE_ENV = 'test';
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import * as lolex from '@sinonjs/fake-timers';
|
import * as lolex from '@sinonjs/fake-timers';
|
||||||
import { async, initTestDb } from './utils';
|
import { async, initTestDb } from './utils';
|
||||||
import TestChart from '../src/services/chart/charts/classes/test';
|
import TestChart from '../src/services/chart/charts/test';
|
||||||
import TestGroupedChart from '../src/services/chart/charts/classes/test-grouped';
|
import TestGroupedChart from '../src/services/chart/charts/test-grouped';
|
||||||
import TestUniqueChart from '../src/services/chart/charts/classes/test-unique';
|
import TestUniqueChart from '../src/services/chart/charts/test-unique';
|
||||||
import * as _TestChart from '../src/services/chart/charts/schemas/test';
|
import * as _TestChart from '../src/services/chart/charts/entities/test';
|
||||||
import * as _TestGroupedChart from '../src/services/chart/charts/schemas/test-grouped';
|
import * as _TestGroupedChart from '../src/services/chart/charts/entities/test-grouped';
|
||||||
import * as _TestUniqueChart from '../src/services/chart/charts/schemas/test-unique';
|
import * as _TestUniqueChart from '../src/services/chart/charts/entities/test-unique';
|
||||||
import Chart from '../src/services/chart/core';
|
|
||||||
|
|
||||||
describe('Chart', () => {
|
describe('Chart', () => {
|
||||||
let testChart: TestChart;
|
let testChart: TestChart;
|
||||||
|
@ -19,9 +18,9 @@ describe('Chart', () => {
|
||||||
|
|
||||||
beforeEach(async(async () => {
|
beforeEach(async(async () => {
|
||||||
await initTestDb(false, [
|
await initTestDb(false, [
|
||||||
Chart.schemaToEntity(_TestChart.name, _TestChart.schema),
|
_TestChart.entity.hour, _TestChart.entity.day,
|
||||||
Chart.schemaToEntity(_TestGroupedChart.name, _TestGroupedChart.schema),
|
_TestGroupedChart.entity.hour, _TestGroupedChart.entity.day,
|
||||||
Chart.schemaToEntity(_TestUniqueChart.name, _TestUniqueChart.schema)
|
_TestUniqueChart.entity.hour, _TestUniqueChart.entity.day,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
testChart = new TestChart();
|
testChart = new TestChart();
|
||||||
|
@ -132,6 +131,32 @@ describe('Chart', () => {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('複数回saveされてもデータの更新は一度だけ', async(async () => {
|
||||||
|
await testChart.increment();
|
||||||
|
await testChart.save();
|
||||||
|
await testChart.save();
|
||||||
|
await testChart.save();
|
||||||
|
|
||||||
|
const chartHours = await testChart.getChart('hour', 3, null);
|
||||||
|
const chartDays = await testChart.getChart('day', 3, null);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(chartHours, {
|
||||||
|
foo: {
|
||||||
|
dec: [0, 0, 0],
|
||||||
|
inc: [1, 0, 0],
|
||||||
|
total: [1, 0, 0]
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(chartDays, {
|
||||||
|
foo: {
|
||||||
|
dec: [0, 0, 0],
|
||||||
|
inc: [1, 0, 0],
|
||||||
|
total: [1, 0, 0]
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('Can updates at different times', async(async () => {
|
it('Can updates at different times', async(async () => {
|
||||||
await testChart.increment();
|
await testChart.increment();
|
||||||
await testChart.save();
|
await testChart.save();
|
||||||
|
|
Loading…
Reference in a new issue