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