[questions][chore] update to support location tb

pull/457/head
hpkoh 3 years ago
parent 373e249014
commit 0cfb063c7c

@ -0,0 +1,39 @@
/*
Warnings:
- You are about to drop the column `location` on the `QuestionsQuestionEncounter` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "QuestionsQuestionEncounter" DROP COLUMN "location",
ADD COLUMN "cityId" TEXT,
ADD COLUMN "countryId" TEXT,
ADD COLUMN "stateId" TEXT,
ALTER COLUMN "companyId" DROP NOT NULL;
-- CreateIndex
CREATE INDEX "QuestionsAnswer_updatedAt_id_idx" ON "QuestionsAnswer"("updatedAt", "id");
-- CreateIndex
CREATE INDEX "QuestionsAnswer_upvotes_id_idx" ON "QuestionsAnswer"("upvotes", "id");
-- CreateIndex
CREATE INDEX "QuestionsAnswerComment_updatedAt_id_idx" ON "QuestionsAnswerComment"("updatedAt", "id");
-- CreateIndex
CREATE INDEX "QuestionsAnswerComment_upvotes_id_idx" ON "QuestionsAnswerComment"("upvotes", "id");
-- CreateIndex
CREATE INDEX "QuestionsQuestionComment_updatedAt_id_idx" ON "QuestionsQuestionComment"("updatedAt", "id");
-- CreateIndex
CREATE INDEX "QuestionsQuestionComment_upvotes_id_idx" ON "QuestionsQuestionComment"("upvotes", "id");
-- AddForeignKey
ALTER TABLE "QuestionsQuestionEncounter" ADD CONSTRAINT "QuestionsQuestionEncounter_countryId_fkey" FOREIGN KEY ("countryId") REFERENCES "Country"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "QuestionsQuestionEncounter" ADD CONSTRAINT "QuestionsQuestionEncounter_stateId_fkey" FOREIGN KEY ("stateId") REFERENCES "State"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "QuestionsQuestionEncounter" ADD CONSTRAINT "QuestionsQuestionEncounter_cityId_fkey" FOREIGN KEY ("cityId") REFERENCES "City"("id") ON DELETE SET NULL ON UPDATE CASCADE;

@ -107,27 +107,30 @@ model Company {
} }
model Country { model Country {
id String @id id String @id
name String @unique name String @unique
code String @unique code String @unique
states State[] states State[]
questionsQuestionEncounters QuestionsQuestionEncounter[]
} }
model State { model State {
id String @id id String @id
name String name String
countryId String countryId String
cities City[] cities City[]
country Country @relation(fields: [countryId], references: [id]) country Country @relation(fields: [countryId], references: [id])
questionsQuestionEncounters QuestionsQuestionEncounter[]
@@unique([name, countryId]) @@unique([name, countryId])
} }
model City { model City {
id String @id id String @id
name String name String
stateId String stateId String
state State @relation(fields: [stateId], references: [id]) state State @relation(fields: [stateId], references: [id])
questionsQuestionEncounters QuestionsQuestionEncounter[]
@@unique([name, stateId]) @@unique([name, stateId])
} }
@ -435,12 +438,12 @@ model QuestionsQuestion {
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
user User? @relation(fields: [userId], references: [id], onDelete: SetNull) user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
encounters QuestionsQuestionEncounter[] encounters QuestionsQuestionEncounter[]
votes QuestionsQuestionVote[] votes QuestionsQuestionVote[]
comments QuestionsQuestionComment[] comments QuestionsQuestionComment[]
answers QuestionsAnswer[] answers QuestionsAnswer[]
QuestionsListQuestionEntry QuestionsListQuestionEntry[] questionsListQuestionEntries QuestionsListQuestionEntry[]
@@index([lastSeenAt, id]) @@index([lastSeenAt, id])
@@index([upvotes, id]) @@index([upvotes, id])
@ -450,14 +453,18 @@ model QuestionsQuestionEncounter {
id String @id @default(cuid()) id String @id @default(cuid())
questionId String questionId String
userId String? userId String?
// TODO: sync with models (location, role) companyId String?
companyId String countryId String?
location String @db.Text stateId String?
cityId String?
role String @db.Text role String @db.Text
seenAt DateTime seenAt DateTime
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
country Country? @relation(fields: [countryId], references: [id], onDelete: SetNull)
state State? @relation(fields: [stateId], references: [id], onDelete: SetNull)
city City? @relation(fields: [cityId], references: [id], onDelete: SetNull)
company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull) company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull)
user User? @relation(fields: [userId], references: [id], onDelete: SetNull) user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
question QuestionsQuestion @relation(fields: [questionId], references: [id], onDelete: Cascade) question QuestionsQuestion @relation(fields: [questionId], references: [id], onDelete: Cascade)

@ -2,7 +2,7 @@ import { z } from 'zod';
import { createRouter } from '../context'; import { createRouter } from '../context';
import type { AggregatedQuestionEncounter } from '~/types/questions'; import { createAggregatedQuestionEncounter } from '~/utils/questions/server/aggregate-encounters';
export const questionsQuestionEncounterRouter = createRouter().query( export const questionsQuestionEncounterRouter = createRouter().query(
'getAggregatedEncounters', 'getAggregatedEncounters',
@ -21,41 +21,7 @@ export const questionsQuestionEncounterRouter = createRouter().query(
}, },
}); });
const companyCounts: Record<string, number> = {}; return createAggregatedQuestionEncounter(questionEncountersData);
const locationCounts: Record<string, number> = {};
const roleCounts: Record<string, number> = {};
let latestSeenAt = questionEncountersData[0].seenAt;
for (let i = 0; i < questionEncountersData.length; i++) {
const encounter = questionEncountersData[i];
latestSeenAt =
latestSeenAt < encounter.seenAt ? encounter.seenAt : latestSeenAt;
if (!(encounter.company!.name in companyCounts)) {
companyCounts[encounter.company!.name] = 1;
}
companyCounts[encounter.company!.name] += 1;
if (!(encounter.location in locationCounts)) {
locationCounts[encounter.location] = 1;
}
locationCounts[encounter.location] += 1;
if (!(encounter.role in roleCounts)) {
roleCounts[encounter.role] = 1;
}
roleCounts[encounter.role] += 1;
}
const questionEncounter: AggregatedQuestionEncounter = {
companyCounts,
latestSeenAt,
locationCounts,
roleCounts,
};
return questionEncounter;
}, },
}, },
); );

@ -1,7 +1,6 @@
import { z } from 'zod'; import { z } from 'zod';
import { TRPCError } from '@trpc/server'; import { TRPCError } from '@trpc/server';
import { createAggregatedQuestionEncounter } from '~/utils/questions/server/aggregate-encounters';
import { createProtectedRouter } from '../context'; import { createProtectedRouter } from '../context';

@ -1,5 +1,6 @@
import { z } from 'zod'; import { z } from 'zod';
import { QuestionsQuestionType } from '@prisma/client'; import { QuestionsQuestionType } from '@prisma/client';
import { JobTitleLabels } from '~/components/shared/JobTitles';
import { TRPCError } from '@trpc/server'; import { TRPCError } from '@trpc/server';
import { createQuestionWithAggregateData } from '~/utils/questions/server/aggregate-encounters'; import { createQuestionWithAggregateData } from '~/utils/questions/server/aggregate-encounters';
@ -11,13 +12,16 @@ import { SortOrder, SortType } from '~/types/questions.d';
export const questionsQuestionRouter = createRouter() export const questionsQuestionRouter = createRouter()
.query('getQuestionsByFilter', { .query('getQuestionsByFilter', {
input: z.object({ input: z.object({
companyNames: z.string().array(), companyIds: z.string().array(),
cursor: z.string().nullish(), cursor: z.string().nullish(),
endDate: z.date().default(new Date()), endDate: z.date().default(new Date()),
limit: z.number().min(1).default(50), limit: z.number().min(1).default(50),
countryIds: z.string().array(),
cityIds: z.string().array(),
stateIds: z.string().array(),
locations: z.string().array(), locations: z.string().array(),
questionTypes: z.nativeEnum(QuestionsQuestionType).array(), questionTypes: z.nativeEnum(QuestionsQuestionType).array(),
roles: z.string().array(), roles: z.nativeEnum(JobTitleLabels).array(),
sortOrder: z.nativeEnum(SortOrder), sortOrder: z.nativeEnum(SortOrder),
sortType: z.nativeEnum(SortType), sortType: z.nativeEnum(SortType),
startDate: z.date().optional(), startDate: z.date().optional(),
@ -56,7 +60,9 @@ export const questionsQuestionRouter = createRouter()
encounters: { encounters: {
select: { select: {
company: true, company: true,
location: true, country: true,
city: true,
state: true,
role: true, role: true,
seenAt: true, seenAt: true,
}, },
@ -84,11 +90,11 @@ export const questionsQuestionRouter = createRouter()
gte: input.startDate, gte: input.startDate,
lte: input.endDate, lte: input.endDate,
}, },
...(input.companyNames.length > 0 ...(input.companyIds.length > 0
? { ? {
company: { company: {
name: { id: {
in: input.companyNames, in: input.companyIds,
}, },
}, },
} }
@ -149,7 +155,9 @@ export const questionsQuestionRouter = createRouter()
encounters: { encounters: {
select: { select: {
company: true, company: true,
location: true, country: true,
city: true,
state: true,
role: true, role: true,
seenAt: true, seenAt: true,
}, },

@ -14,10 +14,20 @@ export type Question = {
user: string; user: string;
}; };
export type StateInfo = {
total: number;
cityCounts: Record<string, number>;
};
export type CountryInfo = {
total: number;
stateInfos: Record<string, StateInfo>;
};
export type AggregatedQuestionEncounter = { export type AggregatedQuestionEncounter = {
companyCounts: Record<string, number>; companyCounts: Record<string, number>;
latestSeenAt: Date; latestSeenAt: Date;
locationCounts: Record<string, number>; countryCounts: Record<string, CountryInfo>;
roleCounts: Record<string, number>; roleCounts: Record<string, number>;
}; };

@ -1,15 +1,20 @@
import type { import type {
City,
State,
Country,
Company, Company,
QuestionsQuestion, QuestionsQuestion,
QuestionsQuestionVote, QuestionsQuestionVote,
} from '@prisma/client'; } from '@prisma/client';
import { Vote } from '@prisma/client'; import { Vote } from '@prisma/client';
import type { AggregatedQuestionEncounter, Question } from '~/types/questions'; import type { AggregatedQuestionEncounter, Question, CountryInfo} from '~/types/questions';
type AggregatableEncounters = Array<{ type AggregatableEncounters = Array<{
company: Company | null; company: Company | null;
location: string; city: City | null;
country: Country | null;
state: State | null;
role: string; role: string;
seenAt: Date; seenAt: Date;
}>; }>;
@ -67,8 +72,8 @@ export function createQuestionWithAggregateData(
export function createAggregatedQuestionEncounter( export function createAggregatedQuestionEncounter(
encounters: AggregatableEncounters, encounters: AggregatableEncounters,
): AggregatedQuestionEncounter { ): AggregatedQuestionEncounter {
const countryCounts: Record<string, CountryInfo> = {};
const companyCounts: Record<string, number> = {}; const companyCounts: Record<string, number> = {};
const locationCounts: Record<string, number> = {};
const roleCounts: Record<string, number> = {}; const roleCounts: Record<string, number> = {};
let latestSeenAt = encounters[0].seenAt; let latestSeenAt = encounters[0].seenAt;
@ -77,15 +82,50 @@ export function createAggregatedQuestionEncounter(
latestSeenAt = latestSeenAt =
latestSeenAt < encounter.seenAt ? encounter.seenAt : latestSeenAt; latestSeenAt < encounter.seenAt ? encounter.seenAt : latestSeenAt;
if (!(encounter.company!.name in companyCounts)) { if (encounter.company !== null) {
companyCounts[encounter.company!.name] = 0; if (!(encounter.company.name in companyCounts)) {
companyCounts[encounter.company!.name] = 0;
}
companyCounts[encounter.company!.name] += 1;
} }
companyCounts[encounter.company!.name] += 1;
if (!(encounter.location in locationCounts)) {
locationCounts[encounter.location] = 0; if (encounter.country !== null) {
if (!(encounter.country.name in countryCounts)) {
countryCounts[encounter.country.name] = {
total: 0,
stateInfos: {},
};
}
const countryInfo = countryCounts[encounter.country.name];
countryInfo.total += 1;
const countryStateInfo = countryInfo.stateInfos;
if (encounter.state !== null) {
if (!(encounter.state.name in countryStateInfo)) {
countryStateInfo[encounter.state.name] = {
total: 0,
cityCounts: {},
};
}
const stateInfo = countryStateInfo[encounter.state.name];
stateInfo.total += 1;
const cityCounts = stateInfo.cityCounts;
if (encounter.city !== null) {
if (!(encounter.city.name in cityCounts)) {
cityCounts[encounter.city.name] = 0;
}
cityCounts[encounter.city.name] += 1;
}
}
} }
locationCounts[encounter.location] += 1;
if (!(encounter.role in roleCounts)) { if (!(encounter.role in roleCounts)) {
roleCounts[encounter.role] = 0; roleCounts[encounter.role] = 0;
@ -96,7 +136,7 @@ export function createAggregatedQuestionEncounter(
return { return {
companyCounts, companyCounts,
latestSeenAt, latestSeenAt,
locationCounts, countryCounts,
roleCounts, roleCounts,
}; };
} }

Loading…
Cancel
Save