inquisitio 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dff28f9b5d191622a0c07139b2f03edb8ec8c87d
4
- data.tar.gz: f03905452101150efceed98062cd8a9a9666b13b
3
+ metadata.gz: f276ddda623e737c046197a26d4696dc87859265
4
+ data.tar.gz: 60eac7285494fc1adad7edb022ca36e6030f9db5
5
5
  SHA512:
6
- metadata.gz: 40362fa85a4a8bb8b71aa3b51f1d2aead37829b82cea692649fd0217d9936ac76e3b75f36de5cd467317d4d4bac606a98e325f03cd586a583bbf347ea819a2a7
7
- data.tar.gz: 5068a7af2ad3f093d11821332c60cc0d3be383ec4dffa7fec84779a77d820a6d6caa84aefb48dcafaeee0a7006a2cddb9864a311696c157df92409a719d7f8ec
6
+ metadata.gz: 59bc49ebc2a61c0dcf17118ad7de9329be918b13088df8c78e9175ec8bb964135523e3972a743864b704057442e4e4f39286933ed3b131528682004eb96246fe
7
+ data.tar.gz: b48f671ce616589f245bd9ecf308920832900ca35711385579b9544324769e3f37e0d7b08436f9a5ade1a63539467b0c569a250d1f1dba783e3d3ae7c079689c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ 2.1.0 / 2015-07-22
2
+ [FEATURE] Add support for filter queries (fq) as Searcher#filter
3
+ [FEATURE] Add support for facets as Searcher#facets
4
+
1
5
  2.0.0 / 2015-07-21
2
6
  [FEATURE] Removed support for 2011-02-01 api
3
7
 
@@ -0,0 +1,16 @@
1
+ module Inquisitio
2
+ class Facets
3
+ extend Forwardable
4
+
5
+ def initialize(facets)
6
+ @facets = JSON.parse(facets.to_json, symbolize_names: true)
7
+ end
8
+
9
+ def fields
10
+ @facets.keys
11
+ end
12
+
13
+ def_delegator :@facets, :[]
14
+
15
+ end
16
+ end
@@ -6,78 +6,78 @@ module Inquisitio
6
6
  end
7
7
 
8
8
  def initialize(options = {})
9
- @query = options[:query]
10
- @named_fields = options[:named_fields] || {}
9
+ @query_terms = options[:query][:terms]
10
+ @query_named_fields = options[:query][:named_fields] || {}
11
+ @filter_query = options[:filter_query] || {}
12
+ @filter_query_terms = @filter_query[:terms] || []
13
+ @filter_query_named_fields = @filter_query[:named_fields] || {}
14
+ @facets = options[:facets] || {}
11
15
  @q_options = options[:q_options] || {}
12
16
  @expressions = options[:expressions] || {}
13
- @arguments = options[:arguments] || {}
14
17
  @return_fields = options[:return_fields] || []
15
18
  @size = options[:size] || Inquisitio.config.default_search_size
16
19
  @start = options[:start] || 0
17
20
  @sort = options[:sort] || {}
18
- @q_parser = options[:q_parser] || (is_simple? ? nil : :structured)
21
+ @q_parser = options[:q_parser] || (is_simple?(@query_terms, @query_named_fields) ? nil : :structured)
19
22
  end
20
23
 
21
24
  def build
22
- components = [url_root]
23
- components << (is_simple? ? simple_query : boolean_query)
24
- components << "&q.parser=#{@q_parser}" if @q_parser && Inquisitio.config.api_version == '2013-01-01'
25
- components << "&return=#{CGI::escape(@return_fields.join(',').gsub('\'', ''))}" unless @return_fields.empty?
26
- components << arguments
27
- components << '&q.options=' + CGI::escape(@q_options.to_json) unless @q_options.empty?
25
+ components = []
26
+ components << 'q=' + CGI::escape(build_query(@query_terms, @query_named_fields))
27
+ if !@filter_query_terms.empty? || !@filter_query_named_fields.empty?
28
+ components << 'fq=' + CGI::escape(build_query(@filter_query_terms, @filter_query_named_fields))
29
+ end
30
+ components << "q.parser=#{@q_parser}" if @q_parser
31
+ components << "return=#{CGI::escape(@return_fields.join(',').gsub('\'', ''))}" unless @return_fields.empty?
32
+ # components << arguments
33
+ @facets.each do |field,settings|
34
+ components << "facet.#{field}=#{CGI::escape(settings.to_json)}"
35
+ end
36
+ components << 'q.options=' + CGI::escape(@q_options.to_json) unless @q_options.empty?
28
37
  @expressions.each do |name, expression|
