From 92794b30a7945ebd962f656c4f1b453cedded3c5 Mon Sep 17 00:00:00 2001 From: Jeremy Prevost Date: Mon, 12 Feb 2024 14:23:45 -0500 Subject: [PATCH] Adds opensearch geo_bounding_box query support Relevant ticket(s): * https://mitlibraries.atlassian.net/browse/GDT-167 How does this address that need: * Adds an optional geo_bounding_box search to our existing opensearch query builder --- app/models/opensearch.rb | 25 +++++++++++++++++++++++++ test/models/opensearch_test.rb | 29 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/app/models/opensearch.rb b/app/models/opensearch.rb index e533f79c..223e33d6 100644 --- a/app/models/opensearch.rb +++ b/app/models/opensearch.rb @@ -111,9 +111,34 @@ def matches match_single_field_nested(:subjects, m) match_geodistance(m) if @params[:geodistance].present? + match_geobox(m) if @params[:geobox].present? m end + # https://opensearch.org/docs/latest/query-dsl/geo-and-xy/geo-bounding-box/ + def match_geobox(match_array) + match_array << { + bool: { + must: { + match_all: {} + }, + filter: { + geo_bounding_box: { + 'locations.geoshape': { + top: @params[:geobox][:max_latitude], + bottom: @params[:geobox][:min_latitude], + left: @params[:geobox][:min_longitude], + right: @params[:geobox][:max_longitude] + } + } + } + } + } + end + + # https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-geo-distance-query.html + # Note: at the time of this implementation, opensearch does not have documentation on + # this features hence the link to the prefork elasticsearch docs def match_geodistance(match_array) match_array << { bool: { diff --git a/test/models/opensearch_test.rb b/test/models/opensearch_test.rb index 39d1870e..b7624cf1 100644 --- a/test/models/opensearch_test.rb +++ b/test/models/opensearch_test.rb @@ -279,6 +279,8 @@ class OpensearchTest < ActiveSupport::TestCase os.instance_variable_set(:@params, { geodistance: { latitude: '42.361145', longitude: '-71.057083', distance: '50mi' } }) + refute(os.matches.to_json.include?('{"multi_match":{"query":')) + assert( os.query.to_json.include?('{"distance":"50mi","locations.geoshape":{"lat":"42.361145","lon":"-71.057083"}}') ) @@ -296,4 +298,31 @@ class OpensearchTest < ActiveSupport::TestCase os.query.to_json.include?('{"distance":"50mi","locations.geoshape":{"lat":"42.361145","lon":"-71.057083"}}') ) end + + test 'can search by bounding box' do + os = Opensearch.new + os.instance_variable_set(:@params, + { geobox: { max_latitude: '42.886', min_latitude: '41.239', + max_longitude: '-73.928', min_longitude: '-69.507' } }) + + refute(os.matches.to_json.include?('{"multi_match":{"query"')) + + assert( + os.query.to_json.include?('{"locations.geoshape":{"top":"42.886","bottom":"41.239","left":"-69.507","right":"-73.928"}}') + ) + end + + test 'can search by bounding box and keyword' do + os = Opensearch.new + os.instance_variable_set(:@params, + { geobox: { max_latitude: '42.886', min_latitude: '41.239', + max_longitude: '-73.928', min_longitude: '-69.507' }, + q: 'rail stations' }) + + assert(os.matches.to_json.include?('{"multi_match":{"query":"rail stations","fields":')) + + assert( + os.query.to_json.include?('{"locations.geoshape":{"top":"42.886","bottom":"41.239","left":"-69.507","right":"-73.928"}}') + ) + end end