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

Backport: use lucene query parser for browse category search #3007

Merged
merged 6 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,27 @@ Style/IfWithBooleanLiteralBranches: # (new in 1.9)
Enabled: true
Style/StringChars: # (new in 1.12)
Enabled: true

# Disabling these rules so that a backport to Spotlight v3.5.0.2 can pass CI
Spec/ExampleWording:
Enabled: false
Style/Documentation:
Enabled: false
Style/RedundantParentheses:
Enabled: false
Style/HashEachMethods:
Enabled: false
Lint/RedundantSafeNavigation:
Enabled: false
Style/HashExcept:
Enabled: false
Rails/Pluck:
Enabled: false
Rails/FindEach:
Enabled: false
Lint/RedundantStringCoercion:
Enabled: false
Layout/EmptyLinesAroundExceptionHandlingKeywords:
Enabled: false
Lint/SymbolConversion:
Enabled: false
19 changes: 16 additions & 3 deletions app/models/concerns/spotlight/browse_category_search_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module BrowseCategorySearchBuilder
def apply_browse_category_defaults(solr_params)
return unless current_browse_category

solr_params.merge!(browse_category_search_builder.to_hash)
solr_params.merge!(browse_category_search_builder.to_hash.except(:q))
end

def fix_up_browse_category_defaults(solr_params)
Expand All @@ -25,14 +25,27 @@ def fix_up_browse_category_defaults(solr_params)
end

def fix_up_browse_category_queries(solr_params)
return unless solr_params.dig(:json, :query, :bool, :must) && blacklight_params[:q]
return unless current_browse_category

query = browse_category_search_builder.to_hash[:q]

solr_params.append_query(query) if query.present?

# for a browse category formed from a saved search, the query syntax reads as two "must" values
# e.g. "json"=>{"query"=>{"bool"=>{"must"=>["savedsearchterm", "browsecategorysearchterm"]}}}}
must_values = solr_params.dig(:json, :query, :bool, :must)

return unless must_values&.any?

# this type of query must be parsed by lucene to function
solr_params[:defType] = 'lucene'

# This replicates existing spotlight 2.x search behavior, more or less. It
# doesn't take into account the possibility that the browse category query
# could use a different search field (which.. doesn't have an existing UI
# control.. and may require additional upstream work to properly encapsulate
# the two query parameters)
solr_params[:json][:query][:bool][:must].map! do |q|
must_values.map! do |q|
q.is_a?(String) ? { edismax: { query: q } } : q
end
end
Expand Down
1 change: 0 additions & 1 deletion blacklight-spotlight.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,5 @@ these collections.)
s.add_development_dependency 'sitemap_generator'
s.add_development_dependency 'solr_wrapper'
s.add_development_dependency 'sqlite3'
s.add_development_dependency 'webdrivers'
s.add_development_dependency 'webmock'
end
2 changes: 1 addition & 1 deletion config/locales/spotlight.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ en:
note_provenance_tesim: Provenance
note_references_tesim: References
note_source_tesim: Source
personal_name_ssm: Personal names
personal_name_ssim: Personal names
search:
all_fields: Everything
author: Author
Expand Down
12 changes: 12 additions & 0 deletions spec/factories/searches.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,16 @@

after(:build) { |search| search.thumbnail = FactoryBot.create(:featured_image) }
end

factory :search_field_search, class: 'Spotlight::Search' do
exhibit
title { 'Based on a search field' }
query_params { { 'search_field' => 'search', 'q' => 'model' } }
end

factory :facet_search, class: 'Spotlight::Search' do
exhibit
title { 'Based on a facet' }
query_params { { 'f' => { 'language_ssim' => 'Latin' } } }
end
end
28 changes: 27 additions & 1 deletion spec/features/browse_category_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@

before do
blacklight_config = exhibit.blacklight_config
blacklight_config.index_fields.each do |_, config|
blacklight_config.index_fields.each_value do |config|
config.gallery = false
end
blacklight_config.save
Expand Down Expand Up @@ -155,4 +155,30 @@
expect(page).to have_css "meta[property='og:title'][content='#{search.title}']", visible: false
end
end

