Skip to content

Commit 4ccac6f

Browse files
Add support for geodistance search to GraphQL
** Why are these changes being introduced: * We recently added support for a geodistance search to the OpenSearch model, and now we need to enable that support within GraphQL. ** Relevant ticket(s): * https://mitlibraries.atlassian.net/browse/gdt-162 ** How does this address that need: * This defines a Geodistance type, with three fields: a distance (as a string), a latitude value (a float), and a longitude value (also a float). * The Geodistance type is then added as an argument of the search field, allowing GraphQL consumers to conduct searches geospatially. * We add three tests to support this functionality: - Geospatial search alone - Geospatial search combined with a search term - Confirming that the geodistance type imposes a requirement for those three fields (by omitting the distance parameter) ** Document any side effects to this change: * This may be the first time we are using Float as a field type in GraphQL? It seems that we use String elsewhere even when the input is numeric. I'm not sure this is good pracrice, so I wanted to propose that we use a float here. * There are no tests to confirm that the latitude and longitude fields are required, as it seems unlikely that this requirement would not be met in practice. The distance parameter feels the most likely to be omitted.
1 parent d267e10 commit 4ccac6f

File tree

5 files changed

+607
-6
lines changed

5 files changed

+607
-6
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module Types
2+
class GeodistanceType < Types::BaseInputObject
3+
description 'Search within a certain distance of a given latitude and longitude'
4+
argument :distance, String, description: 'Search distance to the location? (include units; i.e. "100km" or "50mi")'
5+
argument :latitude, Float, description: 'A decimal between -90.0 and 90.0 (Southern hemisphere is negative)'
6+
argument :longitude, Float, description: 'A decimal between -180.0 and 180.0 (Western hemisphere is negative)'
7+
end
8+
end

app/graphql/types/query_type.rb

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ def record_id(id:, index:)
4141
argument :funding_information, String, required: false, default_value: nil,
4242
description: 'Search by funding information; e.g., funding source, ' \
4343
'award name, etc.'
44+
argument :geodistance, GeodistanceType, required: false, default_value: nil,
45+
description: 'Search within a certain distance of a specific location'
4446
argument :identifiers, String, required: false, default_value: nil,
4547
description: 'Search by unique indentifier; e.g., ISBN, DOI, etc.'
4648
argument :locations, String, required: false, default_value: nil, description: 'Search by locations'
@@ -77,10 +79,10 @@ def record_id(id:, index:)
7779
'for a list of possible values'
7880
end
7981

80-
def search(searchterm:, citation:, contributors:, funding_information:, identifiers:, locations:, subjects:,
81-
title:, index:, source:, from:, **filters)
82-
query = construct_query(searchterm, citation, contributors, funding_information, identifiers, locations,
83-
subjects, title, source, filters)
82+
def search(searchterm:, citation:, contributors:, funding_information:, geodistance:, identifiers:, locations:,
83+
subjects:, title:, index:, source:, from:, **filters)
84+
query = construct_query(searchterm, citation, contributors, funding_information, geodistance, identifiers,
85+
locations, subjects, title, source, filters)
8486

8587
results = Opensearch.new.search(from, query, Timdex::OSClient, highlight_requested?, index)
8688

@@ -109,13 +111,14 @@ def inject_hits_fields_into_source(hits)
109111
modded_sources
110112
end
111113

112-
def construct_query(searchterm, citation, contributors, funding_information, identifiers, locations, subjects,
113-
title, source, filters)
114+
def construct_query(searchterm, citation, contributors, funding_information, geodistance, identifiers, locations,
115+
subjects, title, source, filters)
114116
query = {}
115117
query[:q] = searchterm
116118
query[:citation] = citation
117119
query[:contributors] = contributors
118120
query[:funding_information] = funding_information
121+
query[:geodistance] = geodistance
119122
query[:identifiers] = identifiers
120123
query[:locations] = locations
121124
query[:subjects] = subjects

test/controllers/graphql_controller_v2_test.rb

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,75 @@ def setup
183183
end
184184
end
185185

186+
test 'graphqlv2 geodistance search returns results' do
187+
VCR.use_cassette('graphqlv2 geodistance') do
188+
post '/graphql', params: { query: '{
189+
search(geodistance: {
190+
distance: "100000km",
191+
latitude: 42.3596653,
192+
longitude: -71.0921384
193+
}) {
194+
hits
195+
records {
196+
title
197+
}
198+
}
199+
}' }
200+
assert_equal(200, response.status)
201+
json = JSON.parse(response.body)
202+
203+
assert_nil(json['errors'])
204+
assert(json['data']['search']['hits'].positive?)
205+
end
206+
end
207+
208+
test 'graphqlv2 geodistance search fails without three required arguments' do
209+
post '/graphql', params: { query: '{
210+
search(geodistance: {
211+
latitude: 42.3596653,
212+
longitude: -71.0921384
213+
}) {
214+
hits
215+
records {
216+
title
217+
}
218+
}
219+
}' }
220+
assert_equal(200, response.status)
221+
json = JSON.parse(response.body)
222+
223+
assert(json['errors'].length.positive?)
224+
assert_equal(
225+
"Argument 'distance' on InputObject 'Geodistance' is required. Expected type String!",
226+
json['errors'].first['message']
227+
)
228+
end
229+
230+
test 'graphqlv2 geodistance search with another argument' do
231+
VCR.use_cassette('graphqlv2 geodistance with searchterm') do
232+
post '/graphql', params: { query: '{
233+
search(
234+
searchterm: "train stations",
235+
geodistance: {
236+
distance: "100000km",
237+
latitude: 42.3596653,
238+
longitude: -71.0921384
239+
}
240+
) {
241+
hits
242+
records {
243+
title
244+
}
245+
}
246+
}' }
247+
assert_equal(200, response.status)
248+
json = JSON.parse(response.body)
249+
250+
assert_nil(json['errors'])
251+
assert(json['data']['search']['hits'].positive?)
252+
end
253+
end
254+
186255
test 'graphqlv2 search aggregations' do
187256
VCR.use_cassette('graphql v2 search data') do
188257
post '/graphql', params: { query: '{

0 commit comments

Comments
 (0)