Skip to content

Commit

Permalink
feat: ✨ personal general 에 dailyActivities 추가
Browse files Browse the repository at this point in the history
- 기존과는 다른 구조로 제작하였습니다. 앞으로 db 정리하고 이런 방식으로 고치면 어떨까 하는 생각입니다.
- daily_logtimes collection 을 추가했습니다.
- scale_teams, events_users 로 인해 성능 저하가 심각한 수준이면 별도 collection 으로 분리할 예정입니다. 현재 자체 테스트에선 dev 기준 100 ~ 300ms 정도 나옵니다.

- close #388
- close #389
  • Loading branch information
jpham005 committed Nov 9, 2023
1 parent 5910567 commit 1b79fee
Show file tree
Hide file tree
Showing 18 changed files with 505 additions and 2 deletions.
18 changes: 18 additions & 0 deletions app/src/api/dailyLogtime/db/dailyLogtime.database.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import type { HydratedDocument } from 'mongoose';

export type DailyLogtimeDocument = HydratedDocument<daily_logtimes>;

@Schema({ collection: 'daily_logtimes' })
export class daily_logtimes {
@Prop({ required: true })
userId: number;

@Prop({ required: true })
date: Date;

@Prop({ required: true })
value: number;
}

export const dailyLogtimeSchema = SchemaFactory.createForClass(daily_logtimes);
11 changes: 11 additions & 0 deletions app/src/api/event/db/event.database.aggregate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {
lookupStage,
type CollectionLookup,
} from 'src/database/mongoose/database.mongoose.aggregation';
import { events } from './event.database.schema';

export const lookupEvents: CollectionLookup = (
localField,
foreignField,
pipeline,
) => lookupStage(events.name, localField, foreignField, pipeline);
27 changes: 27 additions & 0 deletions app/src/api/event/db/event.database.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import type { HydratedDocument } from 'mongoose';

export type eventDocument = HydratedDocument<events>;

@Schema({ collection: 'events' })
export class events {
@Prop({ required: true })
id: number;

@Prop({ required: true })
beginAt: Date;

@Prop({ required: true })
endAt: Date;

@Prop({ required: true })
name: string;

@Prop({ required: true })
description: string;

@Prop({ required: true })
location: string;
}

export const eventSchema = SchemaFactory.createForClass(events);
11 changes: 11 additions & 0 deletions app/src/api/eventsUser/db/eventsUser.database.aggregate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {
lookupStage,
type CollectionLookup,
} from 'src/database/mongoose/database.mongoose.aggregation';
import { events_users } from './eventsUser.database.schema';

export const lookupEventsUsers: CollectionLookup = (
localField,
foreignField,
pipeline,
) => lookupStage(events_users.name, localField, foreignField, pipeline);
13 changes: 13 additions & 0 deletions app/src/api/eventsUser/db/eventsUser.database.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Prop, Schema } from '@nestjs/mongoose';

@Schema({ collection: 'events_users' })
export class events_users {
@Prop({ required: true })
id: number;

@Prop({ required: true })
eventId: number;

@Prop({ required: true })
userId: number;
}
2 changes: 1 addition & 1 deletion app/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import { TeamInfoModule } from './page/teamInfo/teamInfo.module';
CacheModule.register({
isGlobal: true,
store: new ShallowStore({
max: 100000,
max: 10000000,
ttl: DateWrapper.MIN * 3,
}),
}),
Expand Down
118 changes: 118 additions & 0 deletions app/src/dailyActivity/dailyActivity.dao.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import type { Model } from 'mongoose';
import { daily_logtimes } from 'src/api/dailyLogtime/db/dailyLogtime.database.schema';
import { lookupEvents } from 'src/api/event/db/event.database.aggregate';
import { events } from 'src/api/event/db/event.database.schema';
import { events_users } from 'src/api/eventsUser/db/eventsUser.database.schema';
import { scale_team } from 'src/api/scaleTeam/db/scaleTeam.database.schema';
import {
DailyActivityType,
type DailyDefaultRecord,
type DailyLogtimeRecord,
type FindDailyActivityRecordInput,
type FindDailyActivityRecordOutput,
} from './dailyActivity.dto';

