Skip to content

Commit 7fb6ad4

Browse files
committed
update slugging and check for existing companies in script
1 parent 1da7330 commit 7fb6ad4

File tree

4 files changed

+98
-11
lines changed

4 files changed

+98
-11
lines changed

.infra/Pulumi.prod.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,5 @@ config:
196196
secure: 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
197197
gcp:project: devkit-prod
198198
gcp:region: us-central1
199+
api:anthropicApiKey:
200+
secure: AAABAHEzcSWbWl8xVDhOLgPCopvQnihNX6MIzyG/JbaYeGA3p1qrJ3UEQhOvBg7kMoIbx9u+0CRC102IldakBAlxx0l9l9kCDSE/JqqfCfjiLT5mjdLISRE5q2dQz+MaqqVzqm0MzaIQQkWBmOxeJAxRxQ/+/dWdla8RMWKG+Q7PnrH8vS8PeNKASRQ=

__tests__/schema/autocompletes.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,4 +1251,45 @@ describe('query autocompleteCompany', () => {
12511251
},
12521252
]);
12531253
});
1254+
1255+
it('should return results when searching by non-Latin characters (Korean)', async () => {
1256+
const res = await client.query(QUERY, {
1257+
variables: { query: '삼성' },
1258+
});
1259+
1260+
expect(res.errors).toBeFalsy();
1261+
expect(res.data.autocompleteCompany).toMatchObject([
1262+
{
1263+
id: 'samsung',
1264+
name: 'Samsung Electronics',
1265+
image: 'https://example.com/samsung.png',
1266+
},
1267+
]);
1268+
});
1269+
1270+
it('should return results when searching by non-Latin characters (Japanese)', async () => {
1271+
const res = await client.query(QUERY, {
1272+
variables: { query: 'トヨタ' },
1273+
});
1274+
1275+
expect(res.errors).toBeFalsy();
1276+
expect(res.data.autocompleteCompany).toMatchObject([
1277+
{
1278+
id: 'toyota',
1279+
name: 'Toyota Motor Corporation',
1280+
image: 'https://example.com/toyota.png',
1281+
},
1282+
]);
1283+
});
1284+
1285+
it('should not return unrelated companies when searching with non-Latin characters', async () => {
1286+
const res = await client.query(QUERY, {
1287+
variables: { query: '삼성' },
1288+
});
1289+
1290+
expect(res.errors).toBeFalsy();
1291+
// Should only return Samsung, not other companies with non-Latin altNames
1292+
expect(res.data.autocompleteCompany.length).toBe(1);
1293+
expect(res.data.autocompleteCompany[0].id).toBe('samsung');
1294+
});
12541295
});

bin/enrichExistingCompanies.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import '../src/config';
33
import createOrGetConnection from '../src/db';
44
import { UserExperience } from '../src/entity/user/experiences/UserExperience';
55
import { UserExperienceType } from '../src/entity/user/experiences/types';
6+
import { Company } from '../src/entity/Company';
67
import {
78
enrichCompanyForExperience,
89
EnrichmentLogger,
910
} from '../src/common/companyEnrichment';
11+
import { DataSource } from 'typeorm';
1012

1113
const QUEUE_CONCURRENCY = 1;
1214

@@ -16,6 +18,20 @@ interface ExperienceData {
1618
type: string;
1719
}
1820

