blacklight_advanced_search 2.2.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|