context 'with a search field based browse category' do
let(:search) { FactoryBot.create(:search_field_search, title: 'Search field search', exhibit: exhibit, published: true, search_box: true) }

it 'conducts a search within the browse category' do
visit spotlight.exhibit_browse_path(exhibit, search)
expect(search.documents.count).to eq 6

fill_in 'Search within this browse category', with: 'azimuthal'
click_button 'Search within browse category'
expect(page).to have_text('Your search matched 1 of 6 items in this browse category.')
end
end

context 'with a facet based browse category' do
let(:search) { FactoryBot.create(:facet_search, title: 'Facet search', exhibit: exhibit, published: true, search_box: true) }

it 'conducts a search within the browse category' do
visit spotlight.exhibit_browse_path(exhibit, search)
expect(search.documents.count).to eq 3

fill_in 'Search within this browse category', with: 'Stopendael'
click_button 'Search within browse category'
expect(page).to have_text('Your search matched 1 of 3 items in this browse category.')
end
end
end
1 change: 1 addition & 0 deletions spec/features/exhibits/translation_editing_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@

visit spotlight.search_exhibit_catalog_path(exhibit, q: '*', locale: 'fr')

find('button[aria-label="plus »"]').click # Hamburger icon
expect(page).to have_css('h3.facet-field-heading', text: 'Géographique')
end
end
Expand Down
72 changes: 50 additions & 22 deletions spec/models/spotlight/browse_category_search_builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,65 @@ class BrowseCategoryMockSearchBuilder < Blacklight::SearchBuilder
end
let(:solr_request) { Blacklight::Solr::Request.new }
let(:blacklight_params) { { browse_category_id: search.id } }
let(:search) { FactoryBot.create(:search, exhibit: exhibit, query_params: { sort: 'type', f: { genre_ssim: ['term'] }, q: 'search query' }) }

describe '#restrict_to_browse_category' do
it 'adds the search query parameters from the browse category' do
params = subject.to_hash.with_indifferent_access
context 'with a facet as the basis of the browse category (no search query present)' do
let(:search) { FactoryBot.create(:search, exhibit: exhibit, query_params: { sort: 'type', f: { genre_ssim: ['genre facet'] } }) }

expect(params).to include(
q: 'search query',
fq: ['{!term f=genre_ssim}term'],
sort: 'sort_type_ssi asc'
)
describe 'constructs params properly' do
it 'adds facet to the solr params' do
params = subject.to_hash.with_indifferent_access

expect(params).to include(
fq: ['{!term f=genre_ssim}genre facet'],
sort: 'sort_type_ssi asc'
)
end

it 'does not override the default query parser' do
params = subject.to_hash.with_indifferent_access
expect(params).not_to include(:defType)
end
end
end

context 'with a user-provided query' do
let(:blacklight_params) { { browse_category_id: search.id, q: 'cats' } }
context 'with a search query as part of the construction of the browse category' do
let(:search) { FactoryBot.create(:search, exhibit: exhibit, query_params: { sort: 'type', f: { genre_ssim: ['term'] }, q: 'search query' }) }

it 'uses the user-provided query to further restrict the search' do
describe 'constructs params properly' do
it 'includes the facet and the seach term information in the params' do
params = subject.to_hash.with_indifferent_access
expect(params).not_to include(:q)

