blacklight_advanced_search 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -0
- data/README.rdoc +66 -42
- data/VERSION +1 -1
- data/app/controllers/advanced_controller.rb +23 -4
- data/app/controllers/blacklight_advanced_search/advanced_controller.rb +4 -6
- data/blacklight_advanced_search.gemspec +6 -0
- data/lib/blacklight_advanced_search/controller.rb +16 -83
- data/lib/blacklight_advanced_search/engine.rb +3 -0
- data/lib/blacklight_advanced_search/parse_basic_q.rb +56 -0
- data/lib/blacklight_advanced_search.rb +26 -0
- data/lib/generators/blacklight_advanced_search/blacklight_advanced_search_generator.rb +2 -5
- data/spec/acceptance/blacklight_advanced_search_form_spec.rb +19 -0
- data/spec/integration/blacklight_stub_spec.rb +10 -0
- data/spec/internal/app/controllers/application_controller.rb +4 -0
- data/spec/internal/app/models/solr_document.rb +3 -0
- data/spec/internal/config/database.yml +3 -0
- data/spec/internal/config/routes.rb +6 -0
- data/spec/internal/config/solr.yml +18 -0
- data/spec/internal/db/combustion_test.sqlite +0 -0
- data/spec/internal/db/schema.rb +53 -0
- data/spec/internal/log/.gitignore +1 -0
- data/spec/internal/public/favicon.ico +0 -0
- data/spec/spec_helper.rb +19 -3
- metadata +88 -11
- data/spec/support/blacklight_mock.rb +0 -5
data/Gemfile
ADDED
data/README.rdoc
CHANGED
@@ -2,8 +2,9 @@ This is an advanced search plugin for Blacklight ( http://www.projectblacklight.
|
|
2
2
|
|
3
3
|
== Pre-requisites:
|
4
4
|
* The Blacklight plugin ( http://github.com/projectblacklight/blacklight )
|
5
|
-
* NOTE: Blacklight 3.2 is required for current version of Advanced Search plugin
|
6
|
-
* 1.
|
5
|
+
* NOTE: Blacklight 3.2.2+ is required for current (1.2.1+) version of Advanced Search plugin, and likewise BL 3.2.x requires current 1.2.1+ version of Advanced Search.
|
6
|
+
* 1.2.0 was an aborted version of Blacklight 3.2 compatibility, don't use it, if you did, re-do your configuration according to current instructions and delete any local ./app/controllers/advanced_controller.rb file.
|
7
|
+
* 1.1.x versions will work with older version of Blacklight 3.0.x-3.1.x
|
7
8
|
* advanced search plugin latest 0.X.X version will work with Blacklight 2.9/Rails2.
|
8
9
|
* Older tagged versions of Advanced Search may work with even older BL.
|
9
10
|
|
@@ -12,20 +13,20 @@ This is an advanced search plugin for Blacklight ( http://www.projectblacklight.
|
|
12
13
|
=== Blacklight 3.x/Rails 3
|
13
14
|
|
14
15
|
Add to your application's Gemfile:
|
16
|
+
|
15
17
|
gem "blacklight_advanced_search"
|
16
18
|
|
17
19
|
then run 'bundle install'. Then run:
|
20
|
+
|
18
21
|
rails generate blacklight_advanced_search
|
19
22
|
|
20
|
-
* The 'generate' command will install the plugin's assets into your application's application.js/application.css asset pipeline files
|
21
|
-
* It will also generate AdvancedController into app/controllers/advanced_controller.rb, which you should configure for your local needs.
|
23
|
+
* The 'generate' command will install 'require' statements for the plugin's assets into your application's application.js/application.css asset pipeline files
|
22
24
|
* And it can optionally install a localized search form with a link to advanced search. If you've already localized your search form you'll want to do this manually instead.
|
23
25
|
|
24
26
|
You may want to set `blacklight_config.advanced_search.advanced_parse_q = true` to enable AND/OR/NOT parsing even in ordinary search, this is not on by default.
|
25
27
|
|
26
28
|
== Accessing
|
27
29
|
|
28
|
-
|
29
30
|
The advanced search form will be available in your app at /advanced
|
30
31
|
|
31
32
|
url_for(:controller => "advanced", :action => "index")
|
@@ -33,11 +34,7 @@ The advanced search form will be available in your app at /advanced
|
|
33
34
|
You can also send the advanced search form url parameters representing a search, to have the form add on additional 'advanced' criteria to the search. For example:
|
34
35
|
|
35
36
|
url_for( params.merge(:controller => "advanced", :action => "index")
|
36
|
-
|
37
|
-
If you do not have the default :controller/:action route enabled for your application, you may need to add a custom route to config/routes.rb. For example:
|
38
|
-
|
39
|
-
map.advanced 'advanced', :controller => 'advanced', :action => 'index'
|
40
|
-
|
37
|
+
|
41
38
|
By default there won't be any links in your application to the search form. If you've heavily customized your app, you can put them wherever you want as above.
|
42
39
|
|
43
40
|
However, especially if your app isn't much customized, the optional installer can write a localized Blacklight search form into your application with a 'more options' link to advanced. You may need to adjust your styles to make room for the new link, depending on what you've done with your app.
|
@@ -46,47 +43,67 @@ However, especially if your app isn't much customized, the optional installer ca
|
|
46
43
|
|
47
44
|
== Configuration:
|
48
45
|
|
49
|
-
If your application uses a single Solr qt request handler for all its search fields, then this plugin may work well with no configuration. Nonetheless, configuration is available to change or improve behavior, or to use a separate Solr request handler for the advanced search plugin.
|
46
|
+
If your application uses a single Solr qt request handler for all its search fields, then this plugin may work well with no configuration. Nonetheless, configuration is available to change or improve behavior, or to use a separate Solr request handler for the advanced search plugin. Most configuration takes place in your ./app/controllers/catalog_controller.rb `configure_blacklight` block.
|
50
47
|
|
51
|
-
All plugin configuration mentioned below can be in any initializer in your app (any ruby file in config/initializers), although using the convention config/initializers/blacklight_advanced_search.rb may keep things clear.
|
52
|
-
|
53
|
-
The optional installer script can install a sample blacklight_advanced_search.rb for you which demonstrates various options.
|
54
48
|
|
55
49
|
=== Expression parsing in ordinary search
|
56
50
|
|
57
|
-
|
51
|
+
Turn this feature on by adding to your CatalogController definition:
|
52
|
+
|
53
|
+
include BlacklightAdvancedSearch::ParseBasicQ
|
54
|
+
|
55
|
+
This will intercept queries entered in ordinary Blacklight search interface, and parse them for AND/OR/NOT (and parens), producing appropriate Solr query. This allows single-field boolean expressions to be entered in ordinary search, providing a consistent experience with advanced search.
|
56
|
+
|
57
|
+
Individual blacklight 'search fields' can have advanced parsing turned off:
|
58
|
+
|
59
|
+
config.add_search_field("something") do |field|
|
60
|
+
# ...
|
61
|
+
field.advanced_parse = false
|
62
|
+
end
|
63
|
+
|
58
64
|
|
59
65
|
When this feature is turned on, queries that don't have any special operators (eg: AND, OR, NOT, parens) will still be passed to Solr much the same as they were before. But queries with special operators will have appropriate Solr queries generated for them, usually including nested "_query_" elements, to have the same meaning they would in advanced search. If a user enters something that is a syntax error for parsed search (for instance an unclosed phrase quote or paren), the logic will rescue from the parse erorr by running the exact user entered query direct to solr without advanced parsing.
|
60
66
|
|
61
|
-
Due to limitations of the logic, sometimes these generated Solr queries may not really be as simple as they could be, they may include a *single* nested _query_, which really doens't need to be a nested query at all, although it will still work fine.
|
67
|
+
Due to limitations of the logic, sometimes these generated Solr queries may not really be as simple as they could be, they may include a *single* nested _query_, which really doens't need to be a nested query at all, although it will still work fine.
|
68
|
+
|
69
|
+
If there'a a parse error trying to parse as boolean expression, it'll give up and pass the query on through same as it would without ParseBasicQ.
|
62
70
|
|
63
71
|
=== Search fields
|
64
72
|
|
65
|
-
Your main blacklight search fields are generally defined in
|
73
|
+
Your main blacklight search fields are generally defined in CatalogController blacklight_config using `add_search_field`. They are all by default used in advanced search.
|
66
74
|
|
67
|
-
|
68
|
-
|
69
|
-
|
75
|
+
If there are particular search fields in your main blacklight config you want excluded from the advanced search form, you can set ":include_in_advanced_search => false"
|
76
|
+
|
77
|
+
config.add_search_field("isbn") do |field|
|
78
|
+
field.include_in_advanced_search = false
|
79
|
+
field.solr_parameters = { :qf => "isbn_t" }
|
80
|
+
end
|
70
81
|
|
71
|
-
If you
|
82
|
+
If you want search fields ONLY available in advanced search, you can suppress them from ordinary search:
|
72
83
|
|
73
|
-
|
84
|
+
config.add_search_field("publisher") do |field|
|
85
|
+
field.include_in_simple_select = false
|
86
|
+
field.solr_parameters = { :qf => "publisher_t" }
|
87
|
+
end
|
74
88
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
:
|
89
|
+
All advanced search fields must share the same Solr request handler (":qt"). As such, search fields that use a custom ":qt" parameter may not be re-used by the advanced search plugin. However, you may use a separate Solr request handler than the Blacklight default. If you would like the advanced search to use a different Solr request handler than your app's default, set:
|
90
|
+
|
91
|
+
config.advanced_search = {
|
92
|
+
:qt => 'advanced'
|
79
93
|
}
|
80
|
-
|
94
|
+
|
95
|
+
If you use a separate Solr request handler for advanced search, you must supply a completely separate list of search fields for the advanced search form, just define them with `:include_in_advanced_search => true` and `:include_in_simple_search => false` as above.
|
81
96
|
|
82
97
|
Additionally, to make your advanced search solr requests more concise, you are strongly encouraged to take advantage of the :local_solr_parameters option in your search field definition to use a solr parameter substitution with $variables.
|
83
98
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
99
|
+
configure_blacklight do |config|
|
100
|
+
config.add_search_field('author') do |field|
|
101
|
+
field.solr_local_parameters = {
|
102
|
+
:qf=>"$qf_author",
|
103
|
+
:pf=>"$pf_author"
|
104
|
+
}
|
105
|
+
end
|
106
|
+
end
|
90
107
|
|
91
108
|
Within your solrconfig.xml you may then provide the appropriate custom configuration.
|
92
109
|
|
@@ -117,23 +134,26 @@ Within your solrconfig.xml you may then provide the appropriate custom configura
|
|
117
134
|
|
118
135
|
By default, the advanced search form will show as limits whatever facets are configured as default in your Solr request handler. To have the advanced search form request specific facets and/or specific facet parameters, you can set config[:form_solr_parameters].
|
119
136
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
137
|
+
config.advanced_search => {
|
138
|
+
:form_solr_parameters => {
|
139
|
+
"facet.field" => ["format", "language_facet"],
|
140
|
+
"facet.limit" => -1, # return all facet values
|
141
|
+
"facet.sort" => "index" # sort by byte order of values
|
142
|
+
}
|
143
|
+
}
|
125
144
|
|
126
145
|
|
127
146
|
=== Advanced Search Config Options
|
128
147
|
|
148
|
+
Complete list of keys that can be supplied inside your configure_blacklight `advanced_search` block:
|
149
|
+
|
129
150
|
[:qt]
|
130
151
|
Solr request handler to use for any search that includes advanced search criteria. Defaults to what the application has set as Blacklight.config[:default_qt]
|
131
152
|
[:url_key]
|
132
153
|
Key to use in application URLs to indicate advanced search is included in a query, defaults to "advanced". URLs will have "&search_field=[url key]".
|
133
154
|
[:form_solr_paramters]
|
134
155
|
A hash of solr parameters which will be included in Solr request sent before display of advanced search form. Can be used to set facet parameters for advanced search form display.
|
135
|
-
|
136
|
-
Set to 'true' to have AND/OR/NOT parsed even in ordinary 'simple' blacklight search, and converted to appropriate Solr query for that single field.
|
156
|
+
|
137
157
|
|
138
158
|
== Translation to Solr Query, technical details
|
139
159
|
|
@@ -147,12 +167,16 @@ You may also find the rspecs for parsing a user-entered query and converting it
|
|
147
167
|
|
148
168
|
== Running tests
|
149
169
|
|
150
|
-
|
151
|
-
|
170
|
+
(experimental, in progress).
|
171
|
+
|
172
|
+
(Limited) test coverage is provided with rspec, run all tests by running:
|
173
|
+
rspec
|
152
174
|
|
175
|
+
from the blacklight_advanced_search directory. you may need to `bundle install` first.
|
153
176
|
|
154
177
|
== To Do
|
155
178
|
|
156
179
|
* Alphabetical sorting of facet values returned by solr in count order (perhaps with limit).
|
180
|
+
* Parse for fielded search too like << title:"some title" >>. 'field:' that doesn't match an actual known field should be passed through same as now, while recognized fields should generate nested queries tied to those blacklight search types.
|
157
181
|
|
158
182
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.2.
|
1
|
+
1.2.1
|
@@ -1,8 +1,27 @@
|
|
1
|
+
# This class should NOT be generated into local app. If you generated
|
2
|
+
# into local app in a previous version, remove that, config is done
|
3
|
+
# in CatalogController now.
|
4
|
+
#
|
5
|
+
# Note that this NEEDS to sub-class CatalogController, so it gets any
|
6
|
+
# custom searching behavior you've added, and uses when fetching facets
|
7
|
+
# etc. It does that right now because BlacklightAdvancedSearch::AdvancedController
|
8
|
+
# is hard-coded to subclass CatalogController.
|
9
|
+
#
|
10
|
+
# TODO:
|
11
|
+
# This seperate controller may not need to exist at all -- it just exists
|
12
|
+
# to provide the advanced search form (and fetching of facets to display
|
13
|
+
# on that form). Instead, mix-in a new "advanced" action to CatalogController?
|
14
|
+
# (Make a backwards compat route though).
|
15
|
+
#
|
16
|
+
# Alternately, if this does exist as a seperate controller, it should
|
17
|
+
# _directly_ < CatalogController, and BlacklightAdvancedSearch::AdvancedController
|
18
|
+
# should be a mix-in that does not assume parent controller. Then, if you have
|
19
|
+
# multi-controllers, you just need to create new `AdvancedControllerForX < XController`
|
20
|
+
# which still mixes in BlacklightAdvancedSearch::AdvancedController. There
|
21
|
+
# are probably some other edges that need to be smoothed for that approach, but
|
22
|
+
# that'd be the direction.
|
1
23
|
class AdvancedController < BlacklightAdvancedSearch::AdvancedController
|
2
24
|
|
3
|
-
|
4
|
-
config.advanced_search.form_solr_parameters ||= {}
|
5
|
-
config.advanced_search.url_key ||= 'advanced'
|
6
|
-
end
|
25
|
+
copy_blacklight_config_from(CatalogController)
|
7
26
|
|
8
27
|
end
|
@@ -3,10 +3,7 @@
|
|
3
3
|
class BlacklightAdvancedSearch::AdvancedController < CatalogController
|
4
4
|
include AdvancedHelper # so we get the #advanced_search_context method
|
5
5
|
|
6
|
-
|
7
|
-
config.advanced_search ||= Blacklight::OpenStructWithHashAccess.new
|
8
|
-
end
|
9
|
-
|
6
|
+
|
10
7
|
def index
|
11
8
|
unless request.method==:post
|
12
9
|
@response = get_advanced_search_facets
|
@@ -48,8 +45,9 @@ class BlacklightAdvancedSearch::AdvancedController < CatalogController
|
|
48
45
|
|
49
46
|
input = HashWithIndifferentAccess.new
|
50
47
|
input.merge!( search_context_params )
|
51
|
-
|
52
|
-
input.merge!( blacklight_config.advanced_search[:
|
48
|
+
|
49
|
+
input.merge!( :qt => blacklight_config.advanced_search[:qt] || blacklight_config.default_qt , :per_page => 0)
|
50
|
+
input.merge!( blacklight_config.advanced_search[:form_solr_parameters] ) if blacklight_config.advanced_search[:form_solr_parameters]
|
53
51
|
input[:q] ||= '{!lucene}*:*'
|
54
52
|
|
55
53
|
|
@@ -21,4 +21,10 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_dependency "rails", "~> 3.0"
|
22
22
|
s.add_dependency "blacklight", "~> 3.2.1"
|
23
23
|
s.add_dependency "parslet"
|
24
|
+
|
25
|
+
s.add_development_dependency "rspec"
|
26
|
+
s.add_development_dependency "rspec-rails"
|
27
|
+
s.add_development_dependency "capybara"
|
28
|
+
s.add_development_dependency "sqlite3"
|
29
|
+
s.add_development_dependency 'launchy'
|
24
30
|
end
|
@@ -6,15 +6,16 @@ module BlacklightAdvancedSearch::Controller
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
# default adv config values
|
10
|
+
self.blacklight_config.advanced_search ||= Blacklight::OpenStructWithHashAccess.new
|
11
|
+
self.blacklight_config.advanced_search[:url_key] ||= 'advanced'
|
12
|
+
self.blacklight_config.advanced_search[:form_solr_parameters] ||= {}
|
13
|
+
|
13
14
|
|
14
|
-
#
|
15
|
+
# Parse app URL params used for adv searches
|
15
16
|
solr_search_params_logic << :add_advanced_search_to_solr
|
16
17
|
|
17
|
-
|
18
|
+
# Display advanced search constraints properly
|
18
19
|
helper BlacklightAdvancedSearch::RenderConstraintsOverride
|
19
20
|
helper BlacklightAdvancedSearch::CatalogHelperOverride
|
20
21
|
end
|
@@ -28,91 +29,23 @@ module BlacklightAdvancedSearch::Controller
|
|
28
29
|
# If we've got the hint that we're doing an 'advanced' search, then
|
29
30
|
# map that to solr #q, over-riding whatever some other logic may have set, yeah.
|
30
31
|
# the hint right now is :search_field request param is set to a magic
|
31
|
-
# key.
|
32
|
-
if (req_params[:search_field] ==
|
33
|
-
|
32
|
+
# key. OR of :f_inclusive is set for advanced params, we need processing too.
|
33
|
+
if ( (req_params[:search_field] == self.blacklight_config.advanced_search[:url_key]) ||
|
34
|
+
req_params[:f_inclusive] )
|
34
35
|
# Set this as a controller instance variable, not sure if some views/helpers depend on it. Better to leave it as a local variable
|
35
36
|
# if not, more investigation later.
|
36
|
-
@advanced_query = BlacklightAdvancedSearch::QueryParser.new(req_params,
|
37
|
-
deep_merge!(solr_parameters, @advanced_query.to_solr )
|
37
|
+
@advanced_query = BlacklightAdvancedSearch::QueryParser.new(req_params, self.blacklight_config )
|
38
|
+
BlacklightAdvancedSearch.deep_merge!(solr_parameters, @advanced_query.to_solr )
|
38
39
|
if @advanced_query.keyword_queries.length > 0
|
39
|
-
# force :qt if set
|
40
|
-
|
40
|
+
# force :qt if set, fine if it's nil, we'll use whatever CatalogController
|
41
|
+
# ordinarily uses.
|
42
|
+
solr_parameters[:qt] = self.blacklight_config.advanced_search[:qt]
|
41
43
|
solr_parameters[:defType] = "lucene"
|
42
44
|
end
|
43
45
|
|
44
46
|
end
|
45
47
|
end
|
46
|
-
|
47
|
-
# This method can be included in solr_search_params_logic to have us
|
48
|
-
# parse an ordinary entered :q for AND/OR/NOT and produce appropriate
|
49
|
-
# Solr query. Note that it is NOT included in solr_search_params_logic
|
50
|
-
# by default when this module is included, because it is optional behavior.
|
51
|
-
# BlacklightAdvancedSearch init code will add it to CatalogController
|
52
|
-
# if it's configured to do so. You can of course add it yourself
|
53
|
-
# manually too.
|
54
|
-
#
|
55
|
-
# Note: For syntactically invalid input, we'll just skip the adv
|
56
|
-
# parse and send it straight to solr same as if advanced_parse_q
|
57
|
-
# were not being used.
|
58
|
-
def add_advanced_parse_q_to_solr(solr_parameters, req_params = params)
|
59
|
-
unless req_params[:q].blank?
|
60
|
-
field_def = search_field_def_for_key( req_params[:search_field]) ||
|
61
|
-
default_search_field
|
62
|
-
|
63
|
-
|
64
|
-
# If the individual field has advanced_parse_q suppressed, punt
|
65
|
-
return if field_def[:advanced_parse_q] == false
|
66
|
-
|
67
|
-
solr_direct_params = field_def[:solr_parameters] || {}
|
68
|
-
solr_local_params = field_def[:solr_local_parameters] || {}
|
69
|
-
|
70
|
-
# See if we can parse it, if we can't, we're going to give up
|
71
|
-
# and just allow basic search, perhaps with a warning.
|
72
|
-
begin
|
73
|
-
adv_search_params = ParsingNesting::Tree.parse(req_params[:q]).to_single_query_params( solr_local_params )
|
74
|
-
|
75
|
-
deep_merge!(solr_parameters, solr_direct_params)
|
76
|
-
|
77
|
-
deep_merge!(
|
78
|
-
solr_parameters,
|
79
|
-
adv_search_params
|
80
|
-
)
|
81
|
-
rescue Parslet::UnconsumedInput => e
|
82
|
-
# do nothing, don't merge our input in, keep basic search
|
83
|
-
# optional TODO, display error message in flash here, but hard to
|
84
|
-
# display a good one.
|
85
|
-
return
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
|
91
|
-
protected
|
92
|
-
# Merges new_hash into source_hash, without modifying arguments, but
|
93
|
-
# will merge nested arrays and hashes too. Also will NOT merge nil or blank
|
94
|
-
# from new_hash into old_hash
|
95
|
-
def deep_merge!(source_hash, new_hash)
|
96
|
-
source_hash.merge!(new_hash) do |key, old, new|
|
97
|
-
if new.respond_to?(:blank) && new.blank?
|
98
|
-
old
|
99
|
-
elsif (old.kind_of?(Hash) and new.kind_of?(Hash))
|
100
|
-
deep_merge!(old, new)
|
101
|
-
elsif (old.kind_of?(Array) and new.kind_of?(Array))
|
102
|
-
old.concat(new).uniq
|
103
|
-
elsif new.nil?
|
104
|
-
# Allowing nil values to over-write on merge messes things up.
|
105
|
-
# don't set a nil value if you really want to force blank, set
|
106
|
-
# empty string.
|
107
|
-
old
|
108
|
-
else
|
109
|
-
new
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
|
115
|
-
|
48
|
+
|
116
49
|
|
117
50
|
end
|
118
51
|
|
@@ -11,6 +11,9 @@ module BlacklightAdvancedSearch
|
|
11
11
|
# dev mode, if they are in the BL plugin and haven't been copied to
|
12
12
|
# local, they won't be. But we do our best.
|
13
13
|
config.to_prepare do
|
14
|
+
|
15
|
+
|
16
|
+
|
14
17
|
# Ordinary module over-ride to CatalogController
|
15
18
|
CatalogController.send(:include,
|
16
19
|
BlacklightAdvancedSearch::Controller
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# A solr search logic mix-in to CatalogController.
|
2
|
+
# If mixed-in, adds Advanced Search parsing behavior
|
3
|
+
# to queries entered on basic/standard/simple search
|
4
|
+
# form too, for boolean expressions.
|
5
|
+
#
|
6
|
+
# simply:
|
7
|
+
# include BlacklightAdvancedSearch::ParseBasicQ
|
8
|
+
# in your CatalogController
|
9
|
+
module BlacklightAdvancedSearch::ParseBasicQ
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do
|
13
|
+
solr_search_params_logic << :add_advanced_parse_q_to_solr
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# This method can be included in solr_search_params_logic to have us
|
18
|
+
# parse an ordinary entered :q for AND/OR/NOT and produce appropriate
|
19
|
+
# Solr query. Note that it is NOT included in solr_search_params_logic
|
20
|
+
# by default when this module is included, because it is optional behavior.
|
21
|
+
# BlacklightAdvancedSearch init code will add it to CatalogController
|
22
|
+
# if it's configured to do so. You can of course add it yourself
|
23
|
+
# manually too.
|
24
|
+
#
|
25
|
+
# Note: For syntactically invalid input, we'll just skip the adv
|
26
|
+
# parse and send it straight to solr same as if advanced_parse_q
|
27
|
+
# were not being used.
|
28
|
+
def add_advanced_parse_q_to_solr(solr_parameters, req_params = params)
|
29
|
+
unless req_params[:q].blank?
|
30
|
+
field_def = search_field_def_for_key( req_params[:search_field]) ||
|
31
|
+
default_search_field
|
32
|
+
|
33
|
+
|
34
|
+
# If the individual field has advanced_parse_q suppressed, punt
|
35
|
+
return if field_def[:advanced_parse] == false
|
36
|
+
|
37
|
+
solr_direct_params = field_def[:solr_parameters] || {}
|
38
|
+
solr_local_params = field_def[:solr_local_parameters] || {}
|
39
|
+
|
40
|
+
# See if we can parse it, if we can't, we're going to give up
|
41
|
+
# and just allow basic search, perhaps with a warning.
|
42
|
+
begin
|
43
|
+
adv_search_params = ParsingNesting::Tree.parse(req_params[:q]).to_single_query_params( solr_local_params )
|
44
|
+
|
45
|
+
BlacklightAdvancedSearch.deep_merge!(solr_parameters, solr_direct_params)
|
46
|
+
BlacklightAdvancedSearch.deep_merge!(solr_parameters, adv_search_params)
|
47
|
+
rescue Parslet::UnconsumedInput => e
|
48
|
+
# do nothing, don't merge our input in, keep basic search
|
49
|
+
# optional TODO, display error message in flash here, but hard to
|
50
|
+
# display a good one.
|
51
|
+
return
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -5,8 +5,34 @@ module BlacklightAdvancedSearch
|
|
5
5
|
autoload :QueryParser, 'blacklight_advanced_search/advanced_query_parser'
|
6
6
|
autoload :ParsingNestingParser, 'blacklight_advanced_search/parsing_nesting_parser'
|
7
7
|
autoload :FilterParser, 'blacklight_advanced_search/filter_parser'
|
8
|
+
autoload :ParseBasicQ, 'blacklight_advanced_search/parse_basic_q'
|
8
9
|
|
9
10
|
require 'blacklight_advanced_search/version'
|
10
11
|
require 'blacklight_advanced_search/engine'
|
11
12
|
|
13
|
+
|
14
|
+
# Utility method used in our solr search logic.
|
15
|
+
# Merges new_hash into source_hash, but will recursively
|
16
|
+
# merge nested arrays and hashes too; also will NOT merge nil
|
17
|
+
# or blank values from new_hash into source_hash, nil or blank values
|
18
|
+
# in new_hash will not overwrite values in source_hash.
|
19
|
+
def self.deep_merge!(source_hash, new_hash)
|
20
|
+
source_hash.merge!(new_hash) do |key, old, new|
|
21
|
+
if new.respond_to?(:blank) && new.blank?
|
22
|
+
old
|
23
|
+
elsif (old.kind_of?(Hash) and new.kind_of?(Hash))
|
24
|
+
deep_merge!(old, new)
|
25
|
+
elsif (old.kind_of?(Array) and new.kind_of?(Array))
|
26
|
+
old.concat(new).uniq
|
27
|
+
elsif new.nil?
|
28
|
+
# Allowing nil values to over-write on merge messes things up.
|
29
|
+
# don't set a nil value if you really want to force blank, set
|
30
|
+
# empty string.
|
31
|
+
old
|
32
|
+
else
|
33
|
+
new
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
12
38
|
end
|
@@ -3,14 +3,11 @@ require 'rails/generators'
|
|
3
3
|
class BlacklightAdvancedSearchGenerator < Rails::Generators::Base
|
4
4
|
source_root File.expand_path('../templates', __FILE__)
|
5
5
|
|
6
|
-
require File.expand_path('../assets_generator.rb', __FILE__)
|
7
|
-
def
|
6
|
+
require File.expand_path('../assets_generator.rb', __FILE__)
|
7
|
+
def inject_asset_requires
|
8
8
|
BlacklightAdvancedSearch::AssetsGenerator.start
|
9
9
|
end
|
10
10
|
|
11
|
-
def install_local_controller
|
12
|
-
copy_file("advanced_controller.rb", "app/controllers/advanced_controller.rb")
|
13
|
-
end
|
14
11
|
|
15
12
|
def install_localized_search_form
|
16
13
|
if options[:force] or yes?("Install local search form with advanced link?")
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Blacklight Advanced Search Form" do
|
4
|
+
before do
|
5
|
+
AdvancedController.blacklight_config = Blacklight::Configuration.new
|
6
|
+
AdvancedController.configure_blacklight do |config|
|
7
|
+
|
8
|
+
config.add_search_field('title') do |field|
|
9
|
+
field.solr_local_parameters = { :qf => "title_t", :pf => "title_t"}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should show the search fields" do
|
15
|
+
visit '/advanced'
|
16
|
+
page.should have_selector('input#title')
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Blacklight Test Application' do
|
4
|
+
it "should have a Blacklight module" do
|
5
|
+
Blacklight.should be_a_kind_of(Module)
|
6
|
+
end
|
7
|
+
it "should have a Catalog controller" do
|
8
|
+
CatalogController.blacklight_config.should be_a_kind_of(Blacklight::Configuration)
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# = jetty_path key
|
2
|
+
# each environment can have a jetty_path with absolute or relative
|
3
|
+
# (to app root) path to a jetty/solr install. This is used
|
4
|
+
# by the rake tasks that start up solr automatically for testing
|
5
|
+
# and by rake solr:marc:index.
|
6
|
+
#
|
7
|
+
# jetty_path is not used by a running Blacklight application
|
8
|
+
# at all. In general you do NOT need to deploy solr in Jetty, you can deploy it
|
9
|
+
# however you want.
|
10
|
+
# jetty_path is only required for rake tasks that need to know
|
11
|
+
# how to start up solr, generally for automated testing.
|
12
|
+
|
13
|
+
development:
|
14
|
+
url: http://127.0.0.1:8983/solr
|
15
|
+
test: &test
|
16
|
+
url: http://127.0.0.1:8983/solr
|
17
|
+
cucumber:
|
18
|
+
<<: *test
|
Binary file
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# This file is auto-generated from the current state of the database. Instead
|
3
|
+
# of editing this file, please use the migrations feature of Active Record to
|
4
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
5
|
+
#
|
6
|
+
# Note that this schema.rb definition is the authoritative source for your
|
7
|
+
# database schema. If you need to create the application database on another
|
8
|
+
# system, you should be using db:schema:load, not running all the migrations
|
9
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
10
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
11
|
+
#
|
12
|
+
# It's strongly recommended to check this file into your version control system.
|
13
|
+
|
14
|
+
ActiveRecord::Schema.define(:version => 20111123152341) do
|
15
|
+
|
16
|
+
create_table "bookmarks", :force => true do |t|
|
17
|
+
t.integer "user_id", :null => false
|
18
|
+
t.string "document_id"
|
19
|
+
t.string "title"
|
20
|
+
t.datetime "created_at"
|
21
|
+
t.datetime "updated_at"
|
22
|
+
t.string "user_type"
|
23
|
+
end
|
24
|
+
|
25
|
+
create_table "searches", :force => true do |t|
|
26
|
+
t.text "query_params"
|
27
|
+
t.integer "user_id"
|
28
|
+
t.datetime "created_at"
|
29
|
+
t.datetime "updated_at"
|
30
|
+
t.string "user_type"
|
31
|
+
end
|
32
|
+
|
33
|
+
add_index "searches", ["user_id"], :name => "index_searches_on_user_id"
|
34
|
+
|
35
|
+
create_table "users", :force => true do |t|
|
36
|
+
t.string "email", :default => "", :null => false
|
37
|
+
t.string "encrypted_password", :limit => 128, :default => "", :null => false
|
38
|
+
t.string "reset_password_token"
|
39
|
+
t.datetime "reset_password_sent_at"
|
40
|
+
t.datetime "remember_created_at"
|
41
|
+
t.integer "sign_in_count", :default => 0
|
42
|
+
t.datetime "current_sign_in_at"
|
43
|
+
t.datetime "last_sign_in_at"
|
44
|
+
t.string "current_sign_in_ip"
|
45
|
+
t.string "last_sign_in_ip"
|
46
|
+
t.datetime "created_at"
|
47
|
+
t.datetime "updated_at"
|
48
|
+
end
|
49
|
+
|
50
|
+
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
|
51
|
+
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
*.log
|
File without changes
|
data/spec/spec_helper.rb
CHANGED
@@ -1,7 +1,23 @@
|
|
1
|
-
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
Bundler.require :default, :development
|
5
|
+
|
6
|
+
require 'blacklight/engine'
|
7
|
+
require 'rsolr'
|
8
|
+
require 'rsolr-ext'
|
9
|
+
require 'capybara/rspec'
|
10
|
+
Combustion.initialize!
|
11
|
+
|
12
|
+
Blacklight.solr_config = { :url => 'http://127.0.0.1:8983/solr' }
|
13
|
+
|
14
|
+
class SolrDocument
|
15
|
+
include Blacklight::Solr::Document
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'rspec/rails'
|
19
|
+
require 'capybara/rails'
|
2
20
|
|
3
|
-
Dir[Pathname.new(File.expand_path("../support/**/*.rb", __FILE__))].each {|f| require f}
|
4
|
-
require 'lib/blacklight_advanced_search'
|
5
21
|
|
6
22
|
RSpec.configure do |config|
|
7
23
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blacklight_advanced_search
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2012-03-15 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
17
|
-
requirement: &
|
17
|
+
requirement: &2164715840 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '3.0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *2164715840
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: blacklight
|
28
|
-
requirement: &
|
28
|
+
requirement: &2164712380 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ~>
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: 3.2.1
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *2164712380
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: parslet
|
39
|
-
requirement: &
|
39
|
+
requirement: &2164711400 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,7 +44,62 @@ dependencies:
|
|
44
44
|
version: '0'
|
45
45
|
type: :runtime
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *2164711400
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rspec
|
50
|
+
requirement: &2164706680 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *2164706680
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: rspec-rails
|
61
|
+
requirement: &2164705220 !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
type: :development
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: *2164705220
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: capybara
|
72
|
+
requirement: &2164703840 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: *2164703840
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: sqlite3
|
83
|
+
requirement: &2164702960 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: *2164702960
|
92
|
+
- !ruby/object:Gem::Dependency
|
93
|
+
name: launchy
|
94
|
+
requirement: &2164701540 !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
type: :development
|
101
|
+
prerelease: false
|
102
|
+
version_requirements: *2164701540
|
48
103
|
description:
|
49
104
|
email:
|
50
105
|
- blacklight-development@googlegroups.com
|
@@ -53,6 +108,7 @@ extensions: []
|
|
53
108
|
extra_rdoc_files: []
|
54
109
|
files:
|
55
110
|
- .gitignore
|
111
|
+
- Gemfile
|
56
112
|
- LICENSE
|
57
113
|
- README.rdoc
|
58
114
|
- Rakefile
|
@@ -82,6 +138,7 @@ files:
|
|
82
138
|
- lib/blacklight_advanced_search/controller.rb
|
83
139
|
- lib/blacklight_advanced_search/engine.rb
|
84
140
|
- lib/blacklight_advanced_search/filter_parser.rb
|
141
|
+
- lib/blacklight_advanced_search/parse_basic_q.rb
|
85
142
|
- lib/blacklight_advanced_search/parsing_nesting_parser.rb
|
86
143
|
- lib/blacklight_advanced_search/render_constraints_override.rb
|
87
144
|
- lib/blacklight_advanced_search/version.rb
|
@@ -92,6 +149,17 @@ files:
|
|
92
149
|
- lib/parsing_nesting/Readme.rdoc
|
93
150
|
- lib/parsing_nesting/grammar.rb
|
94
151
|
- lib/parsing_nesting/tree.rb
|
152
|
+
- spec/acceptance/blacklight_advanced_search_form_spec.rb
|
153
|
+
- spec/integration/blacklight_stub_spec.rb
|
154
|
+
- spec/internal/app/controllers/application_controller.rb
|
155
|
+
- spec/internal/app/models/solr_document.rb
|
156
|
+
- spec/internal/config/database.yml
|
157
|
+
- spec/internal/config/routes.rb
|
158
|
+
- spec/internal/config/solr.yml
|
159
|
+
- spec/internal/db/combustion_test.sqlite
|
160
|
+
- spec/internal/db/schema.rb
|
161
|
+
- spec/internal/log/.gitignore
|
162
|
+
- spec/internal/public/favicon.ico
|
95
163
|
- spec/lib/filter_parser_spec.rb
|
96
164
|
- spec/parsing_nesting/build_tree_spec.rb
|
97
165
|
- spec/parsing_nesting/consuming_spec.rb
|
@@ -99,7 +167,6 @@ files:
|
|
99
167
|
- spec/rcov.opts
|
100
168
|
- spec/spec.opts
|
101
169
|
- spec/spec_helper.rb
|
102
|
-
- spec/support/blacklight_mock.rb
|
103
170
|
- uninstall.rb
|
104
171
|
homepage: http://projectblacklight.org/
|
105
172
|
licenses: []
|
@@ -121,11 +188,22 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
188
|
version: '0'
|
122
189
|
requirements: []
|
123
190
|
rubyforge_project: blacklight
|
124
|
-
rubygems_version: 1.8.
|
191
|
+
rubygems_version: 1.8.15
|
125
192
|
signing_key:
|
126
193
|
specification_version: 3
|
127
194
|
summary: Blacklight Advanced Search plugin
|
128
195
|
test_files:
|
196
|
+
- spec/acceptance/blacklight_advanced_search_form_spec.rb
|
197
|
+
- spec/integration/blacklight_stub_spec.rb
|
198
|
+
- spec/internal/app/controllers/application_controller.rb
|
199
|
+
- spec/internal/app/models/solr_document.rb
|
200
|
+
- spec/internal/config/database.yml
|
201
|
+
- spec/internal/config/routes.rb
|
202
|
+
- spec/internal/config/solr.yml
|
203
|
+
- spec/internal/db/combustion_test.sqlite
|
204
|
+
- spec/internal/db/schema.rb
|
205
|
+
- spec/internal/log/.gitignore
|
206
|
+
- spec/internal/public/favicon.ico
|
129
207
|
- spec/lib/filter_parser_spec.rb
|
130
208
|
- spec/parsing_nesting/build_tree_spec.rb
|
131
209
|
- spec/parsing_nesting/consuming_spec.rb
|
@@ -133,4 +211,3 @@ test_files:
|
|
133
211
|
- spec/rcov.opts
|
134
212
|
- spec/spec.opts
|
135
213
|
- spec/spec_helper.rb
|
136
|
-
- spec/support/blacklight_mock.rb
|