inquisitio 2.0.0 → 2.1.0

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.
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