Skip to content

Commit

Permalink
Use specific implementations for find and get
Browse files Browse the repository at this point in the history
This is rather than having the general function for both
  • Loading branch information
neilbmclaughlin committed Feb 6, 2025
1 parent 02e8076 commit d19d9e5
Showing 1 changed file with 124 additions and 121 deletions.
245 changes: 124 additions & 121 deletions server/services/lib/bing-results-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,144 +3,147 @@ const { addBufferToBbox, formatName, slugify } = require('../../util')
// source: https://en.wikipedia.org/wiki/Ceremonial_counties_of_England
// see also for a description of the difference between ceremonial and administrative counties
const englishCeremonialCounties =
[
'bedfordshire',
'berkshire',
'bristol',
'buckinghamshire',
'cambridgeshire',
'cheshire',
'city of london',
'cornwall',
'cumbria',
'derbyshire',
'devon',
'dorset',
'durham',
'east riding of yorkshire',
'east sussex',
'essex',
'gloucestershire',
'greater london',
'greater manchester',
'hampshire',
'herefordshire',
'hertfordshire',
'isle of wight',
'kent',
'lancashire',
'leicestershire',
'lincolnshire',
'merseyside',
'norfolk',
'north yorkshire',
'northamptonshire',
'northumberland',
'nottinghamshire',
'oxfordshire',
'rutland',
'shropshire',
'somerset',
'south yorkshire',
'staffordshire',
'suffolk',
'surrey',
'tyne and wear',
'warwickshire',
'west midlands',
'west sussex',
'west yorkshire',
'wiltshire',
'worcestershire'
]

const passThroughFilter = () => true

