Skip to content

Commit 06a65e6

Browse files
committed
feat: add search by person
1 parent 6185f85 commit 06a65e6

22 files changed

Lines changed: 267 additions & 2 deletions

src/backend/bigquery/bigquery.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ describe('[Function] bigQuery', () => {
4141
name: 'name',
4242
locale: 'locale',
4343
taxon: 'taxon',
44+
person: 'person',
4445
organisation: 'organisation',
4546
link: 'link',
4647
}
@@ -59,6 +60,7 @@ describe('[Function] bigQuery', () => {
5960
name: 'name',
6061
locale: 'locale',
6162
taxon: 'taxon',
63+
person: 'person',
6264
organisation: 'organisation',
6365
link: 'link',
6466
},
@@ -75,6 +77,9 @@ describe('[Function] sendInitQuery', () => {
7577
.mockResolvedValueOnce([
7678
[{ name: 'taxon1' }, { name: 'taxon2' }, { name: 'taxon3' }],
7779
])
80+
.mockResolvedValueOnce([
81+
[{ name: 'person1' }, { name: 'person2' }, { name: 'person3' }],
82+
])
7883
.mockResolvedValueOnce([
7984
[{ title: 'org1' }, { title: 'org2' }, { title: 'org3' }],
8085
])
@@ -108,6 +113,14 @@ describe('[Function] sendInitQuery', () => {
108113
location: 'europe-west2',
109114
params: {},
110115
})
116+
expect(BigQuery.prototype.query).toHaveBeenNthCalledWith(2, {
117+
query: `
118+
SELECT DISTINCT title
119+
FROM \`search.person\`
120+
`,
121+
location: 'europe-west2',
122+
params: {},
123+
})
111124
expect(BigQuery.prototype.query).toHaveBeenNthCalledWith(3, {
112125
query: `
113126
SELECT DISTINCT title
@@ -136,6 +149,7 @@ describe('[Function] sendInitQuery', () => {
136149
expect(result).toEqual({
137150
locales: ['', 'en', 'cy', 'fr', 'de'],
138151
taxons: ['taxon1', 'taxon2', 'taxon3'],
152+
people: ['person1', 'person2', 'person3'],
139153
organisations: ['org1', 'org2', 'org3'],
140154
documentTypes: ['dt1', 'dt2', 'dt3'],
141155
governments: ['gov1', 'gov2', 'gov3'],
@@ -150,6 +164,7 @@ describe('[Function] sendSearchQuery', () => {
150164
selectedWords: 'keyword1 keyword2',
151165
excludedWords: 'excluded1 excluded2',
152166
taxon: 'taxon',
167+
person: 'person',
153168
publishingOrganisation: 'organisation',
154169
language: 'en',
155170
documentType: '',
@@ -184,6 +199,7 @@ describe('[Function] sendSearchQuery', () => {
184199
locale: 'en',
185200
organisation: 'organisation',
186201
taxon: 'taxon',
202+
person: 'person',
187203
},
188204
})
189205
})
@@ -210,6 +226,29 @@ describe('[Function] sendSearchQuery', () => {
210226
},
211227
})
212228
})
229+
it('Calls the appropriate queries with Person search type', async () => {
230+
jest.spyOn(buildSqlQuery, 'buildSqlQuery').mockReturnValue('query')
231+
;(BigQuery.prototype.query as jest.Mock)
232+
.mockResolvedValueOnce(['Some result'])
233+
.mockResolvedValueOnce(['Some result'])
234+
await sendSearchQuery(makeSearchParams({ searchType: SearchType.Person }))
235+
expect(BigQuery.prototype.query).toHaveBeenCalledTimes(1)
236+
expect(BigQuery.prototype.query).toHaveBeenNthCalledWith(1, {
237+
query: 'query',
238+
location: 'europe-west2',
239+
params: {
240+
excluded_keyword0: 'excluded1',
241+
excluded_keyword1: 'excluded2',
242+
politicalStatus: 'any',
243+
keyword0: 'keyword1',
244+
keyword1: 'keyword2',
245+
link: 'link',
246+
locale: 'en',
247+
organisation: 'organisation',
248+
person: 'person',
249+
},
250+
})
251+
})
213252
it('Calls the appropriate queries with Organisation search type', async () => {
214253
jest.spyOn(buildSqlQuery, 'buildSqlQuery').mockReturnValue('query')
215254
;(BigQuery.prototype.query as jest.Mock)
@@ -232,6 +271,7 @@ describe('[Function] sendSearchQuery', () => {
232271
locale: 'en',
233272
organisation: 'organisation',
234273
taxon: 'taxon',
274+
person: 'person',
235275
},
236276
})
237277
})

src/backend/bigquery/bigquery.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ export const bigQuery = async function (userQuery: string, options?: any) {
4343
if (options.taxon) {
4444
params.taxon = options.taxon
4545
}
46+
if (options.person) {
47+
params.person = options.person
48+
}
4649
if (options.organisation) {
4750
params.organisation = options.organisation
4851
}
@@ -79,11 +82,12 @@ export const bigQuery = async function (userQuery: string, options?: any) {
7982
const sendInitQuery = async function (): Promise<InitResults> {
8083
let bqLocales: any,
8184
bqTaxons: any,
85+
bqPeople: any,
8286
bqOrganisations: any,
8387
bqDocumentTypes: any,
8488
bqGovernments: any
8589
try {
86-
;[bqLocales, bqTaxons, bqOrganisations, bqDocumentTypes, bqGovernments] =
90+
;[bqLocales, bqTaxons, bqPeople, bqOrganisations, bqDocumentTypes, bqGovernments] =
8791
await Promise.all([
8892
bigQuery(`
8993
SELECT DISTINCT locale
@@ -95,6 +99,10 @@ const sendInitQuery = async function (): Promise<InitResults> {
9599
`),
96100
bigQuery(`
97101
SELECT DISTINCT title
102+
FROM \`search.person\`
103+
`),
104+
bigQuery(`
105+
SELECT DISTINCT title
98106
FROM \`search.organisation\`
99107
`),
100108
bigQuery(`
@@ -116,6 +124,7 @@ const sendInitQuery = async function (): Promise<InitResults> {
116124
.filter((locale: string) => locale !== 'en' && locale !== 'cy')
117125
),
118126
taxons: bqTaxons.map((taxon: any) => taxon.name),
127+
people: bqPeople.map((person: any) => person.title),
119128
organisations: bqOrganisations.map(
120129
(organisation: any) => organisation.title
121130
),
@@ -135,6 +144,7 @@ const sendSearchQuery = async function (
135144
const query = buildSqlQuery(searchParams, keywords, excludedKeywords)
136145
const locale = languageCode(searchParams.language)
137146
const taxon = searchParams.taxon
147+
const person = searchParams.person
138148
const organisation = searchParams.publishingOrganisation
139149
const documentType = searchParams.documentType
140150
const link = searchParams.linkSearchUrl
@@ -147,6 +157,7 @@ const sendSearchQuery = async function (
147157
excludedKeywords,
148158
locale,
149159
taxon,
160+
person,
150161
organisation,
151162
link,
152163
phoneNumber,

src/backend/bigquery/buildSqlQuery.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const prefix = (keywords?: string[], link?: string) => `
2424
withdrawn_explanation,
2525
page_views,
2626
taxons,
27+
people,
2728
primary_organisation,
2829
organisations AS all_organisations,
2930
government,
@@ -76,6 +77,7 @@ const makeParams = (opts = {}) =>
7677
selectedWords: '',
7778
excludedWords: '',
7879
taxon: '',
80+
person: '',
7981
publishingOrganisation: '',
8082
language: '',
8183
documentType: '',
@@ -230,6 +232,26 @@ describe('buildSqlQuery', () => {
230232
expect(queryFmt(query)).toEqual(queryFmt(expected))
231233
})
232234

235+
it('can filter by any person', () => {
236+
const searchParams: SearchParams = makeParams({
237+
person: 'whoever',
238+
})
239+
const keywords: string[] = []
240+
const excludedKeywords: string[] = []
241+
242+
const query = buildSqlQuery(searchParams, keywords, excludedKeywords)
243+
const expectedClauses = `
244+
AND EXISTS
245+
(
246+
SELECT 1 FROM UNNEST (people) AS person
247+
WHERE person = @person
248+
)
249+
`
250+
const expected = expectedQuery(expectedClauses, keywords)
251+
252+
expect(queryFmt(query)).toEqual(queryFmt(expected))
253+
})
254+
233255
it('can filter by any organisation', () => {
234256
const searchParams: SearchParams = makeParams({
235257
publishingOrganisation: 'whatever',
@@ -250,6 +272,26 @@ describe('buildSqlQuery', () => {
250272
expect(queryFmt(query)).toEqual(queryFmt(expected))
251273
})
252274

275+
it('can filter by any person', () => {
276+
const searchParams: SearchParams = makeParams({
277+
person: 'whoever',
278+
})
279+
const keywords: string[] = []
280+
const excludedKeywords: string[] = []
281+
282+
const query = buildSqlQuery(searchParams, keywords, excludedKeywords)
283+
const expectedClauses = `
284+
AND EXISTS
285+
(
286+
SELECT 1 FROM UNNEST (people) AS link
287+
WHERE link = @person
288+
)
289+
`
290+
const expected = expectedQuery(expectedClauses, keywords)
291+
292+
expect(queryFmt(query)).toEqual(queryFmt(expected))
293+
})
294+
253295
it('can filter by any hyperlink', () => {
254296
const link = 'whatever'
255297
const searchParams: SearchParams = makeParams({
@@ -279,6 +321,7 @@ describe('buildSqlQuery', () => {
279321
politicalStatus: 'political',
280322
language: 'whatever',
281323
taxon: 'whatever',
324+
person: 'whoever',
282325
publishingOrganisation: 'whatever',
283326
linkSearchUrl: link,
284327
government: '2015 Conservative government',
@@ -298,6 +341,11 @@ describe('buildSqlQuery', () => {
298341
SELECT 1 FROM UNNEST (taxons) AS taxon
299342
WHERE taxon = @taxon
300343
)
344+
AND EXISTS
345+
(
346+
SELECT 1 FROM UNNEST (people) AS person
347+
WHERE person = @person
348+
)
301349
AND EXISTS
302350
(
303351
SELECT 1 FROM UNNEST (organisations) AS link

src/backend/bigquery/buildSqlQuery.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,17 @@ export const buildSqlQuery = function (
136136
`
137137
}
138138

139+
let personClause = ''
140+
if (searchParams.person !== '') {
141+
personClause = `
142+
AND EXISTS
143+
(
144+
SELECT 1 FROM UNNEST (people) AS person
145+
WHERE person = @person
146+
)
147+
`
148+
}
149+
139150
let organisationClause = ''
140151
if (searchParams.publishingOrganisation !== '') {
141152
organisationClause = `
@@ -206,6 +217,7 @@ export const buildSqlQuery = function (
206217
withdrawn_explanation,
207218
page_views,
208219
taxons,
220+
people,
209221
primary_organisation,
210222
organisations AS all_organisations,
211223
government,
@@ -219,6 +231,7 @@ export const buildSqlQuery = function (
219231
${publishingAppClause}
220232
${localeClause}
221233
${taxonClause}
234+
${personClause}
222235
${organisationClause}
223236
${linkClause}
224237
${phoneNumberClause}

src/backend/constants/routes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export enum Route {
1111
getInitData = '/get-init-data',
1212
searchApi = '/search',
1313
searchTaxon = '/taxon',
14+
searchPerson = '/person',
1415
downloadCSV = '/csv',
1516
login = '/login',
1617
loginCallback = '/auth/gds/callback',

src/backend/utils/csv.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ const csvFieldFormatters: Record<string, any> = {
3838
name: 'Topic tags',
3939
format: formatNames,
4040
},
41+
people: {
42+
name: 'People',
43+
format: identity,
44+
},
4145
primary_organisation: {
4246
name: 'Primary publishing organisation',
4347
format: identity,

src/backend/utils/getParams.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ describe('getParams', () => {
1414
'selected-words': 'some selected-words',
1515
'excluded-words': 'some excluded-words',
1616
taxon: 'some taxon',
17+
person: 'some person',
1718
'publishing-organisation': 'some publishing-organisation',
1819
language: 'some language',
1920
'case-sensitive': 'true',
@@ -35,6 +36,7 @@ describe('getParams', () => {
3536
selectedWords: 'some selected-words',
3637
excludedWords: 'some excluded-words',
3738
taxon: 'some taxon',
39+
person: 'some person',
3840
publishingOrganisation: 'some publishing-organisation',
3941
caseSensitive: true,
4042
documentType: 'some document-type',
@@ -56,6 +58,7 @@ describe('getParams', () => {
5658
'selected-words': '',
5759
'excluded-words': '',
5860
taxon: '',
61+
person: '',
5962
'publishing-organisation': '',
6063
language: '',
6164
'case-sensitive': 'true',
@@ -82,6 +85,7 @@ describe('getParams', () => {
8285
publishingOrganisation: '',
8386
publishingStatus: '',
8487
taxon: '',
88+
person: '',
8589
selectedWords: '',
8690
keywordLocation: 'all',
8791
documentType: '',

src/backend/utils/getParams.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const getParams = (req: express.Request): SearchParams => {
2121
const excludedWords =
2222
sanitiseInput(req.query[UrlParams.ExcludedWords] as string) || ''
2323
const taxon = sanitiseInput(req.query[UrlParams.Taxon] as string) || ''
24+
const person = sanitiseInput(req.query[UrlParams.Person] as string) || ''
2425
const publishingOrganisation =
2526
sanitiseInput(req.query[UrlParams.PublishingOrganisation] as string) || ''
2627
const language = sanitiseInput(req.query[UrlParams.Language] as string) || ''
@@ -60,6 +61,7 @@ export const getParams = (req: express.Request): SearchParams => {
6061
selectedWords,
6162
excludedWords,
6263
taxon,
64+
person,
6365
publishingOrganisation,
6466
language,
6567
documentType,

src/common/types/search-api-types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export enum UrlParams {
44
SelectedWords = 'selected-words',
55
ExcludedWords = 'excluded-words',
66
Taxon = 'taxon',
7+
Person = 'person',
78
PublishingOrganisation = 'publishing-organisation',
89
CaseSensitive = 'case-sensitive',
910
DocumentType = 'document-type',
@@ -23,6 +24,7 @@ export enum SearchType {
2324
PhoneNumber = 'phone-number',
2425
Organisation = 'organisation',
2526
Taxon = 'taxon',
27+
Person = 'person',
2628
Language = 'language',
2729
Advanced = 'advanced',
2830
Results = 'results',
@@ -64,6 +66,7 @@ export type SearchParams = {
6466
selectedWords: string // list of words to search
6567
excludedWords: string // list of words to exclude
6668
taxon: string // taxon to search in
69+
person: string // person whose pages to search
6770
publishingOrganisation: string // organisation to search in
6871
language: string // the language to search for
6972
documentType: string // documentTypeto search in
@@ -83,6 +86,7 @@ export type SearchResults = unknown[]
8386

8487
export type InitResults = {
8588
taxons: string[]
89+
people: string[]
8690
locales: string[]
8791
organisations: string[]
8892
documentTypes: string[]

0 commit comments

Comments
 (0)