Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds index option in geocodeOptions #2

Merged
merged 10 commits into from
Sep 24, 2024
19 changes: 16 additions & 3 deletions api/csv/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable camelcase */
import test from 'ava'

import {computeResultFilename, extractGeocodeOptions, ensureArray} from '../index.js'
import {computeResultFilename, extractGeocodeOptions, ensureArray, extractIndexes} from '../index.js'

test('computeResultFilename', t => {
t.is(computeResultFilename('input.csv'), 'input-geocoded.csv')
Expand Down Expand Up @@ -33,7 +33,8 @@ test('extractGeocodeOptions / geocode options', t => {
type: 'address',
lon: 'longitude',
lat: 'latitude',
resultColumns: ['result_col1', 'result_col2']
resultColumns: ['result_col1', 'result_col2'],
indexes: ['address']
}

const actual = extractGeocodeOptions(req)
Expand All @@ -60,7 +61,8 @@ test('extractGeocodeOptions / default columns', t => {
}

const expected = {
columns: ['col1', 'col2', 'col3']
columns: ['col1', 'col2', 'col3'],
indexes: ['address']
}

const actual = extractGeocodeOptions(req)
Expand All @@ -73,3 +75,14 @@ test('ensureArray', t => {
t.deepEqual(ensureArray(null), [])
t.deepEqual(ensureArray(undefined), [])
})

test('extractIndexes', t => {
t.deepEqual(extractIndexes('address'), ['address'])
t.deepEqual(extractIndexes(['address']), ['address'])
t.deepEqual(extractIndexes([]), ['address'])
t.deepEqual(extractIndexes(undefined), ['address'])
t.deepEqual(extractIndexes(['address', 'poi']), ['address', 'poi'])
t.throws(() => extractIndexes('unknown'), {message: 'Unsupported index type: unknown'})
t.throws(() => extractIndexes(['address', 'unknown']), {message: 'Unsupported index type: unknown'})
t.deepEqual(extractIndexes(['address', 'poi', 'address']), ['address', 'poi'])
})
9 changes: 6 additions & 3 deletions api/csv/__tests__/stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ test('createGeocodeStream / search', async t => {
const operation = 'search'
const geocodeOptions = {
columns: ['column1'],
resultColumns: ['result_status', 'result_error', 'result_result1']
resultColumns: ['result_status', 'result_error', 'result_result1'],
indexes: ['address']
}
const resultsById = {
1: {status: 'ok', result: {result1: 'test'}}
Expand All @@ -120,7 +121,8 @@ test('createGeocodeStream / skipped', async t => {
const operation = 'search'
const geocodeOptions = {
columns: ['column1'],
resultColumns: ['result_status', 'result_error', 'result_result1']
resultColumns: ['result_status', 'result_error', 'result_result1'],
indexes: ['address']
}
const resultsById = {
1: {status: 'ok', result: {result1: 'test'}}
Expand All @@ -136,7 +138,8 @@ test('createGeocodeStream / batch error', async t => {
const operation = 'search'
const geocodeOptions = {
columns: ['column1'],
resultColumns: ['result_status', 'result_error', 'result_result1', 'result_error']
resultColumns: ['result_status', 'result_error', 'result_result1', 'result_error'],
indexes: ['address']
}

const results = await executeInBatch(items, operation, geocodeOptions, new Error('Boom'))
Expand Down
24 changes: 24 additions & 0 deletions api/csv/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import iconv from 'iconv-lite'
import {createCsvReadStream} from '@livingdata/tabular-data-helpers'

import logger from '../../lib/logger.js'
import {GEOCODE_INDEXES} from '../../lib/config.js'

import batch from '../operations/batch.js'

Expand Down Expand Up @@ -69,6 +70,27 @@ export function computeResultFilename(originalFilename) {
return `${basename}-geocoded${extension}`
}

export function extractIndexes(indexesValue) {
if (!indexesValue) {
return ['address']
}

indexesValue = ensureArray(indexesValue)

if (indexesValue.length === 0) {
return ['address']
}

const invalidValue = indexesValue.find(index => !GEOCODE_INDEXES.includes(index))

if (invalidValue) {
throw createHttpError(400, 'Unsupported index type: ' + invalidValue)
}

// Remove duplicates
return [...new Set(indexesValue)]
}

export function extractGeocodeOptions(req) {
const geocodeOptions = {}

Expand Down Expand Up @@ -106,6 +128,8 @@ export function extractGeocodeOptions(req) {
geocodeOptions.resultColumns = ensureArray(req.body.result_columns)
}

geocodeOptions.indexes = extractIndexes(req.body.indexes)

return geocodeOptions
}

Expand Down
3 changes: 2 additions & 1 deletion api/csv/stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ export function createGeocodeStream(geocodeOptions, {operation, indexes, signal,
lon: geocodeOptions.lon
}))

const emptyResultItem = createEmptyResultItem(['address'], operation)
const emptyResultItem = createEmptyResultItem(geocodeOptions.indexes, operation)

try {
const batchResults = await batch({
indexes: geocodeOptions.indexes,
requests: preparedRequests.filter(Boolean) // Remove null values
}, {indexes, signal})

Expand Down
48 changes: 48 additions & 0 deletions api/operations/__tests__/batch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import test from 'ava'

import {mergeResults} from '../batch.js'

test('mergeResults should return the best result', t => {
const indexesResults = {
index1: [
{status: 'ok', result: {score: 1}}
],
index2: [
{status: 'ok', result: {score: 3}}
]
}

const result = mergeResults(indexesResults)

t.deepEqual(result, [{status: 'ok', result: {score: 3}, index: 'index2'}])
})

test('mergeResults should return the error result if any and no successful result', t => {
const indexesResults = {
index1: [
{status: 'not-found'}
],
index2: [
{status: 'error', result: {message: 'error'}}
]
}

const result = mergeResults(indexesResults)

t.deepEqual(result, [{status: 'error', result: {message: 'error'}, index: 'index2'}])
})

test('mergeResults should return not-found if no successful result and no error result', t => {
const indexesResults = {
index1: [
{status: 'not-found'}
],
index2: [
{status: 'not-found'}
]
}

const result = mergeResults(indexesResults)

t.deepEqual(result, [{status: 'not-found', result: {}}])
})
37 changes: 34 additions & 3 deletions api/operations/batch.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,40 @@
import {maxBy} from 'lodash-es'

export default async function batch(payload, options = {}) {
const {indexes, signal} = options
const results = await indexes.dispatchRequest({...payload, indexes: ['address']}, 'batch', {signal})
return mergeResults(results, payload)
const results = await indexes.dispatchRequest(payload, 'batch', {signal})

return mergeResults(results)
}

export function mergeResults(indexesResults) {
return indexesResults.address
const successfulResults = []
let errorResult

for (const [index, indexResults] of Object.entries(indexesResults)) {
for (const result of indexResults) {
if (result.status === 'ok') {
successfulResults.push({
status: 'ok',
result: result.result,
index
})
}

if (result.status === 'error') {
errorResult = {
status: 'error',
result: result.result,
index
}
}
}
}

if (successfulResults.length === 0) {
return errorResult ? [errorResult] : [{status: 'not-found', result: {}}]
}

const bestResult = maxBy(successfulResults, item => item.result.score)
return [bestResult]
}
5 changes: 1 addition & 4 deletions api/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import multer from 'multer'
import w from '../lib/w.js'
import errorHandler from '../lib/error-handler.js'
import {validateBatchPayload} from '../lib/batch.js'
import {GEOCODE_INDEXES} from '../lib/config.js'

import {createIndexes} from './indexes/index.js'
import search from './operations/search.js'
Expand All @@ -22,10 +23,6 @@ import {computeHtmlPage} from './open-api/swagger-ui.js'

import {csv, parseAndValidate} from './csv/index.js'

const GEOCODE_INDEXES = process.env.GEOCODE_INDEXES
? process.env.GEOCODE_INDEXES.split(',')
: ['address', 'poi', 'parcel']

const DEFAULT_UPLOAD_DIR = 'uploads/'

const {API_ROOT_REDIRECTION} = process.env
Expand Down
5 changes: 5 additions & 0 deletions lib/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import process from 'node:process'

export const GEOCODE_INDEXES = process.env.GEOCODE_INDEXES
? process.env.GEOCODE_INDEXES.split(',')
: ['address', 'poi', 'parcel']