29
- components << "&expr.#{name}=" + CGI::escape(expression)
38
+ components << "expr.#{name}=" + CGI::escape(expression)
30
39
  end
31
- components << "&size=#{@size}" unless @arguments[:size]
32
- components << "&start=#{@start}" unless @arguments[:start] || @start == 0 || @start == '0'
33
- components << '&sort=' + @sort.map { |k, v| "#{k}%20#{v}" }.join(',') unless @sort.empty?
34
- components.join('')
40
+ components << "size=#{@size}"
41
+ components << "start=#{@start}" unless @start == 0 || @start == '0'
42
+ components << 'sort=' + @sort.map { |k, v| "#{k}%20#{v}" }.join(',') unless @sort.empty?
43
+ url_root + components.join('&')
35
44
  end
36
45
 
37
- def is_simple?
38
- @named_fields.empty? && Array(@query).size == 1
46
+ def is_simple?(terms, named_fields)
47
+ Array(terms).size == 1 && named_fields.empty?
39
48
  end
40
49
 
41
50
  private
42
- def simple_query
43
- "q=#{CGI::escape(@query.first)}"
44
- end
45
-
46
- def boolean_query
47
51
 
52
+ def build_query(terms, named_fields)
53
+ return terms.first if is_simple?(terms, named_fields)
48
54
  query_blocks = []
55
+ query_blocks << '(and' unless terms.empty? || named_fields.empty?
49
56
 
50
- if Array(@query).empty?
51
- # query_blocks = []
52
- elsif @query.size == 1
53
- query_blocks << "'#{sanitise(@query.first)}'"
54
- else
55
- query_blocks << "(or #{@query.map { |q| "'#{sanitise(q)}'" }.join(' ')})"
57
+ if terms.size == 1
58
+ query_blocks << "'#{dequote(terms.first)}'"
59
+ elsif terms.size > 1
60
+ query_blocks << "(or #{terms.map { |q| "'#{dequote(q)}'" }.join(' ')})"
56
61
  end
57
62
 
58
- query_blocks += @named_fields.map do |key, value|
59
- if value.is_a?(String)
60
- "#{sanitise(key)}:'#{sanitise(value)}'"
61
- elsif value.is_a?(Array)
62
- "(or #{value.map { |v| "#{sanitise(key)}:'#{sanitise(v)}'" }.join(" ")})"
63
- else
64
- raise InquisitioError.new('Filter values must be strings or arrays.')
65
- end
63
+ return query_blocks.join(' ') if named_fields.empty?
64
+
65
+ query_blocks += named_fields.map do |key, value|
66
+ raise InquisitioError.new('Named field values must be strings or arrays.') unless value.is_a?(String) || value.is_a?(Array)
67
+ block = "#{dequote(key)}:'#{dequote(value)}'" if value.is_a?(String)
68
+ block = "#{dequote(key)}:'#{dequote(value.first)}'" if value.is_a?(Array) && value.size == 1
69
+ block = "(or #{value.map { |v| "#{dequote(key)}:'#{dequote(v)}'" }.join(' ')})" if value.is_a?(Array) && value.size > 1
70
+ block
66
71
  end
67
72
 