expect(params).to include(
json: {
query: {
bool: {
must: [
{ edismax: { query: 'cats' } },
{ edismax: { query: 'search query' } }
]
q: 'search query',
fq: ['{!term f=genre_ssim}term'],
sort: 'sort_type_ssi asc'
)
end

context 'with a search term present' do
let(:blacklight_params) { { browse_category_id: search.id, q: 'cats' } }

it 'manipulates the solr query as expected into json syntax' do
params = subject.to_hash.with_indifferent_access
expect(params).not_to include(:q)
expect(params).to include(
json: {
query: {
bool: {
must: [
{ edismax: { query: 'search query' } },
{ edismax: { query: 'cats' } }
]
}
}
}
}
)
)
end

it 'overrides the default query parser' do
params = subject.to_hash.with_indifferent_access
expect(params).to include(defType: 'lucene')
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/services/spotlight/iiif_resource_resolver_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
it 'logs that the JSON was unparsable and falls through to an no-canvas error' do
klass = described_class.name
expect(Rails.logger).to receive(:warn).with(
a_string_matching(/#{klass} failed to parse #{iiif_manifest_url} with: \d{3}: unexpected token at '#{fixture_json}'/)
a_string_matching(/#{klass} failed to parse #{iiif_manifest_url} .* unexpected token at/)
)
expect { resolver.resolve! }.to raise_error(Spotlight::IiifResourceResolver::ManifestError)
end
Expand Down
22 changes: 5 additions & 17 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
require 'engine_cart'
EngineCart.load_application!

Internal::Application.config.active_job.queue_adapter = :inline
ActiveJob::Base.queue_adapter = :test

require 'rails-controller-testing'
require 'rspec/collection_matchers'
Expand All @@ -16,23 +16,11 @@
require 'paper_trail/frameworks/rspec'

require 'selenium-webdriver'
require 'webdrivers'
require 'webmock/rspec'

Capybara.javascript_driver = :headless_chrome
Capybara.javascript_driver = :selenium_chrome_headless

Capybara.register_driver :headless_chrome do |app|
Capybara::Selenium::Driver.load_selenium
browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
opts.args << '--headless'
opts.args << '--disable-gpu'
opts.args << '--no-sandbox'
opts.args << '--window-size=1280,1696'
end
Capybara::Selenium::Driver.new(app, browser: :chrome, capabilities: [browser_options])
end
require 'webmock/rspec'
allowed_sites = ['chromedriver.storage.googleapis.com']
allowed_sites = ['chromedriver.storage.googleapis.com', 'googlechromelabs.github.io', 'edgedl.me.gvt1.com']

WebMock.disable_net_connect!(net_http_connect_on_start: true, allow_localhost: true, allow: allowed_sites)

Expand Down Expand Up @@ -83,8 +71,8 @@
config.after(:each, type: :feature) { Warden.test_reset! }
config.include Controllers::EngineHelpers, type: :controller
config.include Capybara::DSL
config.include ::Rails.application.routes.url_helpers
config.include ::Rails.application.routes.mounted_helpers
config.include Rails.application.routes.url_helpers
config.include Rails.application.routes.mounted_helpers
config.include Spotlight::TestFeaturesHelpers, type: :feature
config.include CapybaraDefaultMaxWaitMetadataHelper, type: :feature

Expand Down
4 changes: 2 additions & 2 deletions spec/test_app_templates/catalog_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ class CatalogController < ApplicationController
# :show may be set to false if you don't want the facet to be drawn in the
# facet bar
config.add_facet_field 'genre_ssim', label: I18n.t('spotlight.search.fields.facet.genre_ssim'), limit: true
config.add_facet_field 'personal_name_ssm', label: I18n.t('spotlight.search.fields.facet.personal_name_ssm'), limit: true
config.add_facet_field 'corporate_name_ssm', label: I18n.t('spotlight.search.fields.facet.corporate_name_ssm'), limit: true
config.add_facet_field 'personal_name_ssim', label: I18n.t('spotlight.search.fields.facet.personal_name_ssm'), limit: true
config.add_facet_field 'corporate_name_ssim', label: I18n.t('spotlight.search.fields.facet.corporate_name_ssm'), limit: true
config.add_facet_field 'subject_geographic_ssim', label: I18n.t('spotlight.search.fields.facet.subject_geographic_ssim')
config.add_facet_field 'subject_temporal_ssim', label: I18n.t('spotlight.search.fields.facet.subject_temporal_ssim')
config.add_facet_field 'language_ssim', label: I18n.t('spotlight.search.fields.facet.language_ssim')
Expand Down
Loading