export type DailyActivityDao = {
findAllRecordByDate: (
args: FindDailyActivityRecordInput,
) => Promise<FindDailyActivityRecordOutput[]>;
};

@Injectable()
export class DailyActivityDaoImpl implements DailyActivityDao {
constructor(
@InjectModel(scale_team.name)
private readonly scaleTeamModel: Model<scale_team>,
) {}

async findAllRecordByDate({
userId,
start,
end,
}: FindDailyActivityRecordInput): Promise<FindDailyActivityRecordOutput[]> {
return await this.scaleTeamModel
.aggregate<DailyLogtimeRecord | DailyDefaultRecord>()
.match({
$or: [{ 'corrector.id': userId }, { 'correcteds.id': userId }],
filledAt: {
$gte: start,
$lt: end,
},
})
.sort({ filledAt: 1 })
.project({
_id: 0,
id: 1,
at: '$filledAt',
type: {
$cond: {
if: { $eq: ['$corrector.id', userId] },
then: DailyActivityType.CORRECTOR,
else: DailyActivityType.CORRECTED,
},
},
})
.unionWith({
coll: daily_logtimes.name,
pipeline: [
{
$match: {
userId,
date: {
$gte: start,
$lt: end,
},
},
},
{
$project: {
_id: 0,
date: '$date',
type: { $literal: DailyActivityType.LOGTIME },
value: 1,
},
},
],
})
.unionWith({
coll: events_users.name,
pipeline: [
{
$match: {
userId,
},
},
lookupEvents('eventId', 'id', [
{
$match: {
date: {
$gte: start,
$lt: end,
},
},
},
]),
{
$project: {
_id: 0,
id: 1,
at: { $first: `$${events.name}.endAt` },
type: { $literal: DailyActivityType.EVENT },
},
},
{
$match: {
at: { $ne: null },
},
},
{
$sort: {
at: 1,
},
},
],
});
}
}
39 changes: 39 additions & 0 deletions app/src/dailyActivity/dailyActivity.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export enum DailyActivityType {
CORRECTOR,
CORRECTED,
EVENT,
LOGTIME,
}

export type DailyLogtimeRecord = {
type: DailyActivityType.LOGTIME;
value: number;
date: Date;
};

export type DailyDefaultRecord = {
type: Exclude<DailyActivityType, DailyActivityType.LOGTIME>;
id: number;
at: Date;
};

export type FindDailyActivityRecordInput = {
userId: number;
start: Date;
end: Date;
};

export type FindDailyActivityRecordOutput =
| DailyLogtimeRecord
| DailyDefaultRecord;

export type FindUserDailyActivityByDateInput = {
userId: number;
start: Date;
end: Date;
};

export type FindUserDailyActivityByDateOutput = {
date: Date;
records: (Omit<DailyLogtimeRecord, 'date'> | DailyDefaultRecord)[];
};
20 changes: 20 additions & 0 deletions app/src/dailyActivity/dailyActivity.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import {
ScaleTeamSchema,
scale_team,
} from 'src/api/scaleTeam/db/scaleTeam.database.schema';
import { DailyActivityDaoImpl } from './dailyActivity.dao';
import { DailyActivityService } from './dailyActivity.service';

@Module({
imports: [
MongooseModule.forFeature([
{ name: scale_team.name, schema: ScaleTeamSchema },
]),
],
providers: [DailyActivityDaoImpl, DailyActivityService],
exports: [DailyActivityService],
})
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class DailyActivityModule {}
60 changes: 60 additions & 0 deletions app/src/dailyActivity/dailyActivity.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Injectable } from '@nestjs/common';
import { DateWrapper } from 'src/dateWrapper/dateWrapper';
import { DailyActivityDaoImpl } from './dailyActivity.dao';
import {
DailyActivityType,
type DailyDefaultRecord,
type DailyLogtimeRecord,
type FindUserDailyActivityByDateInput,
type FindUserDailyActivityByDateOutput,
} from './dailyActivity.dto';