68
- "q=#{CGI::escape("(and #{query_blocks.join(' ')})").gsub('&', '%26')}"
73
+ query_blocks << ')' unless terms.empty? || named_fields.empty?
74
+ query_blocks.join(' ')
69
75
  end
70
76
 
71
- def sanitise(value)
77
+ def dequote(value)
72
78
  value.to_s.gsub('\'', '')
73
79
  end
74
80
 
75
-
76
- def arguments
77
- return '' if @arguments.nil?
78
- @arguments.map { |key, value| "&#{key.to_s.gsub('\'', '')}=#{value.to_s.gsub('\'', '')}" }.join("")
79
- end
80
-
81
81
  def url_root
82
82
  "#{Inquisitio.config.search_endpoint}/#{Inquisitio.config.api_version}/search?"
83
83
  end
@@ -11,8 +11,11 @@ module Inquisitio
11
11
 
12
12
  def initialize(params = nil)
13
13
  @params = params || {
14
- criteria: [],
15
- named_fields: {},
14
+ query_terms: [],
15
+ query_named_fields: {},
16
+ filter_query_terms: [],
17
+ filter_query_named_fields: {},
18
+ facets: {},
16
19
  per: 10,
17
20
  page: 1,
18
21
  returns: [],
@@ -20,7 +23,6 @@ module Inquisitio
20
23
  q_options: {},
21
24
  expressions: {}
22
25
  }
23
- @failed_attempts = 0
24
26
 
25
27
  yield(self) if block_given?
26
28
  end
@@ -67,19 +69,42 @@ module Inquisitio
67
69
  def where(value)
68
70
  clone do |s|
69
71
  if value.is_a?(Array)
70
- s.params[:criteria] += value
72
+ s.params[:query_terms] += value
71
73
  elsif value.is_a?(Hash)
72
74
  value.each do |k, v|
73
75
  k = k.to_sym
74
- s.params[:named_fields][k] ||= []
76
+ s.params[:query_named_fields][k] ||= []
75
77
  if v.is_a?(Array)
76
- s.params[:named_fields][k] = v
78
+ s.params[:query_named_fields][k] = v
77
79
  else
78
- s.params[:named_fields][k] << v
80
+ s.params[:query_named_fields][k] << v
79
81
  end
80
82
  end
81
83
  else
82
- s.params[:criteria] << value
84
+ s.params[:query_terms] << value
85
+ end
86
+ end
87
+ end
88
+
89
+ def filter(value)
90
+ clone do |s|
91
+ if value.nil?
92
+ s.params[:filter_query_terms] = []
93
+ s.params[:filter_query_named_fields] = {}
94
+ elsif value.is_a?(Array)
95
+ s.params[:filter_query_terms] += value
96
+ elsif value.is_a?(Hash)
97
+ value.each do |k, v|
98
+ k = k.to_sym
99
+ s.params[:filter_query_named_fields][k] ||= []
100
+ if v.is_a?(Array)
101
+ s.params[:filter_query_named_fields][k] = v
102
+ else
103
+ s.params[:filter_query_named_fields][k] << v
104
+ end
105
+ end
106
+ else
107
+ s.params[:filter_query_terms] << value
83
108
  end
84
109
  end
85
110
  end
@@ -90,6 +115,12 @@ module Inquisitio
90
115
  end
91
116
  end
92
117
 
118
+ def facets(value)
119
+ clone do |s|
120
+ s.params[:facets] = value
121
+ end
122
+ end
123
+
93
124
  def expressions(value)
94
125
  clone do |s|
95
126
  s.params[:expressions] = value
@@ -130,6 +161,10 @@ module Inquisitio
130
161
  end
131
162
  end
132
163
 
164
+ def result_facets
165
+ @result_facets ||= Facets.new(cloudsearch_body['facets'])
166
+ end
167
+
133
168
  # Proxy everything to the results so that this this class
134
169
  # transparently acts as an Array.
135
170
  def method_missing(name, *args, &block)
@@ -139,35 +174,41 @@ module Inquisitio
139
174
  private
140
175
 
141
176
  def results
142
- return @results unless @results.nil?
143
-
144
- Inquisitio.config.logger.info("Performing search: #{search_url}")
145
- response = Excon.get(search_url)
146
- raise InquisitioError.new("Search failed with status code: #{response.status} Message #{response.body}") unless response.status == 200
147
- body = JSON.parse(response.body)
148
- time_ms = body['status']['time-ms']
149
- @results = Results.new(body['hits']['hit'],
150
- params[:page],
151
- params[:per],
152
- body['hits']['found'],
153
- time_ms)
154
- rescue => e
155
- @failed_attempts += 1
156
- Inquisitio.config.logger.error("Exception Performing search: #{search_url} #{e}")
157
-
158
- if @failed_attempts < Inquisitio.config.max_attempts
159
- Inquisitio.config.logger.error("Retrying search #{@failed_attempts}/#{Inquisitio.config.max_attempts}")
160
- results
161
- else
162
- raise InquisitioError.new('Exception performing search')
177
+ @results ||= begin
178
+ Results.new(cloudsearch_body['hits']['hit'],
179
+ params[:page],
180
+ params[:per],
181
+ cloudsearch_body['hits']['found'],
182
+ cloudsearch_body['status']['time-ms'])
183
+ end
184
+ end
185
+
186
+ def cloudsearch_body
187
+ failed = 0
188
+ @cloudsearch_body ||= begin
189
+ Inquisitio.config.logger.info("Performing search: #{search_url}")
190
+ response = Excon.get(search_url)
191
+ raise InquisitioError.new("Search failed with status code: #{response.status} Message #{response.body}") unless response.status == 200
192
+ JSON.parse(response.body)
193
+ rescue => e
194
+ failed += 1
195
+ Inquisitio.config.logger.error("Exception Performing search: #{search_url} #{e}")
196
+
197
+ if failed < Inquisitio.config.max_attempts
198
+ Inquisitio.config.logger.error("Retrying search #{@failed_attempts}/#{Inquisitio.config.max_attempts}")
199
+ retry
200
+ else
201
+ raise InquisitioError.new('Exception performing search')
202
+ end
163
203
  end
164
204
  end
165
205
 
166
206
  def search_url
167
207
  @search_url ||= begin
168
208
  SearchUrlBuilder.build(
169
- query: params[:criteria],
170
- named_fields: params[:named_fields],
209
+ query: {terms: params[:query_terms], named_fields: params[:query_named_fields]},
210
+ filter_query: {terms: params[:filter_query_terms], named_fields: params[:filter_query_named_fields]},
211
+ facets: params[:facets],
171
212
  size: params[:per],
172
213
  start: params[:per] * (params[:page] - 1),
173
214
  sort: params[:sort],
@@ -1,3 +1,3 @@
1
1
  module Inquisitio
2
- VERSION = '2.0.0'
2
+ VERSION = '2.1.0'
3
3
  end
data/lib/inquisitio.rb CHANGED
@@ -7,6 +7,7 @@ require 'inquisitio/document'
7
7
  require 'inquisitio/search_url_builder'
8
8
  require 'inquisitio/searcher'
9
9
  require 'inquisitio/results'
10
+ require 'inquisitio/facets'
10
11
  require 'inquisitio/indexer'
11
12
 
12
13
  module Inquisitio
@@ -0,0 +1,72 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ module Inquisitio
4
+ class FacetsTest < Minitest::Test
5
+ def setup
6
+ super
7
+ @search_endpoint = 'http://my.search-endpoint.com'
8
+ Inquisitio.config.search_endpoint = @search_endpoint
9
+ @body = {
10
+ 'status' => {'rid' => '9d3b24b', 'time-ms' => 3},
11
+ 'hits' => {'found' => 1, 'start' => 0, 'hit' => [{'data' => {'id' => ['20'], 'title' => ['Foobar2'], 'type' => ['Module_Dog']}}], },
12
+ 'facets' => {
13
+ 'genres' => {
14
+ 'buckets' => [
15
+ {'value' => 'Drama', 'count' => 12},
16
+ {'value' => 'Romance', 'count' => 9}]
17
+ },
18
+ 'rating' => {
19
+ 'buckets' => [
20
+ {'value' => '6.3', 'count' => 3},
21
+ {'value' => '6.2', 'count' => 2},
22
+ {'value' => '7.1', 'count' => 2},
23
+ {'value' => '7.6', 'count' => 1}]
24
+ },
25
+ }
26
+ }.to_json
27
+
28
+ Excon.defaults[:mock] = true
29
+ Excon.stub({}, {body: @body, status: 200})
30
+ end
31
+
32
+ def teardown
33
+ super
34
+ Excon.stubs.clear
35
+ end
36
+
37
+ def test_should_return_fields
38
+ searcher = Searcher.where('star_wars')
39
+ facets = searcher.result_facets
40
+ assert_equal [:genres, :rating], facets.fields
41
+ end
42
+
43
+ def test_should_access_buckets_by_symbols
44
+ searcher = Searcher.where('star_wars')
45
+ facets = searcher.result_facets
46
+ refute_nil facets[:genres]
47
+ refute_nil facets[:rating]
48
+ end
49
+
50
+ def test_should_have_bucket_lengths
51
+ searcher = Searcher.where('star_wars')
52
+ facets = searcher.result_facets
53
+ assert_equal 2, facets[:genres][:buckets].length
54
+ assert_equal 4, facets[:rating][:buckets].length
55
+ end
56
+
57
+ def test_should_have_bucket_entry_values
58
+ searcher = Searcher.where('star_wars')
59
+ facets = searcher.result_facets
60
+ assert_equal 'Drama', facets[:genres][:buckets].first[:value]
61
+ assert_equal '6.2', facets[:rating][:buckets][1][:value]
62
+ end
63
+
64
+ def test_should_have_bucket_entry_counts
65
+ searcher = Searcher.where('star_wars')
66
+ facets = searcher.result_facets
67
+ assert_equal 12, facets[:genres][:buckets].first[:count]
68
+ assert_equal 2, facets[:rating][:buckets][1][:count]
69
+ end
70
+
71
+ end
72
+ end
data/test/results_test.rb CHANGED
@@ -41,7 +41,10 @@ module Inquisitio
41
41
  end
42
42
 
43
43
  def test_should_return_time_taken
44
- body = "{\"status\": {\"rid\" : \"u9aP4eYo8gIK0csK\", \"time-ms\": 4}, \"hits\" : {\"#{@found}\": 33, \"#{@start}\": 0, \"hit\":#{@expected_results.to_json} }}"
44
+ body = {
45
+ 'status' => {'rid' => 'u9aP4eYo8gIK0csK', 'time-ms' => 4},
46
+ 'hits' => {'found' => 33, 'start' => 0, 'hit' => @expected_results}
47
+ }.to_json
45
48
  Excon.stubs.clear
46
49
  Excon.stub({}, {body: body, status: 200})
47
50
 
@@ -10,94 +10,109 @@ module Inquisitio
10
10
  Inquisitio.config.default_search_size = '10'
11
11
  end
12
12
 
13
+ def test_uses_endpoint
14
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']})
15
+ assert (/^#{@search_endpoint}\/2013-01-01\/search(\?|$)/ =~ url), "Should start with endpoint: #{url}"
16
+ end
17
+
13
18
  def test_create_correct_search_url_with_single_criteria_query
14
- url = SearchUrlBuilder.build(query: ['Star Wars'])
15
- expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=Star+Wars&size=10'
16
- assert_equal expected_url, url
19
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']})
20
+ assert /(&|\?)q=Star\+Wars(&|$)/ =~ url
21
+ end
22
+
23
+ def test_create_correct_search_url_with_single_criteria_query_and_single_filter_criteria
24
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']}, filter_query: {terms: ['A New Hope']})
25
+ assert /(&|\?)q=Star\+Wars(&|$)/ =~ url, "should have query in url: #{url}"
26
+ assert /(&|\?)fq=A\+New\+Hope(&|$)/ =~ url, "should have filter query in url: #{url}"
17
27
  end
18
28
 
19
29
  def test_create_correct_search_url_with_ampersand
20
- url = SearchUrlBuilder.build(query: ['Star&Wars'])
30
+ url = SearchUrlBuilder.build(query: {terms: ['Star&Wars']})
21
31
  expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=Star%26Wars&size=10'
22
32
  assert_equal expected_url, url
23
33
  end
24
34
 
25
35
  def test_create_correct_search_url_with_multiple_criteria_should_use_structured_parser
26
- url = SearchUrlBuilder.build(query: ['Star Wars', 'Episode One'])
27
- expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=%28and+%28or+%27Star+Wars%27+%27Episode+One%27%29%29&q.parser=structured&size=10'
36
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars', 'Episode One']})
37
+ expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=%28or+%27Star+Wars%27+%27Episode+One%27%29&q.parser=structured&size=10'
28
38
  assert_equal expected_url, url
29
39
  end
30
40
 
31
41
  def test_create_correct_search_url_with_multiple_criteria_with_ampersand
32
- url = SearchUrlBuilder.build(query: ['Star&Wars', 'Episode One'])
33
- expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=%28and+%28or+%27Star%26Wars%27+%27Episode+One%27%29%29&q.parser=structured&size=10'
42
+ url = SearchUrlBuilder.build(query: {terms: ['Star&Wars', 'Episode One']})
43
+ expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=%28or+%27Star%26Wars%27+%27Episode+One%27%29&q.parser=structured&size=10'
34
44
  assert_equal expected_url, url
35
45
  end
36
46
 
37
47
  def test_create_correct_search_url_including_return_fields
38
- url = SearchUrlBuilder.build(query: ['Star Wars'], return_fields: %w(title year %))
48
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']}, return_fields: %w(title year %))
39
49
  expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=Star+Wars&return=title%2Cyear%2C%25&size=10'
40
50
  assert_equal expected_url, url
41
51
  end
42
52
 
43
53
  def test_create_search_url_with_default_size
44
- url = SearchUrlBuilder.build(query: ['Star Wars'])
54
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']})
45
55
  expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=Star+Wars&size=10'
46
56
  assert_equal expected_url, url
47
57
  end
48
58
 
49
59
  def test_create_search_url_overriding_default_size
50
- url = SearchUrlBuilder.build(query: ['Star Wars'], size: '200')
60
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']}, size: '200')
51
61
  expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=Star+Wars&size=200'
52
62
  assert_equal expected_url, url
53
63
  end
54
64
 
55
65
  def test_create_search_url_with_start_and_default_size
56
- url = SearchUrlBuilder.build(query: ['Star Wars'], start: '20')
66
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']}, start: '20')
57
67
  assert /(&|\?)start=20(&|$)/ =~ url
58
68
  assert /(&|\?)size=10(&|$)/ =~ url
59
69
  end
60
70
 
61
71
  def test_create_search_url_with_start_and_size
62
- url = SearchUrlBuilder.build(query: ['Star Wars'], start: '2', size: '200')
72
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']}, start: '2', size: '200')
63
73
  assert /(&|\?)start=2(&|$)/ =~ url
64
74
  assert /(&|\?)size=200(&|$)/ =~ url
65
75
  end
66
76
 
67
77
  def test_create_search_url_with_named_fields_array
68
- url = SearchUrlBuilder.build(query: ['Star Wars'], named_fields: {genre: ['Animation', 'Action']})
69
- #TODO make the result use fq
70
- expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=%28and+%27Star+Wars%27+%28or+genre%3A%27Animation%27+genre%3A%27Action%27%29%29&q.parser=structured&size=10'
71
- assert_equal expected_url, url
78
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars'], named_fields: {genre: %w(Animation Action)}})
79
+ assert /(&|\?)q=%28and\+%27Star\+Wars%27\+%28or\+genre%3A%27Animation%27\+genre%3A%27Action%27%29\+%29(&|$)/ =~ url
80
+ assert /(&|\?)q.parser=structured(&|$)/ =~ url
81
+ end
82
+
83
+ def test_create_search_url_with_named_fields_in_filter_query
84
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']}, filter_query: {terms: ['A New Hope'], named_fields: {genre: %w(Animation Action)}})
85
+ assert /(&|\?)q=Star\+Wars(&|$)/ =~ url, "should have query in url: #{url}"
86
+ assert /(&|\?)fq=%28and\+%27A\+New\+Hope%27\+%28or\+genre%3A%27Animation%27\+genre%3A%27Action%27%29\+%29(&|$)/ =~ url, "should have filter query in url: #{url}"
72
87
  end
73
88
 
74
89
  def test_create_search_url_with_query_options
75
- url = SearchUrlBuilder.build(query: ['Star Wars'], q_options: {fields: %w(title^2.0 plot^0.5)})
90
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']}, q_options: {fields: %w(title^2.0 plot^0.5)})
76
91
  expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=Star+Wars&q.options=%7B%22fields%22%3A%5B%22title%5E2.0%22%2C%22plot%5E0.5%22%5D%7D&size=10'
77
92
  assert_equal expected_url, url
78
93
  end
79
94
 
80
95
  def test_create_search_url_with_query_defaultoperator_option
81
- url = SearchUrlBuilder.build(query: ['Star Wars'], q_options: {defaultOperator: 'or'})
96
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']}, q_options: {defaultOperator: 'or'})
82
97
  expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=Star+Wars&q.options=%7B%22defaultOperator%22%3A%22or%22%7D&size=10'
83
98
  assert_equal expected_url, url
84
99
  end
85
100
 
86
101
  def test_create_search_url_with_expressions
87
- url = SearchUrlBuilder.build(query: ['Star Wars'], expressions: {rank1: 'log10(clicks)*_score', rank2: 'cos( _score)'})
102
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']}, expressions: {rank1: 'log10(clicks)*_score', rank2: 'cos( _score)'})
88
103
  expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=Star+Wars&expr.rank1=log10%28clicks%29%2A_score&expr.rank2=cos%28+_score%29&size=10'
89
104
  assert_equal expected_url, url
90
105
  end
91
106
 
92
107
  def test_create_url_with_parser
93
- url = SearchUrlBuilder.build(query: ['Star Wars'], q_parser: :structured)
108
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars']}, q_parser: :structured)
94
109
  expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=Star+Wars&q.parser=structured&size=10'
95
110
  assert_equal expected_url, url
96
111
  end
97
112
 
98
113
  def test_create_url_with_overridden_parser
99
- url = SearchUrlBuilder.build(query: ['Star Wars', 'Star Trek'], q_parser: :simple)
100
- expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=%28and+%28or+%27Star+Wars%27+%27Star+Trek%27%29%29&q.parser=simple&size=10'
114
+ url = SearchUrlBuilder.build(query: {terms: ['Star Wars', 'Star Trek']}, q_parser: :simple)
115
+ expected_url = 'http://my.search-endpoint.com/2013-01-01/search?q=%28or+%27Star+Wars%27+%27Star+Trek%27%29&q.parser=simple&size=10'
101
116
  assert_equal expected_url, url
102
117
  end
103
118
 
@@ -55,20 +55,20 @@ module Inquisitio
55
55
  def test_where_sets_variable
56
56
  criteria = 'Star Wars'
57
57
  searcher = Searcher.where(criteria)
58
- assert_equal [criteria], searcher.params[:criteria]
58
+ assert_equal [criteria], searcher.params[:query_terms]
59
59
  end
60
60
 
61
61
  def test_where_sets_variable_with_an_array
62
62
  criteria = %w(Star Wars)
63
63
  searcher = Searcher.where(criteria)
64
- assert_equal criteria, searcher.params[:criteria]
64
+ assert_equal criteria, searcher.params[:query_terms]
65
65
  end
66
66
 
67
67
  def test_where_doesnt_mutate_searcher
68
68
  initial_criteria = 'star wars'
69
69
  searcher = Searcher.where(initial_criteria)
70
70
  searcher.where('Return of the Jedi')
71
- assert_equal [initial_criteria], searcher.params[:criteria]
71
+ assert_equal [initial_criteria], searcher.params[:query_terms]
72
72
  end
73
73
 
74
74
  def test_where_returns_a_new_searcher
@@ -80,28 +80,28 @@ module Inquisitio
80
80
  def test_where_sets_named_fields
81
81
  named_fields = {genre: 'Animation'}
82
82
  searcher = Searcher.where(named_fields)
83
- assert_equal({genre: ['Animation']}, searcher.params[:named_fields])
83
+ assert_equal({genre: ['Animation']}, searcher.params[:query_named_fields])
84
84
  end
85
85
 
86
86
  def test_where_merges_named_fields
87
87
  named_fields1 = {genre: 'Animation'}
88
88
  named_fields2 = {foobar: 'Cat'}
89
89
  searcher = Searcher.where(named_fields1).where(named_fields2)
90
- assert_equal({genre: ['Animation'], foobar: ['Cat']}, searcher.params[:named_fields])
90
+ assert_equal({genre: ['Animation'], foobar: ['Cat']}, searcher.params[:query_named_fields])
91
91
  end
92
92
 
93
93
  def test_symbolizes_where_keys
94
94
  named_fields1 = {'genre' => 'Animation'}
95
95
  named_fields2 = {'foobar' => 'Cat'}
96
96
  searcher = Searcher.where(named_fields1).where(named_fields2)
97
- assert_equal({genre: ['Animation'], foobar: ['Cat']}, searcher.params[:named_fields])
97
+ assert_equal({genre: ['Animation'], foobar: ['Cat']}, searcher.params[:query_named_fields])
98
98
  end
99
99
 
100
100
  def test_where_merges_named_fields_with_same_key
101
101
  named_fields1 = {genre: 'Animation'}
102
102
  named_fields2 = {genre: 'Action'}
103
103
  searcher = Searcher.where(named_fields1).where(named_fields2)
104
- assert_equal({genre: %w(Animation Action)}, searcher.params[:named_fields])
104
+ assert_equal({genre: %w(Animation Action)}, searcher.params[:query_named_fields])
105
105
  end
106
106
 
107
107
  def test_where_gets_correct_url
@@ -113,21 +113,22 @@ module Inquisitio
113
113
  def test_where_gets_correct_url_with_fields_in_search
114
114
  searcher = Searcher.where(title: 'Star Wars')
115
115
  search_url = searcher.send(:search_url)
116
- assert(search_url.include?('q=%28and+%28or+title%3A%27Star+Wars%27%29%29&q.parser=structured'), "Search url should include query: #{search_url}")
116
+ assert /(\?|&)q=title%3A%27Star\+Wars%27(&|$)/ =~ search_url, "Search url should include query: #{search_url}"
117
+ assert /(\?|&)q.parser=structured(&|$)/ =~ search_url, "Search url should include parser: #{search_url}"
117
118
  end
118
119
 
119
120
  def test_where_works_with_array_in_a_hash
120
121
  criteria = {thing: %w(foo bar)}
121
122
  searcher = Searcher.where(criteria)
122
- assert_equal criteria, searcher.params[:named_fields]
123
+ assert_equal criteria, searcher.params[:query_named_fields]
123
124
  end
124
125
 
125
126
  def test_where_works_with_string_and_array
126
127
  str_criteria = 'Star Wars'
127
128
  hash_criteria = {thing: %w(foo bar)}
128
129
  searcher = Searcher.where(str_criteria).where(hash_criteria)
129
- assert_equal hash_criteria, searcher.params[:named_fields]
130
- assert_equal [str_criteria], searcher.params[:criteria]
130
+ assert_equal hash_criteria, searcher.params[:query_named_fields]
131
+ assert_equal [str_criteria], searcher.params[:query_terms]
131
132
  end
132
133
 
133
134
  def test_per_doesnt_mutate_searcher
@@ -303,7 +304,6 @@ module Inquisitio
303
304
  end
304
305
 
305
306
  def test_should_not_specify_return_by_default
306
- Inquisitio.config.api_version = '2013-01-01'
307
307
  searcher = Searcher.where('Star Wars')
308
308
  assert_equal [], searcher.params[:returns]
309
309
  refute searcher.send(:search_url).include? '&return='
@@ -405,17 +405,59 @@ module Inquisitio
405
405
  end
406
406
 
407
407
  def test_should_support_structured_parser
408
- Inquisitio.config.api_version = '2013-01-01'
409
408
  searcher = Searcher.where('star wars').parser(:structured)
410
409
  search_url = searcher.send(:search_url)
411
410
  assert search_url =~ /(\?|&)q\.parser=structured(&|$)/, "search url should include q.parser parameter:\n#{search_url}"
412
411
  end
413
412
 
414
413
  def test_should_support_any_parser
415
- Inquisitio.config.api_version = '2013-01-01'
416
414
  searcher = Searcher.where('star wars').parser(:foo_bar_baz)
417
415
  search_url = searcher.send(:search_url)
418
416
  assert search_url =~ /(\?|&)q\.parser=foo_bar_baz(&|$)/, "search url should include q.parser parameter:\n#{search_url}"
419
417
  end
418
+
419
+ def test_should_not_have_fq_if_no_filter
420
+ searcher = Searcher.where('star wars')
421
+ search_url = searcher.send(:search_url)
422
+ refute search_url =~ /(\?|&)fq=/, "search url should not include fq parameter:\n#{search_url}"
423
+ end
424
+
425
+ def test_should_take_a_filter_query
426
+ searcher = Searcher.where('star wars').filter('a new hope')
427
+ search_url = searcher.send(:search_url)
428
+ assert search_url =~ /(\?|&)fq=a\+new\+hope(&|$)/, "search url should include fq parameter:\n#{search_url}"
429
+ end
430
+
431
+ def test_should_take_a_filter_query_with_fields
432
+ searcher = Searcher.where('star wars').filter(tags: 'anewhope')
433
+ search_url = searcher.send(:search_url)
434
+ assert search_url =~ /(\?|&)fq=tags%3A%27anewhope%27(&|$)/, "search url should include fq parameter:\n#{search_url}"
435
+ end
436
+
437
+ def test_should_accept_empty_filter_to_reset_filters
438
+ searcher = Searcher.where('star wars').filter(tags: 'anewhope').filter(nil)
439
+ search_url = searcher.send(:search_url)
440
+ refute search_url =~ /(\?|&)fq=tags%3A%27anewhope%27(&|$)/, "search url should not include fq parameter:\n#{search_url}"
441
+ end
442
+
443
+ def test_should_tolerate_empty_filter
444
+ searcher = Searcher.where('star wars').filter(nil)
445
+ search_url = searcher.send(:search_url)
446
+ refute search_url =~ /(\?|&)fq=/, "search url should not include fq parameter:\n#{search_url}"
447
+ end
448
+
449
+ def test_should_take_a_facet
450
+ searcher = Searcher.where('star wars').facets(tags: {})
451
+ search_url = searcher.send(:search_url)
452
+ assert search_url =~ /(\?|&)facet\.tags=%7B%7D(&|$)/, "search url should include facet.tags parameter:\n#{search_url}"
453
+ end
454
+
455
+ def test_should_take_facets
456
+ searcher = Searcher.where('star wars').facets(tags: {}, genre: {sort:'bucket', size:5})
457
+ search_url = searcher.send(:search_url)
458
+ assert search_url =~ /(\?|&)facet\.tags=%7B%7D(&|$)/, "search url should include facet.tags parameter:\n#{search_url}"
459
+ assert search_url =~ /(\?|&)facet\.genre=%7B%22sort%22%3A%22bucket%22%2C%22size%22%3A5%7D(&|$)/, "search url should include facet.genre parameter:\n#{search_url}"
460
+ end
461
+
420
462
  end
421
463
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inquisitio
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Walker
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2015-07-22 00:00:00.000000000 Z
14
+ date: 2015-07-23 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: excon
@@ -120,6 +120,7 @@ files:
120
120
  - lib/inquisitio/active_support.rb
121
121
  - lib/inquisitio/configuration.rb
122
122
  - lib/inquisitio/document.rb
123
+ - lib/inquisitio/facets.rb
123
124
  - lib/inquisitio/indexer.rb
124
125
  - lib/inquisitio/inquisitio_error.rb
125
126
  - lib/inquisitio/logger.rb
@@ -129,6 +130,7 @@ files:
129
130
  - lib/inquisitio/version.rb
130
131
  - test/configuration_test.rb
131
132
  - test/document_test.rb
133
+ - test/facets_test.rb
132
134
  - test/indexer_test.rb
133
135
  - test/logger_test.rb
134
136
  - test/results_test.rb
@@ -162,6 +164,7 @@ summary: This wraps AWS CloudSearch in a Ruby Gem
162
164
  test_files:
163
165
  - test/configuration_test.rb
164
166
  - test/document_test.rb
167
+ - test/facets_test.rb
165
168
  - test/indexer_test.rb
166
169
  - test/logger_test.rb
167
170
  - test/results_test.rb