Skip to content

Commit

Permalink
Merge pull request #792 from MITLibraries/gdt-184
Browse files Browse the repository at this point in the history
Add support for geobox searches to GraphQL
  • Loading branch information
matt-bernhardt authored Feb 15, 2024
2 parents 4bdafc0 + 1ed5cbc commit 66dd37a
Show file tree
Hide file tree
Showing 8 changed files with 1,227 additions and 5 deletions.
10 changes: 10 additions & 0 deletions app/graphql/types/geobox_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Types
class GeoboxType < Types::BaseInputObject
description 'Search within a box specified by pairs of latitudes and longitudes. Their order should be left, ' \
'bottom, right, top'
argument :min_longitude, Float, description: 'A decimal between -180.0 and 180.0 (Western hemisphere is negative)'
argument :min_latitude, Float, description: 'A decimal between -90.0 and 90.0 (Southern hemisphere is negative)'
argument :max_longitude, Float, description: 'A decimal between -180.0 and 180.0 (Western hemisphere is negative)'
argument :max_latitude, Float, description: 'A decimal between -90.0 and 90.0 (Southern hemisphere is negative)'
end
end
13 changes: 8 additions & 5 deletions app/graphql/types/query_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ def record_id(id:, index:)
'award name, etc.'
argument :geodistance, GeodistanceType, required: false, default_value: nil,
description: 'Search within a certain distance of a specific location'
argument :geobox, GeoboxType, required: false, default_value: nil,
description: 'Search within a specified box'
argument :identifiers, String, required: false, default_value: nil,
description: 'Search by unique indentifier; e.g., ISBN, DOI, etc.'
argument :locations, String, required: false, default_value: nil, description: 'Search by locations'
Expand Down Expand Up @@ -79,9 +81,9 @@ def record_id(id:, index:)
'for a list of possible values'
end

def search(searchterm:, citation:, contributors:, funding_information:, geodistance:, identifiers:, locations:,
subjects:, title:, index:, source:, from:, **filters)
query = construct_query(searchterm, citation, contributors, funding_information, geodistance, identifiers,
def search(searchterm:, citation:, contributors:, funding_information:, geodistance:, geobox:, identifiers:,
locations:, subjects:, title:, index:, source:, from:, **filters)
query = construct_query(searchterm, citation, contributors, funding_information, geodistance, geobox, identifiers,
locations, subjects, title, source, filters)

results = Opensearch.new.search(from, query, Timdex::OSClient, highlight_requested?, index)
Expand Down Expand Up @@ -111,14 +113,15 @@ def inject_hits_fields_into_source(hits)
modded_sources
end

def construct_query(searchterm, citation, contributors, funding_information, geodistance, identifiers, locations,
subjects, title, source, filters)
def construct_query(searchterm, citation, contributors, funding_information, geodistance, geobox, identifiers,
locations, subjects, title, source, filters)
query = {}
query[:q] = searchterm
query[:citation] = citation
query[:contributors] = contributors
query[:funding_information] = funding_information
query[:geodistance] = geodistance
query[:geobox] = geobox
query[:identifiers] = identifiers
query[:locations] = locations
query[:subjects] = subjects
Expand Down
182 changes: 182 additions & 0 deletions test/controllers/graphql_controller_v2_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,188 @@ def setup
end
end

test 'graphqlv2 geobox search alone' do
VCR.use_cassette('graphqlv2 geobox') do
post '/graphql', params: { query: '{
search(geobox: {
minLongitude: -73.507,
minLatitude: 41.239,
maxLongitude: -69.928,
maxLatitude: 42.886
}) {
hits
records {
title
locations {
geoshape
kind
value
}
}
}
}' }
assert_equal(200, response.status)
json = JSON.parse(response.body)

assert_nil(json['errors'])
assert(json['data']['search']['hits'].positive?)
end
end

test 'graphqlv2 geobox search required arguments' do
post '/graphql', params: { query: '{
search(geobox: {
minLongitude: -73.507,
minLatitude: 41.239,
maxLongitude: -69.928,
}) {
hits
records {
title
locations {
geoshape
kind
value
}
}
}
}' }
assert_equal(200, response.status)
json = JSON.parse(response.body)

assert(json['errors'].length.positive?)
assert_equal(
"Argument 'maxLatitude' on InputObject 'Geobox' is required. Expected type Float!",
json['errors'].first['message']
)
end

test 'graphqlv2 geobox search longitude order matters' do
# This is fragile to our collection having an equal number of records in both hemispheres.
eastern_hits = 0
western_hits = 0
VCR.use_cassette('graphqlv2 geobox eastern hemisphere') do
post '/graphql', params: { query: '{
search(geobox: {
minLongitude: 0,
minLatitude: -90,
maxLongitude: 180,
maxLatitude: 90
}) {
hits
records {
title
locations {
geoshape
kind
value
}
}
}
}' }
assert_equal(200, response.status)
json = JSON.parse(response.body)

assert_nil(json['errors'])
eastern_hits = json['data']['search']['hits']
assert(eastern_hits.positive?)
end
VCR.use_cassette('graphqlv2 geobox western hemisphere') do
post '/graphql', params: { query: '{
search(geobox: {
minLongitude: 180,
minLatitude: -90,
maxLongitude: 0,
maxLatitude: 90
}) {
hits
records {
title
locations {
geoshape
kind
value
}
}
}
}' }
assert_equal(200, response.status)
json = JSON.parse(response.body)

assert_nil(json['errors'])
western_hits = json['data']['search']['hits']
assert(western_hits.positive?)
end
refute_equal(eastern_hits, western_hits)
end

test 'graphqlv2 geobox search with keyword search' do
VCR.use_cassette('graphqlv2 geobox with keyword') do
post '/graphql', params: { query: '{
search(
searchterm: "train stations",
geobox: {
minLongitude: -73.507,
minLatitude: 41.239,
maxLongitude: -69.928,
maxLatitude: 42.886
}
) {
hits
records {
title
locations {
geoshape
kind
value
}
}
}
}' }
assert_equal(200, response.status)
json = JSON.parse(response.body)

assert_nil(json['errors'])
assert(json['data']['search']['hits'].positive?)
end
end

test 'graphqlv2 geobox search with geodistance search' do
# This is not a recommended way to work, but it does function.
VCR.use_cassette('graphqlv2 geobox with geodistance') do
post '/graphql', params: { query: '{
search(
geodistance: {
distance: "1km",
latitude: 0,
longitude: 0
},
geobox: {
minLongitude: -73.507,
minLatitude: 41.239,
maxLongitude: -69.928,
maxLatitude: 42.886
}
) {
hits
records {
title
locations {
geoshape
kind
value
}
}
}
}' }
assert_equal(200, response.status)
json = JSON.parse(response.body)

assert_nil(json['errors'])
assert(json['data']['search']['hits'].positive?)
end
end

test 'graphqlv2 search aggregations' do
VCR.use_cassette('graphql v2 search data') do
post '/graphql', params: { query: '{
Expand Down
77 changes: 77 additions & 0 deletions test/vcr_cassettes/graphqlv2_geobox.yml

Large diffs are not rendered by default.

Loading

0 comments on commit 66dd37a

Please sign in to comment.