21+
async function findExactCompanyMatch(
22+
con: DataSource,
23+
customCompanyName: string,
24+
): Promise<Company | null> {
25+
return con
26+
.getRepository(Company)
27+
.createQueryBuilder('company')
28+
.where('LOWER(company.name) = LOWER(:name)', { name: customCompanyName })
29+
.orWhere('LOWER(company.altName) = LOWER(:name)', {
30+
name: customCompanyName,
31+
})
32+
.getOne();
33+
}
34+
1935
const silentLogger: EnrichmentLogger = {
2036
info: () => {},
2137
warn: () => {},
@@ -47,6 +63,7 @@ const silentLogger: EnrichmentLogger = {
4763
);
4864

4965
let enriched = 0;
66+
let exactMatches = 0;
5067
let processedCount = 0;
5168

5269
const builder = con
@@ -67,6 +84,23 @@ const silentLogger: EnrichmentLogger = {
6784
const stream = await builder.stream();
6885

6986
const enrichQueue = fastq.promise(async (experience: ExperienceData) => {
87+
// First check for exact name match to avoid API calls
88+
const exactMatch = await findExactCompanyMatch(
89+
con,
90+
experience.customCompanyName,
91+
);
92+
93+
if (exactMatch) {
94+
await con
95+
.getRepository(UserExperience)
96+
.update({ id: experience.id }, { companyId: exactMatch.id });
97+
98+
processedCount++;
99+
exactMatches++;
100+
return;
101+
}
102+
103+
// No exact match, use enrichment API
70104
const result = await enrichCompanyForExperience(
71105
con,
72106
{
@@ -95,7 +129,7 @@ const silentLogger: EnrichmentLogger = {
95129
await enrichQueue.drained();
96130

97131
console.log(
98-
`Completed. Processed ${processedCount} experiences, enriched ${enriched} (offset ${offset} to ${offset + processedCount - 1}).`,
132+
`Completed. Processed ${processedCount} experiences: ${exactMatches} exact matches, ${enriched} enriched (offset ${offset} to ${offset + processedCount - 1}).`,
99133
);
100134
console.log(
101135
`Next batch command: npx ts-node bin/enrichExistingCompanies.ts ${limit} ${offset + limit}`,

src/schema/autocompletes.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Keyword, KeywordStatus } from '../entity';
22
import { AutocompleteType, Autocomplete } from '../entity/Autocomplete';
33
import { traceResolvers } from './trace';
4-
import { ILike, Raw } from 'typeorm';
4+
import { FindOptionsWhere, ILike, Raw } from 'typeorm';
55
import { AuthContext, BaseContext } from '../Context';
66
import { textToSlug, toGQLEnum, type GQLCompany } from '../common';
77
import { queryReadReplica } from '../common/queryReadReplica';
@@ -161,20 +161,30 @@ export const resolvers = traceResolvers<unknown, BaseContext>({
161161
ctx: AuthContext,
162162
): Promise<GQLCompany[]> => {
163163
const { type, query, limit } = autocompleteCompanySchema.parse(payload);
164-
const slugQuery = Raw((alias) => `slugify(${alias}) = :slug`, {
165-
slug: textToSlug(query),
166-
});
164+
const slug = textToSlug(query);
165+
166+
const whereConditions: FindOptionsWhere<Company>[] = [
167+
{ type, name: ILike(`%${query}%`) },
168+
{ type, altName: ILike(`%${query}%`) },
169+
];
170+
171+
// Only add slug-based search if the slug is non-empty
172+
// (non-Latin characters like Korean get stripped by slugify, causing false matches)
173+
if (slug) {
174+
const slugQuery = Raw((alias) => `slugify(${alias}) = :slug`, {
175+
slug,
176+
});
177+
whereConditions.unshift(
178+
{ type, name: slugQuery },
179+
{ type, altName: slugQuery },
180+
);
181+
}
167182

168183
return await queryReadReplica(ctx.con, ({ queryRunner }) =>
169184
queryRunner.manager.getRepository(Company).find({
170185
take: limit,
171186
order: { name: 'ASC' },
172-
where: [
173-
{ type, name: slugQuery },
174-
{ type, name: ILike(`%${query}%`) },
175-
{ type, altName: slugQuery },
176-
{ type, altName: ILike(`%${query}%`) },
177-
],
187+
where: whereConditions,
178188
}),
179189
);
180190
},

0 commit comments

Comments
 (0)