From 0be4e1046239f1ed62bb73d4df51a7b9eb1a135f Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 14 Dec 2021 18:12:37 +0900 Subject: [PATCH] enhance(backend): improve chart engine --- .../migration/1639325650583-chart-v3.js | 187 +++++++++++++ packages/backend/src/db/postgre.ts | 10 +- .../charts/{classes => }/active-users.ts | 10 +- .../chart/charts/{classes => }/drive.ts | 10 +- .../{schemas => entities}/active-users.ts | 11 +- .../charts/{schemas => entities}/drive.ts | 6 +- .../{schemas => entities}/federation.ts | 9 +- .../charts/{schemas => entities}/hashtag.ts | 11 +- .../charts/{schemas => entities}/instance.ts | 9 +- .../charts/{schemas => entities}/network.ts | 9 +- .../charts/{schemas => entities}/notes.ts | 6 +- .../{schemas => entities}/per-user-drive.ts | 6 +- .../per-user-following.ts | 8 +- .../{schemas => entities}/per-user-notes.ts | 6 +- .../per-user-reactions.ts | 13 +- .../{schemas => entities}/test-grouped.ts | 6 +- .../{schemas => entities}/test-unique.ts | 6 +- .../charts/{schemas => entities}/test.ts | 6 +- .../charts/{schemas => entities}/users.ts | 6 +- .../chart/charts/{classes => }/federation.ts | 10 +- .../chart/charts/{classes => }/hashtag.ts | 10 +- .../chart/charts/{classes => }/instance.ts | 26 +- .../chart/charts/{classes => }/network.ts | 10 +- .../chart/charts/{classes => }/notes.ts | 10 +- .../charts/{classes => }/per-user-drive.ts | 10 +- .../{classes => }/per-user-following.ts | 10 +- .../charts/{classes => }/per-user-notes.ts | 10 +- .../{classes => }/per-user-reactions.ts | 10 +- .../charts/{classes => }/test-grouped.ts | 10 +- .../chart/charts/{classes => }/test-unique.ts | 10 +- .../chart/charts/{classes => }/test.ts | 12 +- .../chart/charts/{classes => }/users.ts | 10 +- packages/backend/src/services/chart/core.ts | 259 ++++++++++-------- .../backend/src/services/chart/entities.ts | 40 ++- packages/backend/src/services/chart/index.ts | 25 +- packages/backend/test/chart.ts | 45 ++- 36 files changed, 604 insertions(+), 248 deletions(-) create mode 100644 packages/backend/migration/1639325650583-chart-v3.js rename packages/backend/src/services/chart/charts/{classes => }/active-users.ts (81%) rename packages/backend/src/services/chart/charts/{classes => }/drive.ts (89%) rename packages/backend/src/services/chart/charts/{schemas => entities}/active-users.ts (85%) rename packages/backend/src/services/chart/charts/{schemas => entities}/drive.ts (93%) rename packages/backend/src/services/chart/charts/{schemas => entities}/federation.ts (86%) rename packages/backend/src/services/chart/charts/{schemas => entities}/hashtag.ts (84%) rename packages/backend/src/services/chart/charts/{schemas => entities}/instance.ts (97%) rename packages/backend/src/services/chart/charts/{schemas => entities}/network.ts (89%) rename packages/backend/src/services/chart/charts/{schemas => entities}/notes.ts (92%) rename packages/backend/src/services/chart/charts/{schemas => entities}/per-user-drive.ts (92%) rename packages/backend/src/services/chart/charts/{schemas => entities}/per-user-following.ts (93%) rename packages/backend/src/services/chart/charts/{schemas => entities}/per-user-notes.ts (90%) rename packages/backend/src/services/chart/charts/{schemas => entities}/per-user-reactions.ts (78%) rename packages/backend/src/services/chart/charts/{schemas => entities}/test-grouped.ts (85%) rename packages/backend/src/services/chart/charts/{schemas => entities}/test-unique.ts (79%) rename packages/backend/src/services/chart/charts/{schemas => entities}/test.ts (86%) rename packages/backend/src/services/chart/charts/{schemas => entities}/users.ts (90%) rename packages/backend/src/services/chart/charts/{classes => }/federation.ts (80%) rename packages/backend/src/services/chart/charts/{classes => }/hashtag.ts (81%) rename packages/backend/src/services/chart/charts/{classes => }/instance.ts (89%) rename packages/backend/src/services/chart/charts/{classes => }/network.ts (80%) rename packages/backend/src/services/chart/charts/{classes => }/notes.ts (88%) rename packages/backend/src/services/chart/charts/{classes => }/per-user-drive.ts (83%) rename packages/backend/src/services/chart/charts/{classes => }/per-user-following.ts (92%) rename packages/backend/src/services/chart/charts/{classes => }/per-user-notes.ts (86%) rename packages/backend/src/services/chart/charts/{classes => }/per-user-reactions.ts (79%) rename packages/backend/src/services/chart/charts/{classes => }/test-grouped.ts (81%) rename packages/backend/src/services/chart/charts/{classes => }/test-unique.ts (73%) rename packages/backend/src/services/chart/charts/{classes => }/test.ts (80%) rename packages/backend/src/services/chart/charts/{classes => }/users.ts (86%) diff --git a/packages/backend/migration/1639325650583-chart-v3.js b/packages/backend/migration/1639325650583-chart-v3.js new file mode 100644 index 0000000000..95f8432079 --- /dev/null +++ b/packages/backend/migration/1639325650583-chart-v3.js @@ -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") `); + } +} diff --git a/packages/backend/src/db/postgre.ts b/packages/backend/src/db/postgre.ts index 4daf6b4434..b5f228d919 100644 --- a/packages/backend/src/db/postgre.ts +++ b/packages/backend/src/db/postgre.ts @@ -3,10 +3,10 @@ const types = require('pg').types; types.setTypeParser(20, Number); 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 config from '@/config/index'; + +import { dbLogger } from './logger'; import { User } from '@/models/entities/user'; 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 { UserPending } from '@/models/entities/user-pending'; +import { entities as charts } from '@/services/chart/entities'; + const sqlLogger = dbLogger.createSubLogger('sql', 'white', false); class MyCustomLogger implements Logger { @@ -175,7 +177,7 @@ export const entities = [ Ad, PasswordResetRequest, UserPending, - ...charts as any, + ...charts, ]; export function initDb(justBorrow = false, sync = false, forceRecreate = false) { diff --git a/packages/backend/src/services/chart/charts/classes/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts similarity index 81% rename from packages/backend/src/services/chart/charts/classes/active-users.ts rename to packages/backend/src/services/chart/charts/active-users.ts index 029e93a391..9490101e36 100644 --- a/packages/backend/src/services/chart/charts/classes/active-users.ts +++ b/packages/backend/src/services/chart/charts/active-users.ts @@ -1,12 +1,16 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../../core'; +import Chart, { Obj, DeepPartial } from '../core'; import { User } from '@/models/entities/user'; import { SchemaType } from '@/misc/schema'; import { Users } from '@/models/index'; -import { name, schema } from '../schemas/active-users'; +import { name, schema } from './entities/active-users'; type ActiveUsersLog = SchemaType; +/** + * アクティブユーザーに関するチャート + */ +// eslint-disable-next-line import/no-default-export export default class ActiveUsersChart extends Chart { constructor() { super(name, schema); @@ -35,7 +39,7 @@ export default class ActiveUsersChart extends Chart { } @autobind - public async update(user: { id: User['id'], host: User['host'] }) { + public async update(user: { id: User['id'], host: User['host'] }): Promise { const update: Obj = { users: [user.id], }; diff --git a/packages/backend/src/services/chart/charts/classes/drive.ts b/packages/backend/src/services/chart/charts/drive.ts similarity index 89% rename from packages/backend/src/services/chart/charts/classes/drive.ts rename to packages/backend/src/services/chart/charts/drive.ts index 2ed8e7fad1..06cf7ebeeb 100644 --- a/packages/backend/src/services/chart/charts/classes/drive.ts +++ b/packages/backend/src/services/chart/charts/drive.ts @@ -1,13 +1,17 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../../core'; +import Chart, { Obj, DeepPartial } from '../core'; import { SchemaType } from '@/misc/schema'; import { DriveFiles } from '@/models/index'; import { Not, IsNull } from 'typeorm'; import { DriveFile } from '@/models/entities/drive-file'; -import { name, schema } from '../schemas/drive'; +import { name, schema } from './entities/drive'; type DriveLog = SchemaType; +/** + * ドライブに関するチャート + */ +// eslint-disable-next-line import/no-default-export export default class DriveChart extends Chart { constructor() { super(name, schema); @@ -71,7 +75,7 @@ export default class DriveChart extends Chart { } @autobind - public async update(file: DriveFile, isAdditional: boolean) { + public async update(file: DriveFile, isAdditional: boolean): Promise { const update: Obj = {}; update.totalCount = isAdditional ? 1 : -1; diff --git a/packages/backend/src/services/chart/charts/schemas/active-users.ts b/packages/backend/src/services/chart/charts/entities/active-users.ts similarity index 85% rename from packages/backend/src/services/chart/charts/schemas/active-users.ts rename to packages/backend/src/services/chart/charts/entities/active-users.ts index 322fdb8691..d6b49c86c3 100644 --- a/packages/backend/src/services/chart/charts/schemas/active-users.ts +++ b/packages/backend/src/services/chart/charts/entities/active-users.ts @@ -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 = { type: 'object' 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); diff --git a/packages/backend/src/services/chart/charts/schemas/drive.ts b/packages/backend/src/services/chart/charts/entities/drive.ts similarity index 93% rename from packages/backend/src/services/chart/charts/schemas/drive.ts rename to packages/backend/src/services/chart/charts/entities/drive.ts index e7d3267931..3362cbd4cb 100644 --- a/packages/backend/src/services/chart/charts/schemas/drive.ts +++ b/packages/backend/src/services/chart/charts/entities/drive.ts @@ -1,3 +1,7 @@ +import Chart from '../../core'; + +export const name = 'drive'; + const logSchema = { /** * 集計期間時点での、全ドライブファイル数 @@ -65,4 +69,4 @@ export const schema = { }, }; -export const name = 'drive'; +export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/schemas/federation.ts b/packages/backend/src/services/chart/charts/entities/federation.ts similarity index 86% rename from packages/backend/src/services/chart/charts/schemas/federation.ts rename to packages/backend/src/services/chart/charts/entities/federation.ts index 1b8bbce0a8..836116bd06 100644 --- a/packages/backend/src/services/chart/charts/schemas/federation.ts +++ b/packages/backend/src/services/chart/charts/entities/federation.ts @@ -1,6 +1,7 @@ -/** - * フェデレーションに関するチャート - */ +import Chart from '../../core'; + +export const name = 'federation'; + export const schema = { type: 'object' 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); diff --git a/packages/backend/src/services/chart/charts/schemas/hashtag.ts b/packages/backend/src/services/chart/charts/entities/hashtag.ts similarity index 84% rename from packages/backend/src/services/chart/charts/schemas/hashtag.ts rename to packages/backend/src/services/chart/charts/entities/hashtag.ts index 6b4eca2978..43e15456a5 100644 --- a/packages/backend/src/services/chart/charts/schemas/hashtag.ts +++ b/packages/backend/src/services/chart/charts/entities/hashtag.ts @@ -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 = { type: 'object' 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); diff --git a/packages/backend/src/services/chart/charts/schemas/instance.ts b/packages/backend/src/services/chart/charts/entities/instance.ts similarity index 97% rename from packages/backend/src/services/chart/charts/schemas/instance.ts rename to packages/backend/src/services/chart/charts/entities/instance.ts index 5f17ac0d11..9d1f651dbb 100644 --- a/packages/backend/src/services/chart/charts/schemas/instance.ts +++ b/packages/backend/src/services/chart/charts/entities/instance.ts @@ -1,6 +1,7 @@ -/** - * インスタンスごとのチャート - */ +import Chart from '../../core'; + +export const name = 'instance'; + export const schema = { type: 'object' 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); diff --git a/packages/backend/src/services/chart/charts/schemas/network.ts b/packages/backend/src/services/chart/charts/entities/network.ts similarity index 89% rename from packages/backend/src/services/chart/charts/schemas/network.ts rename to packages/backend/src/services/chart/charts/entities/network.ts index 1aeb2a8ad5..3d4fffb855 100644 --- a/packages/backend/src/services/chart/charts/schemas/network.ts +++ b/packages/backend/src/services/chart/charts/entities/network.ts @@ -1,6 +1,7 @@ -/** - * ネットワークに関するチャート - */ +import Chart from '../../core'; + +export const name = 'network'; + export const schema = { type: 'object' 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); diff --git a/packages/backend/src/services/chart/charts/schemas/notes.ts b/packages/backend/src/services/chart/charts/entities/notes.ts similarity index 92% rename from packages/backend/src/services/chart/charts/schemas/notes.ts rename to packages/backend/src/services/chart/charts/entities/notes.ts index a33e7aa8b9..554d3abe12 100644 --- a/packages/backend/src/services/chart/charts/schemas/notes.ts +++ b/packages/backend/src/services/chart/charts/entities/notes.ts @@ -1,3 +1,7 @@ +import Chart from '../../core'; + +export const name = 'notes'; + const logSchema = { total: { type: 'number' as const, @@ -53,4 +57,4 @@ export const schema = { }, }; -export const name = 'notes'; +export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/schemas/per-user-drive.ts b/packages/backend/src/services/chart/charts/entities/per-user-drive.ts similarity index 92% rename from packages/backend/src/services/chart/charts/schemas/per-user-drive.ts rename to packages/backend/src/services/chart/charts/entities/per-user-drive.ts index 1c7c57afda..ebf64e733e 100644 --- a/packages/backend/src/services/chart/charts/schemas/per-user-drive.ts +++ b/packages/backend/src/services/chart/charts/entities/per-user-drive.ts @@ -1,3 +1,7 @@ +import Chart from '../../core'; + +export const name = 'perUserDrive'; + export const schema = { type: 'object' 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); diff --git a/packages/backend/src/services/chart/charts/schemas/per-user-following.ts b/packages/backend/src/services/chart/charts/entities/per-user-following.ts similarity index 93% rename from packages/backend/src/services/chart/charts/schemas/per-user-following.ts rename to packages/backend/src/services/chart/charts/entities/per-user-following.ts index 9e7e71f3bb..8016c5fe97 100644 --- a/packages/backend/src/services/chart/charts/schemas/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/entities/per-user-following.ts @@ -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); diff --git a/packages/backend/src/services/chart/charts/schemas/per-user-notes.ts b/packages/backend/src/services/chart/charts/entities/per-user-notes.ts similarity index 90% rename from packages/backend/src/services/chart/charts/schemas/per-user-notes.ts rename to packages/backend/src/services/chart/charts/entities/per-user-notes.ts index 55b4ff40c0..d8f645b36e 100644 --- a/packages/backend/src/services/chart/charts/schemas/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/entities/per-user-notes.ts @@ -1,3 +1,7 @@ +import Chart from '../../core'; + +export const name = 'perUserNotes'; + export const schema = { type: 'object' 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); diff --git a/packages/backend/src/services/chart/charts/schemas/per-user-reactions.ts b/packages/backend/src/services/chart/charts/entities/per-user-reactions.ts similarity index 78% rename from packages/backend/src/services/chart/charts/schemas/per-user-reactions.ts rename to packages/backend/src/services/chart/charts/entities/per-user-reactions.ts index 9af6c3cd54..bcb7012661 100644 --- a/packages/backend/src/services/chart/charts/schemas/per-user-reactions.ts +++ b/packages/backend/src/services/chart/charts/entities/per-user-reactions.ts @@ -1,6 +1,10 @@ -export const logSchema = { +import Chart from '../../core'; + +export const name = 'perUserReaction'; + +const logSchema = { /** - * フォローしている合計 + * 被リアクション数 */ count: { type: 'number' as const, @@ -8,9 +12,6 @@ export const logSchema = { }, }; -/** - * ユーザーごとのリアクションに関するチャート - */ export const schema = { type: 'object' 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); diff --git a/packages/backend/src/services/chart/charts/schemas/test-grouped.ts b/packages/backend/src/services/chart/charts/entities/test-grouped.ts similarity index 85% rename from packages/backend/src/services/chart/charts/schemas/test-grouped.ts rename to packages/backend/src/services/chart/charts/entities/test-grouped.ts index c2f2e7e886..ca1c8c5700 100644 --- a/packages/backend/src/services/chart/charts/schemas/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/entities/test-grouped.ts @@ -1,3 +1,7 @@ +import Chart from '../../core'; + +export const name = 'testGrouped'; + export const schema = { type: 'object' 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); diff --git a/packages/backend/src/services/chart/charts/schemas/test-unique.ts b/packages/backend/src/services/chart/charts/entities/test-unique.ts similarity index 79% rename from packages/backend/src/services/chart/charts/schemas/test-unique.ts rename to packages/backend/src/services/chart/charts/entities/test-unique.ts index b8da60dc53..2e917ee9ed 100644 --- a/packages/backend/src/services/chart/charts/schemas/test-unique.ts +++ b/packages/backend/src/services/chart/charts/entities/test-unique.ts @@ -1,3 +1,7 @@ +import Chart from '../../core'; + +export const name = 'testUnique'; + export const schema = { type: 'object' 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); diff --git a/packages/backend/src/services/chart/charts/schemas/test.ts b/packages/backend/src/services/chart/charts/entities/test.ts similarity index 86% rename from packages/backend/src/services/chart/charts/schemas/test.ts rename to packages/backend/src/services/chart/charts/entities/test.ts index 19c732fece..fa536ff2cf 100644 --- a/packages/backend/src/services/chart/charts/schemas/test.ts +++ b/packages/backend/src/services/chart/charts/entities/test.ts @@ -1,3 +1,7 @@ +import Chart from '../../core'; + +export const name = 'test'; + export const schema = { type: 'object' 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); diff --git a/packages/backend/src/services/chart/charts/schemas/users.ts b/packages/backend/src/services/chart/charts/entities/users.ts similarity index 90% rename from packages/backend/src/services/chart/charts/schemas/users.ts rename to packages/backend/src/services/chart/charts/entities/users.ts index 47e7791974..08d51c9414 100644 --- a/packages/backend/src/services/chart/charts/schemas/users.ts +++ b/packages/backend/src/services/chart/charts/entities/users.ts @@ -1,3 +1,7 @@ +import Chart from '../../core'; + +export const name = 'users'; + const logSchema = { /** * 集計期間時点での、全ユーザー数 @@ -41,4 +45,4 @@ export const schema = { }, }; -export const name = 'users'; +export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/classes/federation.ts b/packages/backend/src/services/chart/charts/federation.ts similarity index 80% rename from packages/backend/src/services/chart/charts/classes/federation.ts rename to packages/backend/src/services/chart/charts/federation.ts index 9f8b7ec0ae..8abb18b51f 100644 --- a/packages/backend/src/services/chart/charts/classes/federation.ts +++ b/packages/backend/src/services/chart/charts/federation.ts @@ -1,11 +1,15 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../../core'; +import Chart, { Obj, DeepPartial } from '../core'; import { SchemaType } from '@/misc/schema'; import { Instances } from '@/models/index'; -import { name, schema } from '../schemas/federation'; +import { name, schema } from './entities/federation'; type FederationLog = SchemaType; +/** + * フェデレーションに関するチャート + */ +// eslint-disable-next-line import/no-default-export export default class FederationChart extends Chart { constructor() { super(name, schema); @@ -45,7 +49,7 @@ export default class FederationChart extends Chart { } @autobind - public async update(isAdditional: boolean) { + public async update(isAdditional: boolean): Promise { const update: Obj = {}; update.total = isAdditional ? 1 : -1; diff --git a/packages/backend/src/services/chart/charts/classes/hashtag.ts b/packages/backend/src/services/chart/charts/hashtag.ts similarity index 81% rename from packages/backend/src/services/chart/charts/classes/hashtag.ts rename to packages/backend/src/services/chart/charts/hashtag.ts index 53774e566d..34e0614643 100644 --- a/packages/backend/src/services/chart/charts/classes/hashtag.ts +++ b/packages/backend/src/services/chart/charts/hashtag.ts @@ -1,12 +1,16 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../../core'; +import Chart, { Obj, DeepPartial } from '../core'; import { User } from '@/models/entities/user'; import { SchemaType } from '@/misc/schema'; import { Users } from '@/models/index'; -import { name, schema } from '../schemas/hashtag'; +import { name, schema } from './entities/hashtag'; type HashtagLog = SchemaType; +/** + * ハッシュタグに関するチャート + */ +// eslint-disable-next-line import/no-default-export export default class HashtagChart extends Chart { constructor() { super(name, schema, true); @@ -35,7 +39,7 @@ export default class HashtagChart extends Chart { } @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 { const update: Obj = { users: [user.id], }; diff --git a/packages/backend/src/services/chart/charts/classes/instance.ts b/packages/backend/src/services/chart/charts/instance.ts similarity index 89% rename from packages/backend/src/services/chart/charts/classes/instance.ts rename to packages/backend/src/services/chart/charts/instance.ts index 8e7902d163..7f3419b69c 100644 --- a/packages/backend/src/services/chart/charts/classes/instance.ts +++ b/packages/backend/src/services/chart/charts/instance.ts @@ -1,17 +1,21 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../../core'; +import Chart, { Obj, DeepPartial } from '../core'; import { SchemaType } from '@/misc/schema'; import { DriveFiles, Followings, Users, Notes } from '@/models/index'; import { DriveFile } from '@/models/entities/drive-file'; -import { name, schema } from '../schemas/instance'; import { Note } from '@/models/entities/note'; import { toPuny } from '@/misc/convert-host'; +import { name, schema } from './entities/instance'; type InstanceLog = SchemaType; +/** + * インスタンスごとのチャート + */ +// eslint-disable-next-line import/no-default-export export default class InstanceChart extends Chart { constructor() { - super(name, schema); + super(name, schema, true); } @autobind @@ -119,7 +123,7 @@ export default class InstanceChart extends Chart { } @autobind - public async requestReceived(host: string) { + public async requestReceived(host: string): Promise { await this.inc({ requests: { received: 1, @@ -128,7 +132,7 @@ export default class InstanceChart extends Chart { } @autobind - public async requestSent(host: string, isSucceeded: boolean) { + public async requestSent(host: string, isSucceeded: boolean): Promise { const update: Obj = {}; if (isSucceeded) { @@ -143,7 +147,7 @@ export default class InstanceChart extends Chart { } @autobind - public async newUser(host: string) { + public async newUser(host: string): Promise { await this.inc({ users: { total: 1, @@ -153,8 +157,8 @@ export default class InstanceChart extends Chart { } @autobind - public async updateNote(host: string, note: Note, isAdditional: boolean) { - const diffs = {} as any; + public async updateNote(host: string, note: Note, isAdditional: boolean): Promise { + const diffs = {} as Record; if (note.replyId != null) { diffs.reply = isAdditional ? 1 : -1; @@ -175,7 +179,7 @@ export default class InstanceChart extends Chart { } @autobind - public async updateFollowing(host: string, isAdditional: boolean) { + public async updateFollowing(host: string, isAdditional: boolean): Promise { await this.inc({ following: { total: isAdditional ? 1 : -1, @@ -186,7 +190,7 @@ export default class InstanceChart extends Chart { } @autobind - public async updateFollowers(host: string, isAdditional: boolean) { + public async updateFollowers(host: string, isAdditional: boolean): Promise { await this.inc({ followers: { total: isAdditional ? 1 : -1, @@ -197,7 +201,7 @@ export default class InstanceChart extends Chart { } @autobind - public async updateDrive(file: DriveFile, isAdditional: boolean) { + public async updateDrive(file: DriveFile, isAdditional: boolean): Promise { const update: Obj = {}; update.totalFiles = isAdditional ? 1 : -1; diff --git a/packages/backend/src/services/chart/charts/classes/network.ts b/packages/backend/src/services/chart/charts/network.ts similarity index 80% rename from packages/backend/src/services/chart/charts/classes/network.ts rename to packages/backend/src/services/chart/charts/network.ts index 838eec4916..73ea2f7e19 100644 --- a/packages/backend/src/services/chart/charts/classes/network.ts +++ b/packages/backend/src/services/chart/charts/network.ts @@ -1,10 +1,14 @@ import autobind from 'autobind-decorator'; -import Chart, { DeepPartial } from '../../core'; +import Chart, { DeepPartial } from '../core'; import { SchemaType } from '@/misc/schema'; -import { name, schema } from '../schemas/network'; +import { name, schema } from './entities/network'; type NetworkLog = SchemaType; +/** + * ネットワークに関するチャート + */ +// eslint-disable-next-line import/no-default-export export default class NetworkChart extends Chart { constructor() { super(name, schema); @@ -32,7 +36,7 @@ export default class NetworkChart extends Chart { } @autobind - public async update(incomingRequests: number, time: number, incomingBytes: number, outgoingBytes: number) { + public async update(incomingRequests: number, time: number, incomingBytes: number, outgoingBytes: number): Promise { const inc: DeepPartial = { incomingRequests: incomingRequests, totalTime: time, diff --git a/packages/backend/src/services/chart/charts/classes/notes.ts b/packages/backend/src/services/chart/charts/notes.ts similarity index 88% rename from packages/backend/src/services/chart/charts/classes/notes.ts rename to packages/backend/src/services/chart/charts/notes.ts index f8a580b3c0..86cda17225 100644 --- a/packages/backend/src/services/chart/charts/classes/notes.ts +++ b/packages/backend/src/services/chart/charts/notes.ts @@ -1,13 +1,17 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../../core'; +import Chart, { Obj, DeepPartial } from '../core'; import { SchemaType } from '@/misc/schema'; import { Notes } from '@/models/index'; import { Not, IsNull } from 'typeorm'; import { Note } from '@/models/entities/note'; -import { name, schema } from '../schemas/notes'; +import { name, schema } from './entities/notes'; type NotesLog = SchemaType; +/** + * ノートに関するチャート + */ +// eslint-disable-next-line import/no-default-export export default class NotesChart extends Chart { constructor() { super(name, schema); @@ -69,7 +73,7 @@ export default class NotesChart extends Chart { } @autobind - public async update(note: Note, isAdditional: boolean) { + public async update(note: Note, isAdditional: boolean): Promise { const update: Obj = { diffs: {}, }; diff --git a/packages/backend/src/services/chart/charts/classes/per-user-drive.ts b/packages/backend/src/services/chart/charts/per-user-drive.ts similarity index 83% rename from packages/backend/src/services/chart/charts/classes/per-user-drive.ts rename to packages/backend/src/services/chart/charts/per-user-drive.ts index 33d49d9ebb..fff790367f 100644 --- a/packages/backend/src/services/chart/charts/classes/per-user-drive.ts +++ b/packages/backend/src/services/chart/charts/per-user-drive.ts @@ -1,12 +1,16 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../../core'; +import Chart, { Obj, DeepPartial } from '../core'; import { SchemaType } from '@/misc/schema'; import { DriveFiles } from '@/models/index'; 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; +/** + * ユーザーごとのドライブに関するチャート + */ +// eslint-disable-next-line import/no-default-export export default class PerUserDriveChart extends Chart { constructor() { super(name, schema, true); @@ -46,7 +50,7 @@ export default class PerUserDriveChart extends Chart { } @autobind - public async update(file: DriveFile, isAdditional: boolean) { + public async update(file: DriveFile, isAdditional: boolean): Promise { const update: Obj = {}; update.totalCount = isAdditional ? 1 : -1; diff --git a/packages/backend/src/services/chart/charts/classes/per-user-following.ts b/packages/backend/src/services/chart/charts/per-user-following.ts similarity index 92% rename from packages/backend/src/services/chart/charts/classes/per-user-following.ts rename to packages/backend/src/services/chart/charts/per-user-following.ts index 67b623057d..d0a80abdaf 100644 --- a/packages/backend/src/services/chart/charts/classes/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/per-user-following.ts @@ -1,13 +1,17 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../../core'; +import Chart, { Obj, DeepPartial } from '../core'; import { SchemaType } from '@/misc/schema'; import { Followings, Users } from '@/models/index'; import { Not, IsNull } from 'typeorm'; 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; +/** + * ユーザーごとのフォローに関するチャート + */ +// eslint-disable-next-line import/no-default-export export default class PerUserFollowingChart extends Chart { constructor() { super(name, schema, true); @@ -100,7 +104,7 @@ export default class PerUserFollowingChart extends Chart { } @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 { const update: Obj = {}; update.total = isFollow ? 1 : -1; diff --git a/packages/backend/src/services/chart/charts/classes/per-user-notes.ts b/packages/backend/src/services/chart/charts/per-user-notes.ts similarity index 86% rename from packages/backend/src/services/chart/charts/classes/per-user-notes.ts rename to packages/backend/src/services/chart/charts/per-user-notes.ts index 94760c0492..d048c88885 100644 --- a/packages/backend/src/services/chart/charts/classes/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/per-user-notes.ts @@ -1,13 +1,17 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../../core'; +import Chart, { Obj, DeepPartial } from '../core'; import { User } from '@/models/entities/user'; import { SchemaType } from '@/misc/schema'; import { Notes } from '@/models/index'; 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; +/** + * ユーザーごとのノートに関するチャート + */ +// eslint-disable-next-line import/no-default-export export default class PerUserNotesChart extends Chart { constructor() { super(name, schema, true); @@ -46,7 +50,7 @@ export default class PerUserNotesChart extends Chart { } @autobind - public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean) { + public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean): Promise { const update: Obj = { diffs: {}, }; diff --git a/packages/backend/src/services/chart/charts/classes/per-user-reactions.ts b/packages/backend/src/services/chart/charts/per-user-reactions.ts similarity index 79% rename from packages/backend/src/services/chart/charts/classes/per-user-reactions.ts rename to packages/backend/src/services/chart/charts/per-user-reactions.ts index 62508eacdf..2f5353340d 100644 --- a/packages/backend/src/services/chart/charts/classes/per-user-reactions.ts +++ b/packages/backend/src/services/chart/charts/per-user-reactions.ts @@ -1,13 +1,17 @@ import autobind from 'autobind-decorator'; -import Chart, { DeepPartial } from '../../core'; +import Chart, { DeepPartial } from '../core'; import { User } from '@/models/entities/user'; import { Note } from '@/models/entities/note'; import { SchemaType } from '@/misc/schema'; import { Users } from '@/models/index'; -import { name, schema } from '../schemas/per-user-reactions'; +import { name, schema } from './entities/per-user-reactions'; type PerUserReactionsLog = SchemaType; +/** + * ユーザーごとのリアクションに関するチャート + */ +// eslint-disable-next-line import/no-default-export export default class PerUserReactionsChart extends Chart { constructor() { super(name, schema, true); @@ -36,7 +40,7 @@ export default class PerUserReactionsChart extends Chart { } @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 { this.inc({ [Users.isLocalUser(user) ? 'local' : 'remote']: { count: 1 }, }, note.userId); diff --git a/packages/backend/src/services/chart/charts/classes/test-grouped.ts b/packages/backend/src/services/chart/charts/test-grouped.ts similarity index 81% rename from packages/backend/src/services/chart/charts/classes/test-grouped.ts rename to packages/backend/src/services/chart/charts/test-grouped.ts index 2b11930af2..c851d2df01 100644 --- a/packages/backend/src/services/chart/charts/classes/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/test-grouped.ts @@ -1,10 +1,14 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../../core'; +import Chart, { Obj, DeepPartial } from '../core'; import { SchemaType } from '@/misc/schema'; -import { name, schema } from '../schemas/test-grouped'; +import { name, schema } from './entities/test-grouped'; type TestGroupedLog = SchemaType; +/** + * For testing + */ +// eslint-disable-next-line import/no-default-export export default class TestGroupedChart extends Chart { private total = {} as Record; @@ -42,7 +46,7 @@ export default class TestGroupedChart extends Chart { } @autobind - public async increment(group: string) { + public async increment(group: string): Promise { if (this.total[group] == null) this.total[group] = 0; const update: Obj = {}; diff --git a/packages/backend/src/services/chart/charts/classes/test-unique.ts b/packages/backend/src/services/chart/charts/test-unique.ts similarity index 73% rename from packages/backend/src/services/chart/charts/classes/test-unique.ts rename to packages/backend/src/services/chart/charts/test-unique.ts index f8655804d4..3564f675ad 100644 --- a/packages/backend/src/services/chart/charts/classes/test-unique.ts +++ b/packages/backend/src/services/chart/charts/test-unique.ts @@ -1,10 +1,14 @@ import autobind from 'autobind-decorator'; -import Chart, { DeepPartial } from '../../core'; +import Chart, { DeepPartial } from '../core'; import { SchemaType } from '@/misc/schema'; -import { name, schema } from '../schemas/test-unique'; +import { name, schema } from './entities/test-unique'; type TestUniqueLog = SchemaType; +/** + * For testing + */ +// eslint-disable-next-line import/no-default-export export default class TestUniqueChart extends Chart { constructor() { super(name, schema); @@ -28,7 +32,7 @@ export default class TestUniqueChart extends Chart { } @autobind - public async uniqueIncrement(key: string) { + public async uniqueIncrement(key: string): Promise { await this.inc({ foo: [key], }); diff --git a/packages/backend/src/services/chart/charts/classes/test.ts b/packages/backend/src/services/chart/charts/test.ts similarity index 80% rename from packages/backend/src/services/chart/charts/classes/test.ts rename to packages/backend/src/services/chart/charts/test.ts index b3f6b76a44..06add7ede9 100644 --- a/packages/backend/src/services/chart/charts/classes/test.ts +++ b/packages/backend/src/services/chart/charts/test.ts @@ -1,10 +1,14 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../../core'; +import Chart, { Obj, DeepPartial } from '../core'; import { SchemaType } from '@/misc/schema'; -import { name, schema } from '../schemas/test'; +import { name, schema } from './entities/test'; type TestLog = SchemaType; +/** + * For testing + */ +// eslint-disable-next-line import/no-default-export export default class TestChart extends Chart { public total = 0; // publicにするのはテストのため @@ -42,7 +46,7 @@ export default class TestChart extends Chart { } @autobind - public async increment() { + public async increment(): Promise { const update: Obj = {}; update.total = 1; @@ -55,7 +59,7 @@ export default class TestChart extends Chart { } @autobind - public async decrement() { + public async decrement(): Promise { const update: Obj = {}; update.total = -1; diff --git a/packages/backend/src/services/chart/charts/classes/users.ts b/packages/backend/src/services/chart/charts/users.ts similarity index 86% rename from packages/backend/src/services/chart/charts/classes/users.ts rename to packages/backend/src/services/chart/charts/users.ts index 6762410abf..c36c6cd979 100644 --- a/packages/backend/src/services/chart/charts/classes/users.ts +++ b/packages/backend/src/services/chart/charts/users.ts @@ -1,13 +1,17 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../../core'; +import Chart, { Obj, DeepPartial } from '../core'; import { SchemaType } from '@/misc/schema'; import { Users } from '@/models/index'; import { Not, IsNull } from 'typeorm'; import { User } from '@/models/entities/user'; -import { name, schema } from '../schemas/users'; +import { name, schema } from './entities/users'; type UsersLog = SchemaType; +/** + * ユーザー数に関するチャート + */ +// eslint-disable-next-line import/no-default-export export default class UsersChart extends Chart { constructor() { super(name, schema); @@ -59,7 +63,7 @@ export default class UsersChart extends Chart { } @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 { const update: Obj = {}; update.total = isAdditional ? 1 : -1; diff --git a/packages/backend/src/services/chart/core.ts b/packages/backend/src/services/chart/core.ts index 59149dbc08..f97fa521d3 100644 --- a/packages/backend/src/services/chart/core.ts +++ b/packages/backend/src/services/chart/core.ts @@ -30,7 +30,7 @@ type Log = { /** * 集計のグループ */ - group: string | null; + group?: string | null; /** * 集計日時のUnixタイムスタンプ(秒) @@ -38,7 +38,7 @@ type Log = { date: number; }; -const camelToSnake = (str: string) => { +const camelToSnake = (str: string): string => { 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> { private static readonly columnPrefix = '___'; private static readonly columnDot = '_'; @@ -57,7 +58,8 @@ export default abstract class Chart> { group: string | null; }[] = []; public schema: SimpleSchema; - protected repository: Repository; + protected repositoryForHour: Repository; + protected repositoryForDay: Repository; protected abstract genNewLog(latest: T): DeepPartial; @@ -181,9 +183,15 @@ export default abstract class Chart> { } @autobind - public static schemaToEntity(name: string, schema: SimpleSchema): EntitySchema { - return new EntitySchema({ - name: `__chart__${camelToSnake(name)}`, + public static schemaToEntity(name: string, schema: SimpleSchema, grouped = false): { + hour: EntitySchema, + 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: { id: { type: 'integer', @@ -193,37 +201,45 @@ export default abstract class Chart> { date: { type: 'integer', }, - group: { - type: 'varchar', - length: 128, - nullable: true, - }, + ...(grouped ? { + group: { + type: 'varchar', + length: 128, + }, + } : {}), ...Chart.convertSchemaToFlatColumnDefinitions(schema), }, indices: [{ - columns: ['date', 'group'], + columns: grouped ? ['date', 'group'] : ['date'], 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) { this.name = name; this.schema = schema; - const entity = Chart.schemaToEntity(name, schema); - const keys = ['date']; - if (grouped) keys.push('group'); - - entity.options.uniques = [{ - columns: keys, - }]; - - this.repository = getRepository(entity); + const { hour, day } = Chart.schemaToEntity(name, schema, grouped); + this.repositoryForHour = getRepository(hour); + this.repositoryForDay = getRepository(day); } @autobind @@ -247,24 +263,40 @@ export default abstract class Chart> { } @autobind - private getLatestLog(group: string | null = null): Promise { - return this.repository.findOne({ + private getLatestLog(group: string | null, span: 'hour' | 'day'): Promise { + const repository = + span === 'hour' ? this.repositoryForHour : + span === 'day' ? this.repositoryForDay : + new Error('not happen') as never; + + return repository.findOne(group ? { group: group, - }, { + } : {}, { order: { date: -1, }, }).then(x => x || null); } + /** + * 現在(=今のHour or Day)のログをデータベースから探して、あればそれを返し、なければ作成して返します。 + */ @autobind - private async getCurrentLog(group: string | null = null): Promise { + private async claimCurrentLog(group: string | null, span: 'hour' | 'day'): Promise { 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 currentLog = await this.repository.findOne({ + const repository = + 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), ...(group ? { group: group } : {}), }); @@ -283,7 +315,7 @@ export default abstract class Chart> { // * 昨日何もチャートを更新するような出来事がなかった場合は、 // * ログがそもそも作られずドキュメントが存在しないということがあり得るため、 // * 「昨日の」と決め打ちせずに「もっとも最近の」とします - const latest = await this.getLatestLog(group); + const latest = await this.getLatestLog(group, span); if (latest != null) { const obj = Chart.convertFlattenColumnsToObject(latest) as T; @@ -297,16 +329,16 @@ export default abstract class Chart> { // 初期ログデータを作成 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 lockKey = `${this.name}:${date}:${group}`; + const lockKey = group ? `${this.name}:${date}:${span}:${group}` : `${this.name}:${date}:${span}`; const unlock = await getChartInsertLock(lockKey); try { // ロック内でもう1回チェックする - const currentLog = await this.repository.findOne({ + const currentLog = await repository.findOne({ date: date, ...(group ? { group: group } : {}), }); @@ -315,13 +347,13 @@ export default abstract class Chart> { if (currentLog != null) return currentLog; // 新規ログ挿入 - log = await this.repository.insert({ - group: group, + log = await repository.insert({ date: date, + ...(group ? { group: group } : {}), ...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; } finally { @@ -349,10 +381,10 @@ export default abstract class Chart> { // そのログは本来は 01:00~ のログとしてDBに保存されて欲しいのに、02:00~ のログ扱いになってしまう。 // これを回避するための実装は複雑になりそうなため、一旦保留。 - const update = async (log: Log) => { + const update = async (logHour: Log, logDay: Log): Promise => { const finalDiffs = {} as Record; - 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); for (const [k, v] of Object.entries(columns)) { @@ -371,36 +403,60 @@ export default abstract class Chart> { const query = Chart.convertQuery(finalDiffs); // ログ更新 - await this.repository.createQueryBuilder() - .update() - .set(query) - .where('id = :id', { id: log.id }) - .execute(); + await Promise.all([ + this.repositoryForHour.createQueryBuilder() + .update() + .set(query) + .where('id = :id', { id: logHour.id }) + .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に入ったものは消さないようにする - 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)); - 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 - public async resync(group: string | null = null): Promise { + public async resync(group: string | null = null): Promise { const data = await this.fetchActual(group); - const update = async (log: Log) => { - await this.repository.createQueryBuilder() - .update() - .set(Chart.convertObjectToFlattenColumns(data)) - .where('id = :id', { id: log.id }) - .execute(); + const update = async (logHour: Log, logDay: Log): Promise => { + await Promise.all([ + this.repositoryForHour.createQueryBuilder() + .update() + .set(Chart.convertObjectToFlattenColumns(data)) + .where('id = :id', { id: logHour.id }) + .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 @@ -418,13 +474,18 @@ export default abstract class Chart> { const gt = 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') : - 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: { - group: group, date: Between(Chart.dateToTimestamp(gt), Chart.dateToTimestamp(lt)), + ...(group ? { group: group } : {}), }, order: { date: -1, @@ -435,9 +496,9 @@ export default abstract class Chart> { if (logs.length === 0) { // もっとも新しいログを持ってくる // (すくなくともひとつログが無いと隙間埋めできないため) - const recentLog = await this.repository.findOne({ + const recentLog = await repository.findOne(group ? { group: group, - }, { + } : {}, { order: { date: -1, }, @@ -451,9 +512,9 @@ export default abstract class Chart> { } else if (!isTimeSame(new Date(logs[logs.length - 1].date * 1000), gt)) { // 要求された範囲の最も古い箇所時点での最も新しいログを持ってきて末尾に追加する // (隙間埋めできないため) - const outdatedLog = await this.repository.findOne({ - group: group, + const outdatedLog = await repository.findOne({ date: LessThan(Chart.dateToTimestamp(gt)), + ...(group ? { group: group } : {}), }, { order: { date: -1, @@ -467,60 +528,26 @@ export default abstract class Chart> { const chart: T[] = []; - if (span === 'hour') { - for (let i = (amount - 1); i >= 0; i--) { - const current = subtractTime(dateUTC([y, m, d, h]), i, 'hour'); + for (let i = (amount - 1); i >= 0; i--) { + 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)); - if (log) { - const data = Chart.convertFlattenColumnsToObject(log); - chart.unshift(Chart.countUniqueFields(data) as T); - } else { - // 隙間埋め - const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); - const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null; - 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)); - - 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); + if (log) { + const data = Chart.convertFlattenColumnsToObject(log); + chart.unshift(Chart.countUniqueFields(data) as T); + } else { + // 隙間埋め + const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); + const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null; + chart.unshift(Chart.countUniqueFields(this.getNewLog(data)) as T); } } - const res: ArrayValue = {} as any; + const res = {} as Record; /** * [{ foo: 1, bar: 5 }, { foo: 2, bar: 6 }, { foo: 3, bar: 7 }] @@ -528,7 +555,7 @@ export default abstract class Chart> { * { 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)) { const p = path ? `${path}.${k}` : k; if (typeof v === 'object' && !Array.isArray(v)) { @@ -542,7 +569,7 @@ export default abstract class Chart> { compact(chart[0]); - return res; + return res as ArrayValue; } } diff --git a/packages/backend/src/services/chart/entities.ts b/packages/backend/src/services/chart/entities.ts index 9d96a8a7ee..dedbd47080 100644 --- a/packages/backend/src/services/chart/entities.ts +++ b/packages/backend/src/services/chart/entities.ts @@ -1,15 +1,27 @@ -import { fileURLToPath } from 'url'; -import { dirname } from 'path'; -import Chart from './core'; +import { entity as FederationChart } from './charts/entities/federation'; +import { entity as NotesChart } from './charts/entities/notes'; +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); -const _filename = __filename; -const _dirname = dirname(_filename); - -export const entities = Object.values(require('require-all')({ - dirname: _dirname + '/charts/schemas', - filter: /^.+\.[jt]s$/, - resolve: (x: any) => { - return Chart.schemaToEntity(x.name, x.schema); - }, -})); +export const entities = [ + FederationChart.hour, FederationChart.day, + NotesChart.hour, NotesChart.day, + UsersChart.hour, UsersChart.day, + NetworkChart.hour, NetworkChart.day, + ActiveUsersChart.hour, ActiveUsersChart.day, + InstanceChart.hour, InstanceChart.day, + PerUserNotesChart.hour, PerUserNotesChart.day, + DriveChart.hour, DriveChart.day, + PerUserReactionsChart.hour, PerUserReactionsChart.day, + HashtagChart.hour, HashtagChart.day, + PerUserFollowingChart.hour, PerUserFollowingChart.day, + PerUserDriveChart.hour, PerUserDriveChart.day, +]; diff --git a/packages/backend/src/services/chart/index.ts b/packages/backend/src/services/chart/index.ts index 61eb431ea3..0b9887b36f 100644 --- a/packages/backend/src/services/chart/index.ts +++ b/packages/backend/src/services/chart/index.ts @@ -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 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 notesChart = new NotesChart(); export const usersChart = new UsersChart(); diff --git a/packages/backend/test/chart.ts b/packages/backend/test/chart.ts index 935ac9d8e1..66000bc928 100644 --- a/packages/backend/test/chart.ts +++ b/packages/backend/test/chart.ts @@ -3,13 +3,12 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as lolex from '@sinonjs/fake-timers'; import { async, initTestDb } from './utils'; -import TestChart from '../src/services/chart/charts/classes/test'; -import TestGroupedChart from '../src/services/chart/charts/classes/test-grouped'; -import TestUniqueChart from '../src/services/chart/charts/classes/test-unique'; -import * as _TestChart from '../src/services/chart/charts/schemas/test'; -import * as _TestGroupedChart from '../src/services/chart/charts/schemas/test-grouped'; -import * as _TestUniqueChart from '../src/services/chart/charts/schemas/test-unique'; -import Chart from '../src/services/chart/core'; +import TestChart from '../src/services/chart/charts/test'; +import TestGroupedChart from '../src/services/chart/charts/test-grouped'; +import TestUniqueChart from '../src/services/chart/charts/test-unique'; +import * as _TestChart from '../src/services/chart/charts/entities/test'; +import * as _TestGroupedChart from '../src/services/chart/charts/entities/test-grouped'; +import * as _TestUniqueChart from '../src/services/chart/charts/entities/test-unique'; describe('Chart', () => { let testChart: TestChart; @@ -19,9 +18,9 @@ describe('Chart', () => { beforeEach(async(async () => { await initTestDb(false, [ - Chart.schemaToEntity(_TestChart.name, _TestChart.schema), - Chart.schemaToEntity(_TestGroupedChart.name, _TestGroupedChart.schema), - Chart.schemaToEntity(_TestUniqueChart.name, _TestUniqueChart.schema) + _TestChart.entity.hour, _TestChart.entity.day, + _TestGroupedChart.entity.hour, _TestGroupedChart.entity.day, + _TestUniqueChart.entity.hour, _TestUniqueChart.entity.day, ]); 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 () => { await testChart.increment(); await testChart.save();