async function bingResultsParser (bingData, { preFilter = passThroughFilter, postFilter = passThroughFilter }) {
const set = bingData.resourceSets[0]
if (set.estimatedTotal === 0) {
return []
}

// note that allowedTypes also captures precedance rules for when multiple
// results are returned (e.g admindivision2 takes precedance over admindivision1)
const allowedTypes = [
'postcode1',
'postcode3',
'admindivision1',
'admindivision2',
'populatedplace',
'neighborhood'
[
'bedfordshire',
'berkshire',
'bristol',
'buckinghamshire',
'cambridgeshire',
'cheshire',
'city of london',
'cornwall',
'cumbria',
'derbyshire',
'devon',
'dorset',
'durham',
'east riding of yorkshire',
'east sussex',
'essex',
'gloucestershire',
'greater london',
'greater manchester',
'hampshire',
'herefordshire',
'hertfordshire',
'isle of wight',
'kent',
'lancashire',
'leicestershire',
'lincolnshire',
'merseyside',
'norfolk',
'north yorkshire',
'northamptonshire',
'northumberland',
'nottinghamshire',
'oxfordshire',
'rutland',
'shropshire',
'somerset',
'south yorkshire',
'staffordshire',
'suffolk',
'surrey',
'tyne and wear',
'warwickshire',
'west midlands',
'west sussex',
'west yorkshire',
'wiltshire',
'worcestershire'
]

const englandOnlyFilter = r => {
if (r.entityType.toLowerCase() === 'admindivision1') {
return englishCeremonialCounties.indexOf(r.name.toLowerCase()) >= 0
}

return r.address.adminDistrict?.toLowerCase() === 'england'
}
// note that allowedTypes also captures precedance rules for when multiple
// results are returned (e.g admindivision2 takes precedance over admindivision1)
const allowedTypes = [
'postcode1',
'postcode3',
'admindivision1',
'admindivision2',
'populatedplace',
'neighborhood'
]

const distanceInMetres = {
'2k': 2000,
'10k': 10000
}

const distanceInMetres = {
'2k': 2000,
'10k': 10000
const mapper = (r) => {
const name = formatName(r.name)
const bbox = r.bbox.reverse()
// query is the value to use in a search box or the slug to replicate the
// search and get the same result. If the bing format of the name (place
// name + postcode) is used then some postcode searches which were
// successful will subsequently fail to find a postcode with the same name
// e.g TQ9 6JZ => Dartington, Totnes TQ9 6JZ => returns the postcode but
// with a different name (Totnes, TQ9 6JZ, United Kingdom)
// This causes problems with validity checking
// Retained both name and query for display purposes for post codes
// (even though name and query are the are the same for non-postcodes)
const query = ['postcode1', 'postcode3'].includes(r.entityType.toLowerCase())
? r.address.postalCode
: name

return {
name,
query,
slug: slugify(query),
center: r.point.coordinates.reverse(),
bbox2k: addBufferToBbox(bbox, distanceInMetres['2k']),
bbox10k: addBufferToBbox(bbox, distanceInMetres['10k']),
isUK: r.address.countryRegionIso2 === 'GB',
isEngland: { is_england: true }
}
}

const mapper = r => {
const name = formatName(r.name)
const bbox = r.bbox.reverse()
// query is the value to use in a search box or the slug to replicate the
// search and get the same result. If the bing format of the name (place
// name + postcode) is used then some postcode searches which were
// successful will subsequently fail to find a postcode with the same name
// e.g TQ9 6JZ => Dartington, Totnes TQ9 6JZ => returns the postcode but
// with a different name (Totnes, TQ9 6JZ, United Kingdom)
// This causes problems with validity checking
// Retained both name and query for display purposes for post codes
// (even though name and query are the are the same for non-postcodes)
const query = ['postcode1', 'postcode3'].includes(r.entityType.toLowerCase())
? r.address.postalCode
: name
const confidenceFilter = (r) => r.confidence.toLowerCase() === 'high'

return {
name,
query,
slug: slugify(query),
center: r.point.coordinates.reverse(),
bbox2k: addBufferToBbox(bbox, distanceInMetres['2k']),
bbox10k: addBufferToBbox(bbox, distanceInMetres['10k']),
isUK: r.address.countryRegionIso2 === 'GB',
isEngland: { is_england: true }
}
const englandOnlyFilter = (r) => {
if (r.entityType.toLowerCase() === 'admindivision1') {
return englishCeremonialCounties.indexOf(r.name.toLowerCase()) >= 0
}

const allowedTypesFilter = r => allowedTypes.includes(r.entityType.toLowerCase())
return r.address.adminDistrict?.toLowerCase() === 'england'
}

const typesSort = (a, b) =>
allowedTypes.indexOf(a.entityType.toLowerCase()) - allowedTypes.indexOf(b.entityType.toLowerCase())
const allowedTypesFilter = (r) =>
allowedTypes.includes(r.entityType.toLowerCase())

const removeDuplicatesFilter = (place, index, self) => self.findIndex(p => p.slug === place.slug) === index
const baseFilter = (r) =>
allowedTypesFilter(r) && englandOnlyFilter(r)

const data = set.resources
.filter(preFilter)
.filter(allowedTypesFilter)
.filter(englandOnlyFilter)
.sort(typesSort)
.map(mapper)
.filter(removeDuplicatesFilter)
.filter(postFilter)
const typesSort = (a, b) =>
allowedTypes.indexOf(a.entityType.toLowerCase()) -
allowedTypes.indexOf(b.entityType.toLowerCase())

return data
}
const removeDuplicatesFilter = (place, index, self) =>
self.findIndex(p => p.slug === place.slug) === index

async function find (bingResponse) {
const preFilter = (r) => r.confidence.toLowerCase() === 'high'
return bingResultsParser(bingResponse, { preFilter })
const set = bingResponse.resourceSets[0]
return set.estimatedTotal
? set.resources
.filter(confidenceFilter)
.filter(baseFilter)
.sort(typesSort)
.map(mapper)
.filter(removeDuplicatesFilter)
: []
}

async function get (bingResponse, slug) {
const postFilter = (r) => r.slug === slug
return bingResultsParser(bingResponse, { postFilter })
const matchingSlugFilter = (r) => r.slug === slug
const set = bingResponse.resourceSets[0]
return set.estimatedTotal
? set.resources
.filter(baseFilter)
.sort(typesSort)
.map(mapper)
.filter(removeDuplicatesFilter)
.filter(matchingSlugFilter)
: []
}

module.exports = {
Expand Down

0 comments on commit d19d9e5

Please sign in to comment.