blacklight_advanced_search 1.0.0pre1
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/.gitignore +5 -0
- data/LICENSE +14 -0
- data/README.rdoc +172 -0
- data/Rakefile +6 -0
- data/VERSION +1 -0
- data/app/controllers/advanced_controller.rb +61 -0
- data/app/controllers/application_controller.rb +5 -0
- data/app/helpers/advanced_helper.rb +40 -0
- data/app/views/advanced/_advanced_search_facets.html.erb +16 -0
- data/app/views/advanced/_advanced_search_fields.html.erb +6 -0
- data/app/views/advanced/_advanced_search_form.html.erb +48 -0
- data/app/views/advanced/_advanced_search_help.html.erb +22 -0
- data/app/views/advanced/index.html.erb +10 -0
- data/app/views/blacklight_advanced_search/_facet_limit.html.erb +25 -0
- data/blacklight_advanced_search.gemspec +24 -0
- data/config/routes.rb +3 -0
- data/install.rb +0 -0
- data/lib/blacklight_advanced_search/advanced_query_parser.rb +61 -0
- data/lib/blacklight_advanced_search/catalog_helper_override.rb +53 -0
- data/lib/blacklight_advanced_search/controller.rb +101 -0
- data/lib/blacklight_advanced_search/engine.rb +47 -0
- data/lib/blacklight_advanced_search/filter_parser.rb +13 -0
- data/lib/blacklight_advanced_search/parsing_nesting_parser.rb +18 -0
- data/lib/blacklight_advanced_search/render_constraints_override.rb +96 -0
- data/lib/blacklight_advanced_search/version.rb +10 -0
- data/lib/blacklight_advanced_search.rb +74 -0
- data/lib/generators/blacklight_advanced_search/assets_generator.rb +25 -0
- data/lib/generators/blacklight_advanced_search/blacklight_advanced_search_generator.rb +11 -0
- data/lib/generators/blacklight_advanced_search/templates/_search_form.html.erb +13 -0
- data/lib/generators/blacklight_advanced_search/templates/blacklight_advanced_search_config.rb +86 -0
- data/lib/generators/blacklight_advanced_search/templates/public/javascripts/blacklight_advanced_search_javascript.js +62 -0
- data/lib/generators/blacklight_advanced_search/templates/public/stylesheets/advanced_results.css +41 -0
- data/lib/generators/blacklight_advanced_search/templates/public/stylesheets/blacklight_advanced_search_styles.css +129 -0
- data/lib/parsing_nesting/Readme.rdoc +160 -0
- data/lib/parsing_nesting/grammar.rb +78 -0
- data/lib/parsing_nesting/tree.rb +457 -0
- data/spec/lib/filter_parser_spec.rb +28 -0
- data/spec/parsing_nesting/build_tree_spec.rb +238 -0
- data/spec/parsing_nesting/consuming_spec.rb +49 -0
- data/spec/parsing_nesting/to_solr_spec.rb +360 -0
- data/spec/rcov.opts +3 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/support/blacklight_mock.rb +5 -0
- data/uninstall.rb +1 -0
- metadata +164 -0
@@ -0,0 +1,238 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
require 'parsing_nesting/grammar'
|
4
|
+
require 'parsing_nesting/tree'
|
5
|
+
|
6
|
+
|
7
|
+
module ParseTreeSpecHelper
|
8
|
+
include ParsingNesting::Tree
|
9
|
+
|
10
|
+
def parse(s)
|
11
|
+
ParsingNesting::Tree.parse(s)
|
12
|
+
end
|
13
|
+
|
14
|
+
# for things expected to be a one-element list,
|
15
|
+
# make sure they are and return the element
|
16
|
+
def parse_one_element(s)
|
17
|
+
l = parse(s)
|
18
|
+
l.should be_kind_of( List )
|
19
|
+
l.list.length.should == 1
|
20
|
+
return l.list.first
|
21
|
+
end
|
22
|
+
|
23
|
+
def should_be_and_list(graph)
|
24
|
+
graph.should be_kind_of( AndList )
|
25
|
+
yield graph.list if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
def should_be_list(graph)
|
29
|
+
graph.should be_kind_of( List)
|
30
|
+
yield graph.list if block_given?
|
31
|
+
end
|
32
|
+
|
33
|
+
def should_be_or_list(graph)
|
34
|
+
graph.should be_kind_of( OrList )
|
35
|
+
yield graph.list if block_given?
|
36
|
+
end
|
37
|
+
|
38
|
+
def should_be_term(graph, value)
|
39
|
+
graph.should be_kind_of( Term )
|
40
|
+
graph.value.should == value
|
41
|
+
end
|
42
|
+
|
43
|
+
def should_be_phrase(graph, value)
|
44
|
+
graph.should be_kind_of( Phrase )
|
45
|
+
graph.value.should == value
|
46
|
+
end
|
47
|
+
|
48
|
+
def should_be_mandatory(graph)
|
49
|
+
graph.should be_kind_of(MandatoryClause)
|
50
|
+
yield graph.operand if block_given?
|
51
|
+
end
|
52
|
+
def should_be_excluded(graph)
|
53
|
+
graph.should be_kind_of(ExcludedClause)
|
54
|
+
yield graph.operand if block_given?
|
55
|
+
end
|
56
|
+
def should_be_not_expression(graph)
|
57
|
+
graph.should be_kind_of(NotExpression)
|
58
|
+
yield graph.operand if block_given?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "NestingParser" do
|
63
|
+
describe "Building an Object parse tree" do
|
64
|
+
include ParseTreeSpecHelper
|
65
|
+
|
66
|
+
|
67
|
+
it "should build for term list" do
|
68
|
+
should_be_list parse("one two three") do |list|
|
69
|
+
list.length.should == 3
|
70
|
+
should_be_term list[0], "one"
|
71
|
+
should_be_term list[1], "two"
|
72
|
+
should_be_term list[2], "three"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should build AND list" do
|
77
|
+
should_be_and_list parse_one_element("one AND two AND three") do |list|
|
78
|
+
list.length.should == 3
|
79
|
+
|
80
|
+
should_be_term list[0], "one"
|
81
|
+
should_be_term list[1], "two"
|
82
|
+
should_be_term list[2], "three"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should build OR list" do
|
87
|
+
should_be_or_list parse_one_element("one OR two OR three") do |list|
|
88
|
+
list.length.should == 3
|
89
|
+
should_be_term list[0], "one"
|
90
|
+
should_be_term list[1], "two"
|
91
|
+
should_be_term list[2], "three"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "allows AND list of lists" do
|
96
|
+
should_be_and_list parse_one_element('(one two) AND (blue yellow)') do |and_list|
|
97
|
+
and_list.length.should == 2
|
98
|
+
should_be_list and_list[0] do |list|
|
99
|
+
should_be_term(list[0], "one")
|
100
|
+
should_be_term(list[1], "two")
|
101
|
+
end
|
102
|
+
should_be_list and_list[1]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should build for mandatory and excluded" do
|
107
|
+
should_be_list parse("+one -two") do |list|
|
108
|
+
list.length.should == 2
|
109
|
+
|
110
|
+
should_be_mandatory list[0] do |operand|
|
111
|
+
should_be_term(operand, "one")
|
112
|
+
end
|
113
|
+
|
114
|
+
should_be_excluded list[1] do |operand|
|
115
|
+
should_be_term(operand, "two")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should build phrases" do
|
121
|
+
should_be_list parse('"quick brown" +"jumps over" -"lazy dog"') do |list|
|
122
|
+
list.length.should == 3
|
123
|
+
|
124
|
+
should_be_phrase(list[0], "quick brown")
|
125
|
+
|
126
|
+
should_be_mandatory(list[1]) do |operand|
|
127
|
+
should_be_phrase(operand, "jumps over")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should leave phrase literals literal, including weird chars" do
|
133
|
+
phrase_content = "foo+bar -i: '(baz"
|
134
|
+
should_be_phrase parse_one_element("\"#{phrase_content}\""), phrase_content
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should build for NOT on term" do
|
138
|
+
should_be_list parse("one two three NOT four") do |list|
|
139
|
+
should_be_not_expression list[3] do |operand|
|
140
|
+
should_be_term(operand, "four")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should build for NOT on phrase" do
|
146
|
+
should_be_list parse('one two three NOT "quick brown"') do |list|
|
147
|
+
should_be_not_expression list[3] do |operand|
|
148
|
+
should_be_phrase(operand, "quick brown")
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should build NOT on expression" do
|
154
|
+
should_be_list parse('one two NOT (blue OR yellow)') do |list|
|
155
|
+
should_be_not_expression list[2] do |operand|
|
156
|
+
should_be_or_list(operand)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should build NOT preceded by binary op" do
|
162
|
+
should_be_or_list parse_one_element('one OR NOT two') do |list|
|
163
|
+
should_be_not_expression list[1] do |operand|
|
164
|
+
should_be_term(operand, "two")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
it "should bind OR more tightly than AND" do
|
171
|
+
should_be_and_list parse_one_element("grey AND big OR small AND tail") do |list|
|
172
|
+
list.length.should == 3
|
173
|
+
|
174
|
+
should_be_term list[0], "grey"
|
175
|
+
|
176
|
+
should_be_or_list list[1] do |or_list|
|
177
|
+
or_list.length.should == 2
|
178
|
+
should_be_term or_list[0], "big"
|
179
|
+
should_be_term or_list[1], "small"
|
180
|
+
end
|
181
|
+
|
182
|
+
should_be_term list[2], "tail"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should parse AND'd lists" do
|
187
|
+
should_be_and_list parse_one_element("(foo bar one AND two) AND (three four ten OR twelve)") do |list|
|
188
|
+
list.length.should == 2
|
189
|
+
|
190
|
+
should_be_list(list[0]) do |first_half|
|
191
|
+
first_half[0].value.should == 'foo'
|
192
|
+
first_half[1].value.should == "bar"
|
193
|
+
should_be_and_list(first_half[2])
|
194
|
+
end
|
195
|
+
|
196
|
+
should_be_list(list[1]) do |second_half|
|
197
|
+
second_half[0].value.should == "three"
|
198
|
+
second_half[1].value.should == "four"
|
199
|
+
should_be_or_list second_half[2]
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should build for a crazy complicated one" do
|
205
|
+
should_be_list parse("mark +twain AND huck OR fun OR ((jim AND river) AND (red -dogs))") do |list|
|
206
|
+
should_be_term list[0], "mark"
|
207
|
+
should_be_and_list list[1] do |and_list|
|
208
|
+
|
209
|
+
should_be_mandatory and_list[0] do |operand|
|
210
|
+
should_be_term operand, "twain"
|
211
|
+
end
|
212
|
+
|
213
|
+
should_be_or_list and_list[1] do |or_list|
|
214
|
+
should_be_term or_list[0], "huck"
|
215
|
+
should_be_term or_list[1], "fun"
|
216
|
+
|
217
|
+
should_be_and_list or_list[2] do |and_list|
|
218
|
+
and_list.length.should == 2
|
219
|
+
|
220
|
+
should_be_and_list and_list[0]
|
221
|
+
|
222
|
+
should_be_list and_list[1] do |terms|
|
223
|
+
should_be_term terms[0], "red"
|
224
|
+
should_be_excluded terms[1] do |operand|
|
225
|
+
should_be_term operand, "dogs"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
#require 'rubygems'
|
3
|
+
#require 'parslet'
|
4
|
+
#require 'spec'
|
5
|
+
#require 'spec/autorun'
|
6
|
+
|
7
|
+
#load '../../nesting_parser/grammar.rb'
|
8
|
+
#load '../../nesting_parser/tree.rb'
|
9
|
+
|
10
|
+
describe "NestingParser" do
|
11
|
+
describe "Consuming" do
|
12
|
+
before do
|
13
|
+
@parser = ParsingNesting::Grammar.new
|
14
|
+
end
|
15
|
+
# Whole bunch of things we just want to make sure they are consumed
|
16
|
+
# without error, not checking the generated tree yet.
|
17
|
+
["foo",
|
18
|
+
"foo bar",
|
19
|
+
" foo bar ",
|
20
|
+
" foo bar baz ",
|
21
|
+
"+foo",
|
22
|
+
"-foo",
|
23
|
+
"+foo -bar",
|
24
|
+
"one +two three -four five",
|
25
|
+
"foo AND bar",
|
26
|
+
"one AND two AND three AND four",
|
27
|
+
"one OR two OR three OR four",
|
28
|
+
"white OR blue AND big OR small",
|
29
|
+
"+yes AND book OR -online",
|
30
|
+
"(one AND two)",
|
31
|
+
" ( one AND two ) ",
|
32
|
+
"(one OR two) three +four",
|
33
|
+
"(one AND two) OR three",
|
34
|
+
"(one AND -two) AND (+three OR (-four AND five))",
|
35
|
+
"one two three NOT four",
|
36
|
+
"one two three NOT (four OR five)",
|
37
|
+
"NOT four",
|
38
|
+
"NOT (four five)",
|
39
|
+
"(one two three) OR (four five) AND six",
|
40
|
+
'"foo+bar (baz"',
|
41
|
+
"(foo bar one AND two) AND (three four ten OR twelve)"
|
42
|
+
].each do |query|
|
43
|
+
it "should consume<<#{query}>>" do
|
44
|
+
lambda {@parser.parse(query)}.should_not raise_error
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,360 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
#require 'rubygems'
|
3
|
+
#require 'parslet'
|
4
|
+
#require 'spec'
|
5
|
+
#require 'spec/autorun'
|
6
|
+
|
7
|
+
#load '../../lib/parsing_nesting/grammar.rb'
|
8
|
+
#load '../../lib/parsing_nesting/tree.rb'
|
9
|
+
|
10
|
+
module SolrQuerySpecHelper
|
11
|
+
def parse(s)
|
12
|
+
ParsingNesting::Tree.parse(s)
|
13
|
+
end
|
14
|
+
|
15
|
+
# yields localparam string, and the actual internal query
|
16
|
+
def local_param_match(query)
|
17
|
+
query.should =~ /^ *_query_:\"\{([^}]+)\}(.*)" *$/
|
18
|
+
query =~ /^ *_query_:\"\{([^}]+)\}(.*)" *$/
|
19
|
+
(param_str = $1).should_not be_nil
|
20
|
+
(query = $2).should_not be_nil
|
21
|
+
|
22
|
+
yield [param_str, query] if block_given?
|
23
|
+
end
|
24
|
+
|
25
|
+
def bare_local_param_match(query)
|
26
|
+
query.should =~ / *\{([^}]+)\}(.*)/
|
27
|
+
query =~ /\{([^}]+)\}(.*)/
|
28
|
+
(param_str = $1).should_not be_nil
|
29
|
+
(query = $2).should_not be_nil
|
30
|
+
yield [param_str, query] if block_given?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Convenience for matching a lucene query combining nested queries,
|
34
|
+
# and getting out the nested queries as matches.
|
35
|
+
# pass in a string representing a regexp that uses $QUERY as placeholder where
|
36
|
+
# a nested _query_: will be.
|
37
|
+
#
|
38
|
+
# * Any parens in your passed in regexp will
|
39
|
+
# be paren literals, don't escape em yourself -- you can't do your
|
40
|
+
# own captures, because if the regexp passes, it'll yield to a block
|
41
|
+
# with a list, in order, of nested queries.
|
42
|
+
#
|
43
|
+
#
|
44
|
+
# * Can include $ALL to represent literal "*:*"
|
45
|
+
#
|
46
|
+
# Yes, the regexp matching isn't as robust as it could be, hard
|
47
|
+
# to deal with like escaped end-quotes and stuff in a regexp, but
|
48
|
+
# should mostly work.
|
49
|
+
def query_template_matcher(top_query, regexp_str)
|
50
|
+
nested_re = '(_query_:".+")'
|
51
|
+
regexp_str = regexp_str.gsub("(", '\(').gsub(')', '\)').gsub("$QUERY", nested_re).gsub("$ALL", "\\*\\:\\*")
|
52
|
+
regexp = Regexp.new('^ *' + regexp_str + ' *$')
|
53
|
+
|
54
|
+
top_query.should match( regexp )
|
55
|
+
|
56
|
+
yield *regexp.match(top_query).captures if block_given?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "NestingParser" do
|
61
|
+
describe "Translating to Solr" do
|
62
|
+
include SolrQuerySpecHelper
|
63
|
+
|
64
|
+
describe "with basic simple query" do
|
65
|
+
before do
|
66
|
+
@query = parse("one two three").to_query(:qf => "field field2^5", :pf=>"$pf_title")
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should insist on dismax for nested query" do
|
70
|
+
query = parse("one two three").to_query(:defType => "field", :qf=>"$qf")
|
71
|
+
local_param_match(query) do |params, query|
|
72
|
+
params.should match(/^\!dismax /)
|
73
|
+
params.should_not match(/field/)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should include LocalParams" do
|
78
|
+
local_param_match(@query) do |params, query|
|
79
|
+
params.should include("pf=$pf_title")
|
80
|
+
params.should include('qf=\'field field2^5\'')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should include the query" do
|
85
|
+
local_param_match(@query) do |params, query|
|
86
|
+
query.should == "one two three"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "with simple mandatory/excluded terms" do
|
92
|
+
before do
|
93
|
+
@inner_query = 'red +army -"soviet union" germany'
|
94
|
+
@full_query = parse(@inner_query).to_query(:qf => "foo", :pf=>"bar")
|
95
|
+
end
|
96
|
+
it "should include query" do
|
97
|
+
local_param_match(@full_query) do |params, query|
|
98
|
+
query.should == @inner_query.gsub('"', '\\\\"')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "with embeddable AND query" do
|
104
|
+
before do
|
105
|
+
@query = parse("one two AND three").to_query({:qf => "$qf"})
|
106
|
+
end
|
107
|
+
it "should flatten to one dismax query" do
|
108
|
+
local_param_match(@query) do |params, query|
|
109
|
+
query.should == "one +two +three"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe ", with mandatory/excluded" do
|
114
|
+
before do
|
115
|
+
@query = parse("one -two AND +three").to_query({:qf=>"$qf"})
|
116
|
+
end
|
117
|
+
it "should preserve +/- operators" do
|
118
|
+
local_param_match(@query) do |params, query|
|
119
|
+
query.should == "one -two +three"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe ", deeply nested" do
|
125
|
+
before do
|
126
|
+
@query = parse("blue (green AND -violet) AND (+big AND (-small AND medium))").to_query({:qf=>"$qf"})
|
127
|
+
end
|
128
|
+
it "should flatten into dismax" do
|
129
|
+
local_param_match(@query) do |params, query|
|
130
|
+
query.should == "blue +green -violet +big -small +medium"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
it "for simple OR list, forcing mm=1" do
|
136
|
+
query = parse("one OR two OR three").to_query(:qf => "$qf", :mm=>"50%")
|
137
|
+
local_param_match(query) do |params, query|
|
138
|
+
params.should include("mm=1")
|
139
|
+
query.should == "one two three"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
describe "that needs to create multiple nested queries" do
|
146
|
+
|
147
|
+
|
148
|
+
it "for two lists, OR'd" do
|
149
|
+
query = parse("(one two three) OR (red -green +blue)").to_query(:qf => "$qf")
|
150
|
+
|
151
|
+
query_template_matcher(query, "( *$QUERY +OR +$QUERY *)" ) do |first_half, second_half|
|
152
|
+
|
153
|
+
local_param_match(first_half) do |params, query|
|
154
|
+
params.should include("qf=$qf")
|
155
|
+
query.should == "one two three"
|
156
|
+
end
|
157
|
+
|
158
|
+
local_param_match(second_half) do |params, query|
|
159
|
+
params.should include("qf=$qf")
|
160
|
+
query.should == "red -green +blue"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it "for AND list that can not be flattened" do
|
166
|
+
params = {:qf=>"$qf", :pf=>"$pf", :mm=>"50%"}
|
167
|
+
query = parse("a OR b AND x OR y").to_query( params )
|
168
|
+
|
169
|
+
query_template_matcher(query, "( *$QUERY +AND +$QUERY *)" ) do |first, second|
|
170
|
+
first.should == parse("a OR b").to_query(params)
|
171
|
+
second.should == parse("x OR y").to_query(params)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it "for AND of two lists" do
|
176
|
+
params = {:qf => "$qf", :pf=>"$pf", :mm=>"50%"}
|
177
|
+
query = parse("(one +two three) AND (four five -six)").to_query( params )
|
178
|
+
|
179
|
+
query_template_matcher(query, "( *$QUERY +AND +$QUERY *)" ) do |first, second|
|
180
|
+
first.should == parse("one +two three").to_query(params)
|
181
|
+
second.should == parse("four five -six").to_query(params)
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
it "for crazy complicated query" do
|
187
|
+
query = parse("red AND dawn OR (-night -afternoon) AND NOT (moscow OR beach) ").to_query(:qf => "$qf", :pf =>"$pf", :mm=>"50%")
|
188
|
+
|
189
|
+
query_template_matcher(query, "( *$QUERY +AND +( *$QUERY +OR +($ALL AND NOT $QUERY *) *) +AND NOT $QUERY *)") do |red_q, dawn_q, night_q, moscow_q|
|
190
|
+
|
191
|
+
local_param_match(red_q) { |params, query| query.should == "red" }
|
192
|
+
|
193
|
+
local_param_match(dawn_q) { |params, query| query.should == "dawn"}
|
194
|
+
|
195
|
+
local_param_match(night_q) do |params, query|
|
196
|
+
params.should include("mm=1")
|
197
|
+
query.should == "night afternoon"
|
198
|
+
end
|
199
|
+
|
200
|
+
local_param_match(moscow_q) do |params, query|
|
201
|
+
params.should include("mm=1")
|
202
|
+
query.should == "moscow beach"
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
describe "for NOT operator" do
|
213
|
+
it "simple" do
|
214
|
+
query = parse("NOT frog").to_query
|
215
|
+
|
216
|
+
query_template_matcher(query, "NOT $QUERY") do |q|
|
217
|
+
q.should == parse("frog").to_query
|
218
|
+
end
|
219
|
+
end
|
220
|
+
it "binds tightly" do
|
221
|
+
query = parse("one NOT two three").to_query
|
222
|
+
|
223
|
+
query_template_matcher(query, "$QUERY AND NOT $QUERY") do |q1, q2|
|
224
|
+
|
225
|
+
local_param_match(q1) do |params, query|
|
226
|
+
query.should == "one three"
|
227
|
+
end
|
228
|
+
|
229
|
+
local_param_match(q2) do |params, query|
|
230
|
+
query.should == "two"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
it "complicated operand" do
|
236
|
+
query = parse("one OR two NOT (three OR four AND five)").to_query
|
237
|
+
#"_query_:'{!dismax mm=1}one two' AND NOT ( _query_:'{!dismax mm=1}three four' AND _query_:'{!dismax }five' )"
|
238
|
+
query_template_matcher(query, "$QUERY +AND NOT +( *$QUERY +AND +$QUERY *)") do |external_or, internal_or, internal_term|
|
239
|
+
external_or.should == parse("one OR two").to_query
|
240
|
+
internal_or.should == parse("three OR four").to_query
|
241
|
+
internal_term.should == parse("five").to_query
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
it "uses workaround on NOT as operand to OR" do
|
246
|
+
query = parse("two OR (NOT (three))").to_query
|
247
|
+
query_template_matcher(query, "( *$QUERY +OR +($ALL +AND +NOT +$QUERY) *)")
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
251
|
+
|
252
|
+
describe "for pure negative" do
|
253
|
+
it "should convert simple pure negative" do
|
254
|
+
query = parse('-one -two -"a phrase"').to_query(:qf => "$qf", :mm => "100%")
|
255
|
+
|
256
|
+
query_template_matcher(query, " *NOT $QUERY") do |query|
|
257
|
+
local_param_match(query) do |params, query|
|
258
|
+
params.should include("mm=1")
|
259
|
+
query.should == 'one two \\"a phrase\\"'
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should convert pure negative AND" do
|
266
|
+
query = parse("-one AND -two AND -three").to_query(:qf => "$qf", :mm => "100%")
|
267
|
+
|
268
|
+
query_template_matcher(query, "NOT $QUERY") do |query|
|
269
|
+
local_param_match(query) do |params, query|
|
270
|
+
params.should =~ /mm=1 |$/
|
271
|
+
query.should == 'one two three'
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should convert pure negative OR" do
|
277
|
+
query = parse("-one OR -two OR -three").to_query
|
278
|
+
|
279
|
+
query_template_matcher(query, "NOT $QUERY") do |query|
|
280
|
+
local_param_match(query) do |params, query|
|
281
|
+
params.should include("mm=100%")
|
282
|
+
query.should == "one two three"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
end
|
287
|
+
|
288
|
+
it "should convert crazy pure negative combo" do
|
289
|
+
query = parse("(-one -two) OR -three OR (-five AND -six)").to_query
|
290
|
+
|
291
|
+
query_template_matcher(query, "( *($ALL +AND +NOT +$QUERY) +OR +( *$ALL +AND +NOT +$QUERY *) +OR +( *$ALL +AND +NOT +$QUERY *) *)")
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
# When a single parse will be the whole query, we use
|
297
|
+
# different more compact production
|
298
|
+
describe "Single Query" do
|
299
|
+
before do
|
300
|
+
@solr_local_params = {"qf" => "$title_qf", "pf" => "$title_pf"}
|
301
|
+
end
|
302
|
+
describe "simple search" do
|
303
|
+
it "should work with local params" do
|
304
|
+
hash = parse("one +two -three").to_single_query_params(@solr_local_params)
|
305
|
+
hash[:defType].should == "dismax"
|
306
|
+
bare_local_param_match(hash[:q]) do |params, query|
|
307
|
+
query.should == "one +two -three"
|
308
|
+
params.should include("pf=$title_pf")
|
309
|
+
params.should include("qf=$title_qf")
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
it "should work without local params" do
|
314
|
+
hash = parse("one +two -three").to_single_query_params({})
|
315
|
+
hash[:defType].should == "dismax"
|
316
|
+
hash[:q].should == "one +two -three"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
describe "simple pure negative" do
|
320
|
+
it "should be nested NOT" do
|
321
|
+
hash = parse("-one -two").to_single_query_params({})
|
322
|
+
hash[:defType].should == "dismax"
|
323
|
+
query_template_matcher(hash[:q], "NOT $QUERY") do |query|
|
324
|
+
local_param_match(query) do |params, query|
|
325
|
+
query.should == "one two"
|
326
|
+
params.should include("mm=1")
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
describe "complex query" do
|
332
|
+
it "should parse" do
|
333
|
+
hash = parse("one AND (two OR three)").to_single_query_params({})
|
334
|
+
hash[:defType].should == "lucene"
|
335
|
+
query_template_matcher(hash[:q], "( *$QUERY +AND +$QUERY *)") do |first, second|
|
336
|
+
local_param_match(first) do |params, query|
|
337
|
+
params.should include("dismax")
|
338
|
+
query.should == "one"
|
339
|
+
end
|
340
|
+
local_param_match(second) do |params, query|
|
341
|
+
params.should include("mm=1")
|
342
|
+
params.should include("dismax")
|
343
|
+
query.should == "two three"
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
|
350
|
+
end
|
351
|
+
|
352
|
+
|
353
|
+
end
|
354
|
+
|
355
|
+
|
356
|
+
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
|
data/spec/rcov.opts
ADDED
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|