blacklight_advanced_search 2.2.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +8 -0
- data/{README.rdoc → README.md} +85 -59
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/app/assets/javascripts/blacklight_advanced_search.js +4 -4
- data/app/assets/stylesheets/blacklight_advanced_search/advanced_results.css +11 -31
- data/app/assets/stylesheets/blacklight_advanced_search/blacklight_advanced_search_styles.css.scss +42 -7
- data/app/controllers/blacklight_advanced_search/advanced_controller.rb +1 -3
- data/app/helpers/advanced_helper.rb +13 -9
- data/app/views/advanced/_advanced_search_facets.html.erb +15 -2
- data/app/views/advanced/_advanced_search_facets_as_select.html.erb +30 -0
- data/app/views/advanced/_advanced_search_fields.html.erb +5 -3
- data/app/views/advanced/_advanced_search_form.html.erb +26 -25
- data/app/views/advanced/_advanced_search_help.html.erb +19 -17
- data/app/views/advanced/_advanced_search_submit_btns.html.erb +12 -0
- data/app/views/advanced/_facet_limit.html.erb +11 -3
- data/app/views/advanced/index.html.erb +19 -6
- data/app/views/blacklight_advanced_search/_facet_limit.html.erb +8 -3
- data/blacklight_advanced_search.gemspec +7 -3
- data/config/locales/blacklight_advanced_search.en.yml +12 -0
- data/lib/blacklight_advanced_search.rb +28 -17
- data/lib/blacklight_advanced_search/advanced_query_parser.rb +26 -15
- data/lib/blacklight_advanced_search/redirect_legacy_params_filter.rb +32 -0
- data/lib/blacklight_advanced_search/render_constraints_override.rb +33 -7
- data/spec/features/blacklight_advanced_search_form_spec.rb +5 -5
- data/spec/lib/deep_merge_spec.rb +45 -0
- data/spec/test_app_templates/Gemfile.extra +2 -0
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +1 -1
- metadata +41 -15
- data/app/assets/javascripts/blacklight_advanced_search/blacklight_advanced_search_javascript.js +0 -48
- data/app/views/advanced/_facet_layout.html.erb +0 -5
@@ -1,3 +1,16 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
<%# used to render facets with checkboxes on advanced search form,
|
2
|
+
we pretty much just use the built-in blacklight render_facet_partials
|
3
|
+
helper.
|
4
|
+
|
5
|
+
But we've provided a local override of the _facet_limit
|
6
|
+
partial in our own `views/advanced/_facet_limit.html.erb`,
|
7
|
+
that is written to include checkboxes for form selection.
|
8
|
+
|
9
|
+
This is the default display of facets, but you can
|
10
|
+
also choose to use _advanced_search_facets_as_select,
|
11
|
+
for a chosen.js-compatible multi-select.
|
12
|
+
%>
|
13
|
+
|
14
|
+
<div class="advanced-facet-limits panel-group">
|
15
|
+
<%= render_facet_partials facet_field_names %>
|
3
16
|
</div>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<%# alternate version of facets on form that renders using multi-select.
|
2
|
+
Has to copy and paste more code from blacklight than default, making
|
3
|
+
it somewhat more fragile.
|
4
|
+
|
5
|
+
Logic taken from facets_helper_behavior.rb, #render_facet_partials and
|
6
|
+
#render_facet_limit.
|
7
|
+
%>
|
8
|
+
|
9
|
+
<% facets_from_request(facet_field_names).each do |display_facet| %>
|
10
|
+
<% if should_render_facet?(display_facet) %>
|
11
|
+
<div class="form-group advanced-search-facet">
|
12
|
+
<%= label_tag display_facet.name.parameterize, :class => "col-sm-3 control-label" do %>
|
13
|
+
<%= facet_field_label(display_facet.name) %>
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<div class="col-sm-9">
|
17
|
+
<%= content_tag(:select, :multiple => true,
|
18
|
+
:name => "f_inclusive[#{display_facet.name}][]",
|
19
|
+
:id => display_facet.name.parameterize,
|
20
|
+
:class => "form-control advanced-search-facet-select") do %>
|
21
|
+
<% display_facet.items.each do |facet_item| %>
|
22
|
+
<%= content_tag :option, :value => facet_item.value, :selected => facet_value_checked?(display_facet.name, facet_item.value) do %>
|
23
|
+
<%= facet_item.label %> (<%= number_with_delimiter facet_item.hits %>)
|
24
|
+
<% end %>
|
25
|
+
<% end %>
|
26
|
+
<% end %>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
<% end %>
|
30
|
+
<% end %>
|
@@ -1,6 +1,8 @@
|
|
1
1
|
<%- search_fields_for_advanced_search.each do |key, field_def| -%>
|
2
|
-
<div class="
|
3
|
-
<%= label_tag key, "#{field_def.label }:" %>
|
4
|
-
|
2
|
+
<div class="form-group advanced-search-field">
|
3
|
+
<%= label_tag key, "#{field_def.label }", :class => "col-sm-3 control-label" %>
|
4
|
+
<div class="col-sm-9">
|
5
|
+
<%= text_field_tag key, label_tag_default_for(key), :class => 'form-control' %>
|
6
|
+
</div>
|
5
7
|
</div>
|
6
8
|
<%- end -%>
|
@@ -1,42 +1,43 @@
|
|
1
|
-
|
1
|
+
<% unless (search_context_str = render_search_to_s( advanced_search_context)).blank? %>
|
2
|
+
<div class="constraints well search_history">
|
3
|
+
<h4><%= t 'blacklight_advanced_search.form.search_context' %></h4>
|
4
|
+
<%= search_context_str %>
|
5
|
+
</div>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<%= form_tag catalog_index_path, :class => 'advanced form-horizontal', :method => :get do %>
|
2
9
|
|
3
|
-
<%=
|
10
|
+
<%= render_hash_as_hidden_fields(params_for_search(advanced_search_context, {})) %>
|
4
11
|
|
5
|
-
<div class="
|
12
|
+
<div class="input-criteria">
|
6
13
|
|
7
|
-
<div class="
|
8
|
-
<
|
14
|
+
<div class="query-criteria">
|
15
|
+
<h3 class="query-criteria-heading">
|
16
|
+
<%= t('blacklight_advanced_search.form.query_criteria_heading_html', :select_menu => select_menu_for_field_operator ) %>
|
17
|
+
</h3>
|
9
18
|
|
10
19
|
<div id="advanced_search">
|
11
20
|
<%= render 'advanced/advanced_search_fields' %>
|
12
21
|
</div>
|
13
22
|
</div>
|
14
|
-
|
15
|
-
|
23
|
+
|
24
|
+
<div class="limit-criteria">
|
25
|
+
<h3 class="limit-criteria-heading"><%= t('blacklight_advanced_search.form.limit_criteria_heading_html')%></h3>
|
16
26
|
|
17
27
|
<div id="advanced_search_facets" class="limit_input">
|
18
|
-
|
28
|
+
<% if blacklight_config.try(:advanced_search).try {|h| h[:form_facet_partial] } %>
|
29
|
+
<%= render blacklight_config.advanced_search[:form_facet_partial] %>
|
30
|
+
<% else %>
|
31
|
+
<%= render 'advanced_search_facets' %>
|
32
|
+
<% end %>
|
19
33
|
</div>
|
20
34
|
</div>
|
21
35
|
</div>
|
22
36
|
|
23
|
-
|
24
|
-
<div class="constraints">
|
25
|
-
<h4>Within search:</h4>
|
26
|
-
<%= search_context_str %>
|
27
|
-
</div>
|
28
|
-
<% end %>
|
37
|
+
<hr>
|
29
38
|
|
30
|
-
|
31
|
-
|
32
|
-
<div class="pull-left">
|
33
|
-
<%= label_tag(:sort, "Sort results by") %>
|
34
|
-
<%= select_tag(:sort, options_for_select(sort_fields, h(params[:sort]))) %>
|
35
|
-
<%= hidden_field_tag(:search_field, blacklight_config.advanced_search[:url_key]) %>
|
36
|
-
</div>
|
37
|
-
<div class="pull-right">
|
38
|
-
<%= submit_tag 'Search', :class=>'btn btn-primary', :id=>'advanced_search' %>
|
39
|
-
<%= link_to "Start over", {:controller => "advanced", :action => "index"}, :class =>"reset btn" %>
|
40
|
-
</div>
|
39
|
+
<div class="sort-submit-buttons clearfix">
|
40
|
+
<%= render 'advanced_search_submit_btns' %>
|
41
41
|
</div>
|
42
|
+
|
42
43
|
<% end %>
|
@@ -1,22 +1,24 @@
|
|
1
|
-
<div>
|
2
|
-
<
|
3
|
-
<
|
4
|
-
|
5
|
-
|
1
|
+
<div class='panel panel-default'>
|
2
|
+
<div class="panel-heading">Search tips</div>
|
3
|
+
<div class="panel-body">
|
4
|
+
<ul class="advanced-help">
|
5
|
+
<li>Select "match all" to require all fields.
|
6
|
+
</li>
|
6
7
|
|
7
|
-
|
8
|
-
|
8
|
+
<li>Select "match any" to find at least one field.
|
9
|
+
</li>
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
<li>Combine keywords and attributes to find specific items.
|
12
|
+
</li>
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
<li>Use quotation marks to search as a phrase.
|
15
|
+
|
16
|
+
<li>Use "+" before a term to make it required. (Otherwise results matching only some of your terms may be included).</li>
|
17
|
+
|
18
|
+
<li>Use "-" before a word or phrase to exclude.
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
<li>Use "OR", "AND", and "NOT" to create complex boolean logic. You can use parentheses in your complex expressions. </li>
|
21
|
+
<li>Truncation and wildcards are not supported - word-stemming is done automatically.</li>
|
22
|
+
</ul>
|
23
|
+
</div>
|
22
24
|
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<div class="sort-buttons pull-left">
|
2
|
+
<%= label_tag(:sort, t('blacklight_advanced_search.form.sort_label'), :class => "control-label") %>
|
3
|
+
|
4
|
+
<%= select_tag(:sort, options_for_select(sort_fields, h(params[:sort])), :class => "form-control sort-select") %>
|
5
|
+
<%= hidden_field_tag(:search_field, blacklight_config.advanced_search[:url_key]) %>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<div class="submit-buttons pull-right">
|
9
|
+
<%= link_to t('blacklight_advanced_search.form.start_over'), advanced_search_path, :class =>"btn btn-default" %>
|
10
|
+
|
11
|
+
<%= submit_tag t('blacklight_advanced_search.form.search_btn'), :class=>'btn btn-primary advanced-search-submit', :id => "advanced-search-submit" %>
|
12
|
+
</div>
|
@@ -1,7 +1,15 @@
|
|
1
|
-
<ul>
|
1
|
+
<ul class="facet-values list-unstyled blacklight-advanced-facet-select">
|
2
2
|
<% display_facet.items.each do |item| -%>
|
3
3
|
<li>
|
4
|
-
|
4
|
+
<span class="facet-checkbox">
|
5
|
+
<%= check_box_tag "f_inclusive[#{solr_field}][]", item.value.to_sym, facet_value_checked?(solr_field, item.value), :id => "f_inclusive_#{solr_field}_#{item.value.parameterize}"%>
|
6
|
+
</span>
|
7
|
+
|
8
|
+
<span class="label-and-count">
|
9
|
+
<%= label_tag "f_inclusive_#{solr_field}_#{item.value.parameterize}" do %>
|
10
|
+
<%= render_facet_value(solr_field, item, :suppress_link => true) %>
|
11
|
+
<% end %>
|
12
|
+
<span>
|
5
13
|
</li>
|
6
14
|
<% end -%>
|
7
|
-
</ul>
|
15
|
+
</ul>
|
@@ -1,8 +1,21 @@
|
|
1
1
|
<% @page_title = "More Search Options - #{application_name}" %>
|
2
|
-
<script type="text/javascript">
|
3
|
-
$(document).ready(function(){$("form.advanced input:first").focus();});
|
4
|
-
</script>
|
5
2
|
|
6
|
-
<
|
7
|
-
|
8
|
-
|
3
|
+
<div class="advanced-search-form col-sm-12">
|
4
|
+
|
5
|
+
<h1 class="advanced page-header">
|
6
|
+
<%= t('blacklight_advanced_search.form.title') %>
|
7
|
+
<%= link_to t('blacklight_advanced_search.form.start_over'), advanced_search_path, :class =>"btn btn-default pull-right" %>
|
8
|
+
</h1>
|
9
|
+
|
10
|
+
<div class="row">
|
11
|
+
|
12
|
+
<div class="col-md-8">
|
13
|
+
<%= render 'advanced_search_form' %>
|
14
|
+
</div>
|
15
|
+
<div class="col-md-4">
|
16
|
+
<%= render "advanced_search_help" %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
</div>
|
20
|
+
|
21
|
+
</div>
|
@@ -1,9 +1,14 @@
|
|
1
1
|
<div class="advanced_facet_limit">
|
2
|
-
<div class="inclusive_or">
|
2
|
+
<div class="inclusive_or well">
|
3
3
|
<h4>Any of:</h4>
|
4
|
-
<ul>
|
4
|
+
<ul class="list-unstyled facet-values">
|
5
5
|
<% @advanced_query.filters[solr_field].each do |value| %>
|
6
|
-
<li
|
6
|
+
<li>
|
7
|
+
<span class="selected"><%= h(value) %></span>
|
8
|
+
<%= link_to(remove_advanced_facet_param(solr_field, value, params), :class => "remove") do %>
|
9
|
+
<span class="glyphicon glyphicon-remove"></span><span class="sr-only">[remove]</span>
|
10
|
+
<% end %>
|
11
|
+
</li>
|
7
12
|
<% end %>
|
8
13
|
</ul>
|
9
14
|
</div>
|
@@ -17,12 +17,16 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
|
-
|
21
|
-
s.add_dependency "blacklight", "~> 4.0"
|
20
|
+
s.add_dependency "blacklight", ">= 5.1", "< 6.0"
|
22
21
|
s.add_dependency "parslet"
|
23
22
|
|
23
|
+
s.add_development_dependency "blacklight_marc"
|
24
24
|
s.add_development_dependency "rails"
|
25
25
|
s.add_development_dependency "rspec-rails"
|
26
26
|
s.add_development_dependency 'jettywrapper', ">= 1.4.2"
|
27
|
-
|
27
|
+
|
28
|
+
# engine_cart 0.3 is out, but can't run tests if we're using it,
|
29
|
+
# not sure what we need to change to work with 0.3, possibly cbeer
|
30
|
+
# might know, in the meantime locking to 0.2.x.
|
31
|
+
s.add_development_dependency 'engine_cart', "~> 0.2.2"
|
28
32
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
en:
|
2
|
+
blacklight_advanced_search:
|
3
|
+
all: all
|
4
|
+
any: any
|
5
|
+
form:
|
6
|
+
title: More Search Options
|
7
|
+
search_context: Within search
|
8
|
+
limit_criteria_heading_html: "<strong>AND</strong> have these attributes"
|
9
|
+
query_criteria_heading_html: "Find items that match %{select_menu} of"
|
10
|
+
sort_label: "Sort results by"
|
11
|
+
start_over: "Start over"
|
12
|
+
search_btn: 'Search'
|
@@ -6,10 +6,10 @@ module BlacklightAdvancedSearch
|
|
6
6
|
autoload :ParsingNestingParser, 'blacklight_advanced_search/parsing_nesting_parser'
|
7
7
|
autoload :FilterParser, 'blacklight_advanced_search/filter_parser'
|
8
8
|
autoload :ParseBasicQ, 'blacklight_advanced_search/parse_basic_q'
|
9
|
+
autoload :RedirectLegacyParamsFilter, 'blacklight_advanced_search/redirect_legacy_params_filter'
|
9
10
|
|
10
11
|
require 'blacklight_advanced_search/version'
|
11
12
|
require 'blacklight_advanced_search/engine'
|
12
|
-
|
13
13
|
|
14
14
|
# Utility method used in our solr search logic.
|
15
15
|
# Merges new_hash into source_hash, but will recursively
|
@@ -17,22 +17,33 @@ module BlacklightAdvancedSearch
|
|
17
17
|
# or blank values from new_hash into source_hash, nil or blank values
|
18
18
|
# in new_hash will not overwrite values in source_hash.
|
19
19
|
def self.deep_merge!(source_hash, new_hash)
|
20
|
-
source_hash.merge
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
20
|
+
# We used to use built-in source_hash.merge() with a block arg
|
21
|
+
# to customize merge behavior, but that was breaking in some
|
22
|
+
# versions of BL/Rails where source_hash was a kind of HashWithIndifferentAccess,
|
23
|
+
# and hwia is unreliable in some versions of Rails. Oh well.
|
24
|
+
# https://github.com/projectblacklight/blacklight/issues/827
|
25
|
+
|
26
|
+
new_hash.each_pair do |key, new_value|
|
27
|
+
old = source_hash.fetch(key, nil)
|
28
|
+
|
29
|
+
source_hash[key] =
|
30
|
+
if new_value.respond_to?(:blank) && new.blank?
|
31
|
+
old
|
32
|
+
elsif (old.kind_of?(Hash) and new_value.kind_of?(Hash))
|
33
|
+
deep_merge!(old, new_value)
|
34
|
+
old
|
35
|
+
elsif (old.kind_of?(Array) and new_value.kind_of?(Array))
|
36
|
+
old.concat(new_value).uniq
|
37
|
+
elsif new_value.nil?
|
38
|
+
# Allowing nil values to over-write on merge messes things up.
|
39
|
+
# don't set a nil value if you really want to force blank, set
|
40
|
+
# empty string.
|
41
|
+
old
|
42
|
+
else
|
43
|
+
new_value
|
44
|
+
end
|
35
45
|
end
|
36
|
-
|
46
|
+
source_hash
|
47
|
+
end
|
37
48
|
|
38
49
|
end
|
@@ -1,34 +1,42 @@
|
|
1
1
|
module BlacklightAdvancedSearch
|
2
|
+
# Can extract query elements from rails #params query params, and then parse
|
3
|
+
# them and convert them into a solr query with #to_solr
|
4
|
+
#
|
5
|
+
# #keyword_queries and #filters, which just return extracted elements of query
|
6
|
+
# params, may also be useful in display etc.
|
2
7
|
class QueryParser
|
3
8
|
include ParsingNestingParser # only one strategy currently supported. if BlacklightAdvancedSearch.config[:solr_type] == "parsing_nesting"
|
4
9
|
include FilterParser
|
5
10
|
attr_reader :config, :params
|
6
|
-
|
11
|
+
|
7
12
|
def initialize(params,config)
|
8
13
|
@params = HashWithIndifferentAccess.new(params)
|
9
|
-
@config = config
|
14
|
+
@config = config
|
10
15
|
end
|
11
16
|
|
12
17
|
def to_solr
|
13
18
|
@to_solr ||= begin
|
14
19
|
{
|
15
|
-
:q => process_query(params,config),
|
16
|
-
:fq => generate_solr_fq()
|
20
|
+
:q => process_query(params,config),
|
21
|
+
:fq => generate_solr_fq()
|
17
22
|
}
|
18
23
|
end
|
19
24
|
end
|
20
|
-
|
25
|
+
|
21
26
|
# Returns "AND" or "OR", how #keyword_queries will be combined
|
22
27
|
def keyword_op
|
23
28
|
@params["op"] || "AND"
|
24
29
|
end
|
25
|
-
|
30
|
+
|
31
|
+
# extracts advanced-type keyword query elements from query params,
|
32
|
+
# returns as a kash of field => query.
|
33
|
+
# see also keyword_op
|
26
34
|
def keyword_queries
|
27
35
|
unless(@keyword_queries)
|
28
36
|
@keyword_queries = {}
|
29
37
|
|
30
38
|
return @keyword_queries unless @params[:search_field] == ::AdvancedController.blacklight_config.advanced_search[:url_key]
|
31
|
-
|
39
|
+
|
32
40
|
config.search_fields.each do | key, field_def |
|
33
41
|
if ! @params[ key.to_sym ].blank?
|
34
42
|
@keyword_queries[ key ] = @params[ key.to_sym ]
|
@@ -37,24 +45,27 @@ module BlacklightAdvancedSearch
|
|
37
45
|
end
|
38
46
|
return @keyword_queries
|
39
47
|
end
|
40
|
-
|
48
|
+
|
49
|
+
# extracts advanced-type filters from query params,
|
50
|
+
# returned as a hash of field => [array of values]
|
41
51
|
def filters
|
42
52
|
unless (@filters)
|
43
53
|
@filters = {}
|
44
54
|
return @filters unless @params[:f_inclusive]
|
45
|
-
@params[:f_inclusive].each_pair do |field,
|
46
|
-
|
47
|
-
|
48
|
-
@filters[field] << value
|
49
|
-
end
|
50
|
-
end
|
55
|
+
@params[:f_inclusive].each_pair do |field, value_array|
|
56
|
+
@filters[field] ||= value_array.dup
|
57
|
+
end
|
51
58
|
end
|
52
59
|
return @filters
|
53
60
|
end
|
54
61
|
|
62
|
+
def filters_include_value?(field, value)
|
63
|
+
filters[field.to_s].try {|array| array.include? value}
|
64
|
+
end
|
65
|
+
|
55
66
|
def empty?
|
56
67
|
filters.empty? && keyword_queries.empty?
|
57
68
|
end
|
58
|
-
|
69
|
+
|
59
70
|
end
|
60
71
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Returns a lambda that you can use with a before_filter in your
|
2
|
+
# CatalogController to catch and redirect query params using the old
|
3
|
+
# style, used prior to blacklight_advanced_search 5.0.
|
4
|
+
#
|
5
|
+
# This can be used to keep any old bookmarked URLs still working.
|
6
|
+
#
|
7
|
+
# before_filter BlacklightAdvancedSearch::RedirectLegacyParamsFilter, :only => :index
|
8
|
+
#
|
9
|
+
module BlacklightAdvancedSearch
|
10
|
+
class RedirectLegacyParamsFilter
|
11
|
+
|
12
|
+
def self.before(controller)
|
13
|
+
params = controller.send(:params)
|
14
|
+
|
15
|
+
if params[:f_inclusive]
|
16
|
+
legacy_converted = false
|
17
|
+
|
18
|
+
params[:f_inclusive].each_pair do |field, value|
|
19
|
+
if value.kind_of? Hash
|
20
|
+
# old style! convert!
|
21
|
+
legacy_converted = true
|
22
|
+
params[:f_inclusive][field] = value.keys
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if legacy_converted
|
27
|
+
controller.send(:redirect_to, params, :status => :moved_permanently)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|