@Injectable()
export class DailyActivityService {
constructor(private readonly dailyActivityDao: DailyActivityDaoImpl) {}

async findAllUserDailyActivityByDate(
args: FindUserDailyActivityByDateInput,
): Promise<FindUserDailyActivityByDateOutput[]> {
const records = await this.dailyActivityDao.findAllRecordByDate(args);

const recordMapByDate = records.reduce((recordMap, record) => {
if (isDailyLogtimeRecord(record)) {
const timestamp = record.date.getTime();
const prevRecords = recordMap.get(timestamp) ?? [];

recordMap.set(timestamp, [
...prevRecords,
{ type: record.type, value: record.value },
]);

return recordMap;
}

const timestamp = new DateWrapper(record.at)
.startOfDate()
.toDate()
.getTime();

const prevRecords = recordMap.get(timestamp) ?? [];

recordMap.set(timestamp, [
...prevRecords,
{ type: record.type, id: record.id, at: record.at },
]);

return recordMap;
}, new Map<number, FindUserDailyActivityByDateOutput['records']>());

return Array.from(recordMapByDate.entries())
.sort(([a], [b]) => a - b)
.map(([timestamp, records]) => ({
date: new Date(timestamp),
records,
}));
}
}

const isDailyLogtimeRecord = (
record: DailyLogtimeRecord | DailyDefaultRecord,
): record is DailyLogtimeRecord => record.type === DailyActivityType.LOGTIME;
21 changes: 21 additions & 0 deletions app/src/database/dailyLogtime/dailyLogtime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument, ObjectId } from 'mongoose';

export type DailyLogtimeDocument = HydratedDocument<daily_logtimes>;

@Schema()
export class daily_logtimes {
@Prop({ required: true })
_id: ObjectId;

@Prop({ required: true })
userId: number;

@Prop({ required: true })
date: Date;

@Prop({ required: true })
value: number;
}

export const dailyLogtimeSchema = SchemaFactory.createForClass(daily_logtimes);
19 changes: 18 additions & 1 deletion app/src/dateRange/dateRange.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { DateWrapper } from 'src/dateWrapper/dateWrapper';
import { DateRangeArgs, DateTemplate } from './dtos/dateRange.dto';
import { DateRange, DateRangeArgs, DateTemplate } from './dtos/dateRange.dto';
import type { IDateRangedType } from './models/dateRange.model';

@Injectable()
Expand Down Expand Up @@ -55,4 +55,21 @@ export class DateRangeService {
$lt: end,
};
}

getAbsoluteDateRangeByYear(year: number): DateRange {
const start = DateWrapper.createByYear(year).startOfYear().toDate();
const end = DateWrapper.createByYear(year)
.startOfYear()
.moveYear(1)
.toDate();

return { start, end };
}

getRelativeDateRange(): DateRange {
const start = new DateWrapper().startOfDate().moveDate(-364).toDate();
const end = new DateWrapper().startOfDate().moveDate(1).toDate();

return { start, end };
}
}
6 changes: 6 additions & 0 deletions app/src/dateWrapper/dateWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,10 @@ export class DateWrapper {
static currWeek = (): DateWrapper => new DateWrapper().startOfWeek();
static lastWeek = (): DateWrapper => DateWrapper.currWeek().moveWeek(-1);
static nextWeek = (): DateWrapper => DateWrapper.currWeek().moveWeek(1);

static currYear = (): DateWrapper => new DateWrapper().startOfYear();

static createByYear = (year: number): DateWrapper => {
return new DateWrapper(`${year - 1}-12-31T15:00:00.000Z`);
};
}
Loading

0 comments on commit 1b79fee

Please sign in to comment.