Skip to content

Commit

Permalink
Merge pull request #781 from MITLibraries/gdt-135-geodistance
Browse files Browse the repository at this point in the history
Adds geo_distance query to opensearch
  • Loading branch information
JPrevost authored Feb 8, 2024
2 parents dfc1529 + 726b2d7 commit d267e10
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 23 deletions.
3 changes: 2 additions & 1 deletion .codeclimate.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
plugins:
rubocop:
enabled: true
channel: rubocop-1-23-0
channel: rubocop-1-56-3

2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
AllCops:
TargetRubyVersion: 2.7
TargetRubyVersion: 3.1
NewCops: enable
Exclude:
- "db/**/*"
Expand Down
31 changes: 26 additions & 5 deletions app/models/opensearch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def search(from, params, client, highlight = false, index = nil)
@params = params
@highlight = highlight
index = default_index unless index.present?
client.search(index: index,
client.search(index:,
body: build_query(from))
end

Expand All @@ -17,10 +17,10 @@ def default_index
# Construct the json query to send to elasticsearch
def build_query(from)
query_hash = {
from: from,
from:,
size: SIZE,
query: query,
aggregations: aggregations
query:,
aggregations:
}

query_hash[:highlight] = highlight if @highlight
Expand Down Expand Up @@ -109,9 +109,30 @@ def matches
match_single_field_nested(:identifiers, m)
match_single_field_nested(:locations, m)
match_single_field_nested(:subjects, m)

match_geodistance(m) if @params[:geodistance].present?
m
end

def match_geodistance(match_array)
match_array << {
bool: {
must: {
match_all: {}
},
filter: {
geo_distance: {
distance: @params[:geodistance][:distance],
'locations.geoshape': {
lat: @params[:geodistance][:latitude],
lon: @params[:geodistance][:longitude]
}
}
}
}
}
end

# https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html
def filters(params)
f = []
Expand Down Expand Up @@ -177,7 +198,7 @@ def source_array(param)
param.each do |source|
sources << {
term: {
source: source
source:
}
}
end
Expand Down
62 changes: 46 additions & 16 deletions test/models/opensearch_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,66 @@ class OpensearchTest < ActiveSupport::TestCase
test 'matches citation' do
os = Opensearch.new
os.instance_variable_set(:@params, { citation: 'foo' })
assert os.matches.select { |m| m[:citation] == 'foo' }

assert(os.matches.to_json.include?('{"match":{"citation":"foo"}}'))
end

test 'matches title' do
os = Opensearch.new
os.instance_variable_set(:@params, { title: 'foo' })
assert os.matches.select { |m| m[:title] == 'foo' }

assert(os.matches.to_json.include?('{"match":{"title":"foo"}}'))
end

test 'matches contributors' do
os = Opensearch.new
os.instance_variable_set(:@params, { contributors: 'foo' })
assert os.matches.select { |m| m['contributors.value'] == 'foo' }

assert(os.matches.to_json.include?('{"match":{"contributors.value":"foo"}}'))
end

test 'matches funding_information' do
os = Opensearch.new
os.instance_variable_set(:@params, { funding_information: 'foo' })
assert os.matches.select { |m| m['funding_information.funder_name'] == 'foo' }

assert(os.matches.to_json.include?('{"match":{"funding_information.funder_name":"foo"}}'))
end

test 'matches identifiers' do
os = Opensearch.new
os.instance_variable_set(:@params, { identifiers: 'foo' })
assert os.matches.select { |m| m['identifiers.value'] == 'foo' }

assert(os.matches.to_json.include?('{"match":{"identifiers.value":"foo"}}'))
end

test 'matches locations' do
os = Opensearch.new
os.instance_variable_set(:@params, { locations: 'foo' })
assert os.matches.select { |m| m['locations.value'] == 'foo' }

assert(os.matches.to_json.include?('{"match":{"locations.value":"foo"}}'))
end

test 'matches subjects' do
os = Opensearch.new
os.instance_variable_set(:@params, { subjects: 'foo' })
assert os.matches.select { |m| m['subjects.value'] == 'foo' }

assert(os.matches.to_json.include?('{"match":{"subjects.value":"foo"}}'))
end

test 'matches everything' do
os = Opensearch.new
os.instance_variable_set(:@params, { q: 'this', citation: 'here', title: 'is', contributors: 'a',
funding_information: 'real', identifiers: 'search', locations: 'rest',
subjects: 'assured,' })
matches = os.matches
assert matches.select { |m| m[:q] == 'this' }
assert matches.select { |m| m[:citation] == 'here' }
assert matches.select { |m| m[:title] == 'is' }
assert matches.select { |m| m['contributors.value'] == 'a' }
assert matches.select { |m| m['funding_information.funder_name'] == 'real' }
assert matches.select { |m| m['identifiers.value'] == 'search' }
assert matches.select { |m| m['locations.value'] == 'rest' }
assert matches.select { |m| m['subjects.value'] == 'assured' }

assert(os.matches.to_json.include?('{"multi_match":{"query":"this","fields":'))
assert(os.matches.to_json.include?('{"match":{"citation":"here"}}'))
assert(os.matches.to_json.include?('{"match":{"title":"is"}}'))
assert(os.matches.to_json.include?('{"match":{"contributors.value":"a"}}'))
assert(os.matches.to_json.include?('{"match":{"funding_information.funder_name":"real"}}'))
assert(os.matches.to_json.include?('{"match":{"identifiers.value":"search"}}'))
assert(os.matches.to_json.include?('{"match":{"locations.value":"rest"}}'))
assert(os.matches.to_json.include?('{"match":{"subjects.value":"assured,"}}'))
end

test 'can override index' do
Expand Down Expand Up @@ -266,4 +273,27 @@ class OpensearchTest < ActiveSupport::TestCase

refute(os.build_query(0).include?('highlight'))
end

test 'can search by geopoint' do
os = Opensearch.new
os.instance_variable_set(:@params,
{ geodistance: { latitude: '42.361145', longitude: '-71.057083', distance: '50mi' } })

assert(
os.query.to_json.include?('{"distance":"50mi","locations.geoshape":{"lat":"42.361145","lon":"-71.057083"}}')
)
end

test 'can search for combination of geopoint and keyword' do
os = Opensearch.new
os.instance_variable_set(:@params,
{ geodistance: { latitude: '42.361145', longitude: '-71.057083', distance: '50mi' },
q: 'rail stations' })

assert(os.matches.to_json.include?('{"multi_match":{"query":"rail stations","fields":'))

assert(
os.query.to_json.include?('{"distance":"50mi","locations.geoshape":{"lat":"42.361145","lon":"-71.057083"}}')
)
end
end

0 comments on commit d267e10

Please sign in to comment.