tire 0.4.0.pre → 0.4.0.rc

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.
Files changed (46) hide show
  1. data/README.markdown +6 -10
  2. data/examples/rails-application-template.rb +6 -6
  3. data/examples/tire-dsl.rb +10 -9
  4. data/lib/tire.rb +8 -0
  5. data/lib/tire/dsl.rb +7 -6
  6. data/lib/tire/index.rb +22 -12
  7. data/lib/tire/model/import.rb +3 -2
  8. data/lib/tire/model/indexing.rb +21 -13
  9. data/lib/tire/model/naming.rb +2 -1
  10. data/lib/tire/model/persistence.rb +1 -1
  11. data/lib/tire/results/collection.rb +20 -17
  12. data/lib/tire/results/item.rb +1 -1
  13. data/lib/tire/rubyext/ruby_1_8.rb +7 -0
  14. data/lib/tire/search.rb +33 -19
  15. data/lib/tire/search/facet.rb +5 -0
  16. data/lib/tire/search/query.rb +8 -3
  17. data/lib/tire/tasks.rb +47 -14
  18. data/lib/tire/utils.rb +17 -0
  19. data/lib/tire/version.rb +13 -2
  20. data/test/integration/active_model_indexing_test.rb +1 -0
  21. data/test/integration/active_model_searchable_test.rb +7 -5
  22. data/test/integration/active_record_searchable_test.rb +159 -72
  23. data/test/integration/count_test.rb +34 -0
  24. data/test/integration/dsl_search_test.rb +22 -0
  25. data/test/integration/explanation_test.rb +44 -0
  26. data/test/integration/facets_test.rb +15 -0
  27. data/test/integration/fuzzy_queries_test.rb +20 -0
  28. data/test/integration/mongoid_searchable_test.rb +1 -0
  29. data/test/integration/persistent_model_test.rb +22 -1
  30. data/test/integration/text_query_test.rb +17 -3
  31. data/test/models/active_record_models.rb +43 -1
  32. data/test/models/mongoid_models.rb +0 -1
  33. data/test/models/persistent_article_in_namespace.rb +12 -0
  34. data/test/models/supermodel_article.rb +5 -10
  35. data/test/test_helper.rb +16 -2
  36. data/test/unit/index_test.rb +90 -16
  37. data/test/unit/model_import_test.rb +4 -4
  38. data/test/unit/model_search_test.rb +13 -10
  39. data/test/unit/results_collection_test.rb +6 -0
  40. data/test/unit/results_item_test.rb +8 -0
  41. data/test/unit/search_facet_test.rb +9 -0
  42. data/test/unit/search_query_test.rb +36 -7
  43. data/test/unit/search_test.rb +70 -1
  44. data/test/unit/tire_test.rb +23 -12
  45. data/tire.gemspec +11 -8
  46. metadata +90 -48
@@ -28,7 +28,7 @@ module Tire
28
28
  end
29
29
 
30
30
  def [](key)
31
- @attributes[key]
31
+ @attributes[key.to_sym]
32
32
  end
33
33
 
34
34
  def id
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+
3
+ # Require URI escape/unescape compatibility layer from Rack
4
+ #
5
+ # See <http://www.ruby-doc.org/stdlib-1.9.3/libdoc/uri/rdoc/URI.html#method-c-encode_www_form_component>
6
+ #
7
+ require 'rack/backports/uri/common_18'
data/lib/tire/search.rb CHANGED
@@ -1,14 +1,14 @@
1
1
  module Tire
2
2
  module Search
3
3
  class SearchRequestFailed < StandardError; end
4
-
4
+
5
5
  class Search
6
6
 
7
- attr_reader :indices, :json, :query, :facets, :filters, :options
7
+ attr_reader :indices, :json, :query, :facets, :filters, :options, :explain
8
8
 
9
- def initialize(indices=nil, options = {}, &block)
9
+ def initialize(indices=nil, options={}, &block)
10
10
  @indices = Array(indices)
11
- @types = Array(options.delete(:type))
11
+ @types = Array(options.delete(:type)).map { |type| Utils.escape(type) }
12
12
  @options = options
13
13
 
14
14
  @path = ['/', @indices.join(','), @types.join(','), '_search'].compact.join('/').squeeze('/')
@@ -28,6 +28,10 @@ module Tire
28
28
  Configuration.url + @path
29
29
  end
30
30
 
31
+ def params
32
+ @options.empty? ? '' : '?' + @options.to_param
33
+ end
34
+
31
35
  def query(&block)
32
36
  @query = Query.new
33
37
  block.arity < 1 ? @query.instance_eval(&block) : block.call(@query)
@@ -77,12 +81,17 @@ module Tire
77
81
  self
78
82
  end
79
83
 
84
+ def explain(value)
85
+ @explain = value
86
+ self
87
+ end
88
+
80
89
  def version(value)
81
90
  @version = value
82
91
  end
83
92
 
84
93
  def perform
85
- @response = Configuration.client.get(self.url, self.to_json)
94
+ @response = Configuration.client.get(self.url + self.params, self.to_json)
86
95
  if @response.failure?
87
96
  STDERR.puts "[REQUEST FAILED] #{self.to_curl}\n"
88
97
  raise SearchRequestFailed, @response.to_s
@@ -95,26 +104,31 @@ module Tire
95
104
  end
96
105
 
97
106
  def to_curl
98
- %Q|curl -X GET "#{self.url}?pretty=true" -d '#{self.to_json}'|
107
+ %Q|curl -X GET "#{url}#{params.empty? ? '?' : params.to_s + '&'}pretty=true" -d '#{to_json}'|
99
108
  end
100
109
 
101
110
  def to_hash
102
- request = {}
103
- request.update( { :query => @query.to_hash } ) if @query
104
- request.update( { :sort => @sort.to_ary } ) if @sort
105
- request.update( { :facets => @facets.to_hash } ) if @facets
106
- request.update( { :filter => @filters.first.to_hash } ) if @filters && @filters.size == 1
107
- request.update( { :filter => { :and => @filters.map { |filter| filter.to_hash } } } ) if @filters && @filters.size > 1
108
- request.update( { :highlight => @highlight.to_hash } ) if @highlight
109
- request.update( { :size => @size } ) if @size
110
- request.update( { :from => @from } ) if @from
111
- request.update( { :fields => @fields } ) if @fields
112
- request.update( { :version => @version } ) if @version
113
- request
111
+ @options.delete(:payload) || begin
112
+ request = {}
113
+ request.update( { :query => @query.to_hash } ) if @query
114
+ request.update( { :sort => @sort.to_ary } ) if @sort
115
+ request.update( { :facets => @facets.to_hash } ) if @facets
116
+ request.update( { :filter => @filters.first.to_hash } ) if @filters && @filters.size == 1
117
+ request.update( { :filter => { :and => @filters.map {|filter| filter.to_hash} } } ) if @filters && @filters.size > 1
118
+ request.update( { :highlight => @highlight.to_hash } ) if @highlight
119
+ request.update( { :size => @size } ) if @size
120
+ request.update( { :from => @from } ) if @from
121
+ request.update( { :fields => @fields } ) if @fields
122
+ request.update( { :version => @version } ) if @version
123
+ request.update( { :explain => @explain } ) if @explain
124
+ request
125
+ end
114
126
  end
115
127
 
116
128
  def to_json
117
- to_hash.to_json
129
+ payload = to_hash
130
+ # TODO: Remove when deprecated interface is removed
131
+ payload.is_a?(String) ? payload : payload.to_json
118
132
  end
119
133
 
120
134
  def logged(error=nil)
@@ -51,6 +51,11 @@ module Tire
51
51
  @value = { :query => Query.new(&block).to_hash }
52
52
  end
53
53
 
54
+ def filter(field, value, options={})
55
+ @value = { :filter => { :term => { field => value }}.update(options) }
56
+ self
57
+ end
58
+
54
59
  def to_json
55
60
  to_hash.to_json
56
61
  end
@@ -7,8 +7,9 @@ module Tire
7
7
  block.arity < 1 ? self.instance_eval(&block) : block.call(self) if block_given?
8
8
  end
9
9
 
10
- def term(field, value)
11
- @value = { :term => { field => value } }
10
+ def term(field, value, options={})
11
+ query = { field => { :term => value }.update(options) }
12
+ @value = { :term => query }
12
13
  end
13
14
 
14
15
  def terms(field, value, options={})
@@ -30,7 +31,6 @@ module Tire
30
31
  def string(value, options={})
31
32
  @value = { :query_string => { :query => value } }
32
33
  @value[:query_string].update(options)
33
- # TODO: https://github.com/elasticsearch/elasticsearch/wiki/Query-String-Query
34
34
  @value
35
35
  end
36
36
 
@@ -41,6 +41,11 @@ module Tire
41
41
  @value
42
42
  end
43
43
 
44
+ def fuzzy(field, value, options={})
45
+ query = { field => { :term => value }.update(options) }
46
+ @value = { :fuzzy => query }
47
+ end
48
+
44
49
  def boolean(options={}, &block)
45
50
  @boolean ||= BooleanQuery.new(options)
46
51
  block.arity < 1 ? @boolean.instance_eval(&block) : block.call(@boolean) if block_given?
data/lib/tire/tasks.rb CHANGED
@@ -3,22 +3,20 @@ require 'benchmark'
3
3
 
4
4
  namespace :tire do
5
5
 
6
- usage = <<-DESC
7
- Import data from your model using paginate: rake environment tire:import CLASS='MyModel'
6
+ full_comment = <<-DESC.gsub(/ /, '')
7
+ Import data from your model using paginate: rake environment tire:import CLASS='MyModel'.
8
8
 
9
- Pass params for the `paginate` method:
10
- $ rake environment tire:import CLASS='Article' PARAMS='{:page => 1}'
9
+ Pass params for the `paginate` method:
10
+ $ rake environment tire:import CLASS='Article' PARAMS='{:page => 1}'
11
11
 
12
- Force rebuilding the index (delete and create):
13
- $ rake environment tire:import CLASS='Article' PARAMS='{:page => 1}' FORCE=1
12
+ Force rebuilding the index (delete and create):
13
+ $ rake environment tire:import CLASS='Article' PARAMS='{:page => 1}' FORCE=1
14
14
 
15
- Set target index name:
16
- $ rake environment tire:import CLASS='Article' INDEX='articles-new'
17
-
15
+ Set target index name:
16
+ $ rake environment tire:import CLASS='Article' INDEX='articles-new'
18
17
  DESC
19
-
20
- desc usage.split("\n").first.to_s
21
- task :import do
18
+ desc full_comment
19
+ task :import do |t|
22
20
 
23
21
  def elapsed_to_human(elapsed)
24
22
  hour = 60*60
@@ -37,13 +35,15 @@ namespace :tire do
37
35
  end
38
36
 
39
37
  if ENV['CLASS'].to_s == ''
40
- puts '='*80, 'USAGE', '='*80, usage.gsub(/ /, '')
38
+ puts '='*90, 'USAGE', '='*90, full_comment, ""
41
39
  exit(1)
42
40
  end
43
41
 
44
42
  klass = eval(ENV['CLASS'].to_s)
45
43
  params = eval(ENV['PARAMS'].to_s) || {}
46
44
 
45
+ params.update :method => 'paginate'
46
+
47
47
  index = Tire::Index.new( ENV['INDEX'] || klass.tire.index.name )
48
48
 
49
49
  if ENV['FORCE']
@@ -80,7 +80,7 @@ namespace :tire do
80
80
 
81
81
  # Import the documents
82
82
  #
83
- index.import(klass, 'paginate', params) do |documents|
83
+ index.import(klass, params) do |documents|
84
84
 
85
85
  if total
86
86
  done += documents.to_a.size
@@ -97,6 +97,39 @@ namespace :tire do
97
97
  end
98
98
 
99
99
  puts "", '='*80, "Import finished in #{elapsed_to_human(elapsed)}"
100
+ end
101
+
102
+ namespace :index do
103
+
104
+ full_comment = <<-DESC.gsub(/ /, '')
105
+ Delete indices passed in the INDEX environment variable; separate multiple indices by comma.
106
+
107
+ Pass name of a single index to drop in the INDEX environmnet variable:
108
+ $ rake environment tire:index:drop INDEX=articles
109
+
110
+ Pass names of multiple indices to drop in the INDEX or INDICES environmnet variable:
111
+ $ rake environment tire:index:drop INDICES=articles-2011-01,articles-2011-02
112
+
113
+ DESC
114
+ desc full_comment
115
+ task :drop do
116
+ index_names = (ENV['INDEX'] || ENV['INDICES']).to_s.split(/,\s*/)
117
+
118
+ if index_names.empty?
119
+ puts '='*90, 'USAGE', '='*90, full_comment, ""
120
+ exit(1)
121
+ end
122
+
123
+ index_names.each do |name|
124
+ index = Tire::Index.new(name)
125
+ print "* Deleting index \e[1m#{index.name}\e[0m... "
126
+ puts index.delete ? "\e[32mOK\e[0m" : "\e[31mFAILED\e[0m | #{index.response.body}"
127
+ end
128
+
129
+ puts ""
130
+
131
+ end
100
132
 
101
133
  end
134
+
102
135
  end
data/lib/tire/utils.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'uri'
2
+
3
+ module Tire
4
+ module Utils
5
+
6
+ def escape(s)
7
+ URI.encode_www_form_component(s.to_s)
8
+ end
9
+
10
+ def unescape(s)
11
+ s = s.to_s.respond_to?(:force_encoding) ? s.to_s.force_encoding(Encoding::UTF_8) : s.to_s
12
+ URI.decode_www_form_component(s)
13
+ end
14
+
15
+ extend self
16
+ end
17
+ end
data/lib/tire/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Tire
2
- VERSION = "0.4.0.pre"
2
+ VERSION = "0.4.0.rc"
3
3
 
4
4
  CHANGELOG =<<-END
5
5
  IMPORTANT CHANGES LATELY:
@@ -9,8 +9,19 @@ module Tire
9
9
  * Changed that search in persistence returns instances of model not Item
10
10
  * Fixed errors in the Curb client
11
11
  * Re-raise the RestClient::RequestTimeout and RestClient::ServerBrokeConnection exceptions
12
- * Added the `:as` option for model mapping to dynamically set property value in serialization
12
+ * Index#bulk_store and Index#import support the `:raise` option to re-raise exceptions
13
13
  * Prefer ELASTICSEARCH_URL environment variable as the default URL, if present
14
14
  * Added the "text" search query
15
+ * Deprecated the support for passing JSON strings to `Index#store`
16
+ * ActiveModel mapping has the `:as` option dynamically set property value for serialization
17
+ * ActiveModel supports any level of mappings in `mapping`
18
+ * ActiveModel integration now can eagerly load records of multiple types/classes
19
+ * ActiveModel integration now properly supports namespaced models
20
+ * Added support for passing search params (`search_type`, `timeout`, etc.) to search requests
21
+ * Added the "tire:index:drop" Rake task
22
+ * Added the "Filter Facets" support
23
+ * Added the "Fuzzy" search query type
24
+ * Test suite refactorings and changes
25
+ * Relaxed gem dependencies
15
26
  END
16
27
  end
@@ -1,4 +1,5 @@
1
1
  require 'test_helper'
2
+ require File.expand_path('../../models/supermodel_article', __FILE__)
2
3
 
3
4
  module Tire
4
5
 
@@ -1,4 +1,5 @@
1
1
  require 'test_helper'
2
+ require File.expand_path('../../models/supermodel_article', __FILE__)
2
3
 
3
4
  module Tire
4
5
 
@@ -7,13 +8,14 @@ module Tire
7
8
 
8
9
  def setup
9
10
  super
10
- SupermodelArticle.delete_all
11
+ Redis::Persistence.config.redis = Redis.new db: ENV['REDIS_PERSISTENCE_TEST_DATABASE'] || 14
12
+ Redis::Persistence.config.redis.flushdb
11
13
  @model = SupermodelArticle.new :title => 'Test'
12
14
  end
13
15
 
14
16
  def teardown
15
17
  super
16
- SupermodelArticle.delete_all
18
+ SupermodelArticle.all.each { |a| a.destroy }
17
19
  end
18
20
 
19
21
  context "ActiveModel integration" do
@@ -54,7 +56,7 @@ module Tire
54
56
  assert_instance_of Results::Item, results.first
55
57
  assert_equal 'Test', results.first.title
56
58
  assert_not_nil results.first._score
57
- assert_equal id, results.first.id
59
+ assert_equal id.to_s, results.first.id.to_s
58
60
  end
59
61
 
60
62
  should "remove document from index on destroy" do
@@ -72,8 +74,8 @@ module Tire
72
74
  end
73
75
 
74
76
  should "retrieve sorted documents by IDs returned from search" do
75
- SupermodelArticle.create! :title => 'foo'
76
- SupermodelArticle.create! :id => 'abc123', :title => 'bar'
77
+ SupermodelArticle.create :title => 'foo'
78
+ SupermodelArticle.create :id => 'abc123', :title => 'bar'
77
79
 
78
80
  SupermodelArticle.index.refresh
79
81
  results = SupermodelArticle.search 'foo OR bar^100'
@@ -37,36 +37,37 @@ module Tire
37
37
 
38
38
  context "ActiveRecord integration" do
39
39
 
40
- setup do
40
+ setup do
41
41
  ActiveRecordArticle.destroy_all
42
42
  Tire.index('active_record_articles').delete
43
43
 
44
44
  load File.expand_path('../../models/active_record_models.rb', __FILE__)
45
45
  end
46
+
46
47
  teardown do
47
48
  ActiveRecordArticle.destroy_all
48
49
  Tire.index('active_record_articles').delete
49
50
  end
50
51
 
51
52
  should "configure mapping" do
52
- assert_equal 'snowball', ActiveRecordArticle.mapping[:title][:analyzer]
53
- assert_equal 10, ActiveRecordArticle.mapping[:title][:boost]
54
-
55
- assert_equal 'snowball', ActiveRecordArticle.index.mapping['active_record_article']['properties']['title']['analyzer']
53
+ assert_equal 'snowball', ActiveRecordArticle.mapping[:title][:analyzer]
54
+ assert_equal 10, ActiveRecordArticle.mapping[:title][:boost]
55
+
56
+ assert_equal 'snowball', ActiveRecordArticle.index.mapping['active_record_article']['properties']['title']['analyzer']
56
57
  end
57
-
58
+
58
59
  should "save document into index on save and find it" do
59
60
  a = ActiveRecordArticle.new :title => 'Test'
60
61
  a.save!
61
62
  id = a.id
62
-
63
+
63
64
  a.index.refresh
64
-
65
+
65
66
  results = ActiveRecordArticle.search 'test'
66
-
67
+
67
68
  assert results.any?
68
69
  assert_equal 1, results.count
69
-
70
+
70
71
  assert_instance_of Results::Item, results.first
71
72
  assert_not_nil results.first.id
72
73
  assert_equal id.to_s, results.first.id.to_s
@@ -74,43 +75,43 @@ module Tire
74
75
  assert_not_nil results.first._score
75
76
  assert_equal 'Test', results.first.title
76
77
  end
77
-
78
+
78
79
  should "raise exception on invalid query" do
79
80
  ActiveRecordArticle.create! :title => 'Test'
80
-
81
+
81
82
  assert_raise Search::SearchRequestFailed do
82
83
  ActiveRecordArticle.search '[x'
83
84
  end
84
85
  end
85
-
86
+
86
87
  context "with eager loading" do
87
88
  setup do
88
89
  ActiveRecordArticle.destroy_all
89
90
  5.times { |n| ActiveRecordArticle.create! :title => "Test #{n+1}" }
90
91
  ActiveRecordArticle.index.refresh
91
92
  end
92
-
93
+
93
94
  should "load records on query search" do
94
95
  results = ActiveRecordArticle.search '"Test 1"', :load => true
95
-
96
+
96
97
  assert results.any?
97
98
  assert_equal ActiveRecordArticle.find(1), results.first
98
99
  end
99
-
100
+
100
101
  should "load records on block search" do
101
102
  results = ActiveRecordArticle.search :load => true do
102
103
  query { string '"Test 1"' }
103
104
  end
104
-
105
+
105
106
  assert_equal ActiveRecordArticle.find(1), results.first
106
107
  end
107
-
108
+
108
109
  should "load records with options on query search" do
109
110
  assert_equal ActiveRecordArticle.find(['1'], :include => 'comments').first,
110
111
  ActiveRecordArticle.search('"Test 1"',
111
112
  :load => { :include => 'comments' }).results.first
112
113
  end
113
-
114
+
114
115
  should "return empty collection for nonmatching query" do
115
116
  assert_nothing_raised do
116
117
  results = ActiveRecordArticle.search :load => true do
@@ -121,101 +122,101 @@ module Tire
121
122
  end
122
123
  end
123
124
  end
124
-
125
+
125
126
  should "remove document from index on destroy" do
126
127
  a = ActiveRecordArticle.new :title => 'Test remove...'
127
128
  a.save!
128
129
  assert_equal 1, ActiveRecordArticle.count
129
-
130
+
130
131
  a.destroy
131
132
  assert_equal 0, ActiveRecordArticle.all.size
132
-
133
+
133
134
  a.index.refresh
134
135
  results = ActiveRecordArticle.search 'test'
135
136
  assert_equal 0, results.count
136
137
  end
137
-
138
+
138
139
  should "return documents with scores" do
139
140
  ActiveRecordArticle.create! :title => 'foo'
140
141
  ActiveRecordArticle.create! :title => 'bar'
141
-
142
+
142
143
  ActiveRecordArticle.index.refresh
143
144
  results = ActiveRecordArticle.search 'foo OR bar^100'
144
145
  assert_equal 2, results.count
145
-
146
+
146
147
  assert_equal 'bar', results.first.title
147
148
  end
148
-
149
+
149
150
  context "with pagination" do
150
151
  setup do
151
152
  1.upto(9) { |number| ActiveRecordArticle.create :title => "Test#{number}" }
152
153
  ActiveRecordArticle.index.refresh
153
154
  end
154
-
155
+
155
156
  context "and parameter searches" do
156
-
157
+
157
158
  should "find first page with five results" do
158
159
  results = ActiveRecordArticle.search 'test*', :sort => 'title', :per_page => 5, :page => 1
159
160
  assert_equal 5, results.size
160
-
161
+
161
162
  # WillPaginate
162
163
  #
163
164
  assert_equal 2, results.total_pages
164
165
  assert_equal 1, results.current_page
165
166
  assert_equal nil, results.previous_page
166
167
  assert_equal 2, results.next_page
167
-
168
+
168
169
  # Kaminari
169
170
  #
170
171
  assert_equal 5, results.limit_value
171
172
  assert_equal 9, results.total_count
172
173
  assert_equal 2, results.num_pages
173
174
  assert_equal 0, results.offset_value
174
-
175
+
175
176
  assert_equal 'Test1', results.first.title
176
177
  end
177
-
178
+
178
179
  should "find next page with five results" do
179
180
  results = ActiveRecordArticle.search 'test*', :sort => 'title', :per_page => 5, :page => 2
180
181
  assert_equal 4, results.size
181
-
182
+
182
183
  assert_equal 2, results.total_pages
183
184
  assert_equal 2, results.current_page
184
185
  assert_equal 1, results.previous_page
185
186
  assert_equal nil, results.next_page
186
-
187
+
187
188
  #kaminari
188
189
  assert_equal 5, results.limit_value
189
190
  assert_equal 9, results.total_count
190
191
  assert_equal 2, results.num_pages
191
192
  assert_equal 5, results.offset_value
192
-
193
+
193
194
  assert_equal 'Test6', results.first.title
194
195
  end
195
-
196
+
196
197
  should "find not find missing page" do
197
198
  results = ActiveRecordArticle.search 'test*', :sort => 'title', :per_page => 5, :page => 3
198
199
  assert_equal 0, results.size
199
-
200
+
200
201
  assert_equal 2, results.total_pages
201
202
  assert_equal 3, results.current_page
202
203
  assert_equal 2, results.previous_page
203
204
  assert_equal nil, results.next_page
204
-
205
+
205
206
  #kaminari
206
207
  assert_equal 5, results.limit_value
207
208
  assert_equal 9, results.total_count
208
209
  assert_equal 2, results.num_pages
209
210
  assert_equal 10, results.offset_value
210
-
211
+
211
212
  assert_nil results.first
212
213
  end
213
-
214
+
214
215
  end
215
-
216
+
216
217
  context "and block searches" do
217
218
  setup { @q = 'test*' }
218
-
219
+
219
220
  should "find first page with five results" do
220
221
  results = ActiveRecordArticle.search do |search|
221
222
  search.query { |query| query.string @q }
@@ -224,15 +225,15 @@ module Tire
224
225
  search.size 5
225
226
  end
226
227
  assert_equal 5, results.size
227
-
228
+
228
229
  assert_equal 2, results.total_pages
229
230
  assert_equal 1, results.current_page
230
231
  assert_equal nil, results.previous_page
231
232
  assert_equal 2, results.next_page
232
-
233
+
233
234
  assert_equal 'Test1', results.first.title
234
235
  end
235
-
236
+
236
237
  should "find next page with five results" do
237
238
  results = ActiveRecordArticle.search do |search|
238
239
  search.query { |query| query.string @q }
@@ -241,15 +242,15 @@ module Tire
241
242
  search.size 5
242
243
  end
243
244
  assert_equal 4, results.size
244
-
245
+
245
246
  assert_equal 2, results.total_pages
246
247
  assert_equal 2, results.current_page
247
248
  assert_equal 1, results.previous_page
248
249
  assert_equal nil, results.next_page
249
-
250
+
250
251
  assert_equal 'Test6', results.first.title
251
252
  end
252
-
253
+
253
254
  should "not find a missing page" do
254
255
  results = ActiveRecordArticle.search do |search|
255
256
  search.query { |query| query.string @q }
@@ -258,100 +259,186 @@ module Tire
258
259
  search.size 5
259
260
  end
260
261
  assert_equal 0, results.size
261
-
262
+
262
263
  assert_equal 2, results.total_pages
263
264
  assert_equal 3, results.current_page
264
265
  assert_equal 2, results.previous_page
265
266
  assert_equal nil, results.next_page
266
-
267
+
267
268
  assert_nil results.first
268
269
  end
269
-
270
+
270
271
  end
271
-
272
+
272
273
  end
273
-
274
+
274
275
  context "with proxy" do
275
-
276
+
276
277
  should "allow access to Tire instance methods" do
277
278
  a = ActiveRecordClassWithTireMethods.create :title => 'One'
278
279
  assert_equal "THIS IS MY INDEX!", a.index
279
280
  assert_instance_of Tire::Index, a.tire.index
280
281
  assert a.tire.index.exists?, "Index should exist"
281
282
  end
282
-
283
+
283
284
  should "allow access to Tire class methods" do
284
285
  class ::ActiveRecordClassWithTireMethods < ActiveRecord::Base
285
286
  def self.search(*)
286
287
  "THIS IS MY SEARCH!"
287
288
  end
288
289
  end
289
-
290
+
290
291
  ActiveRecordClassWithTireMethods.create :title => 'One'
291
292
  ActiveRecordClassWithTireMethods.tire.index.refresh
292
-
293
+
293
294
  assert_equal "THIS IS MY SEARCH!", ActiveRecordClassWithTireMethods.search
294
-
295
+
295
296
  results = ActiveRecordClassWithTireMethods.tire.search 'one'
296
-
297
+
297
298
  assert_equal 'One', results.first.title
298
299
  end
299
-
300
+
300
301
  end
301
-
302
+
302
303
  context "with dynamic index name" do
303
304
  setup do
304
305
  @a = ActiveRecordClassWithDynamicIndexName.create! :title => 'Test'
305
306
  @a.index.refresh
306
307
  end
307
-
308
+
308
309
  should "search in proper index" do
309
310
  assert_equal 'dynamic_index', ActiveRecordClassWithDynamicIndexName.index.name
310
311
  assert_equal 'dynamic_index', @a.index.name
311
-
312
+
312
313
  results = ActiveRecordClassWithDynamicIndexName.search 'test'
313
314
  assert_equal 'dynamic_index', results.first._index
314
315
  end
315
316
  end
316
-
317
+
317
318
  context "within Rails" do
318
-
319
+
319
320
  setup do
320
321
  module ::Rails; end
321
-
322
+
322
323
  a = ActiveRecordArticle.new :title => 'Test'
323
324
  a.comments.build :author => 'fool', :body => 'Works!'
324
325
  a.stats.build :pageviews => 12, :period => '2011-08'
325
326
  a.save!
326
327
  @id = a.id.to_s
327
-
328
+
328
329
  a.index.refresh
329
330
  @item = ActiveRecordArticle.search('test').first
330
331
  end
331
-
332
+
332
333
  should "have access to indexed properties" do
333
334
  assert_equal 'Test', @item.title
334
335
  assert_equal 'fool', @item.comments.first.author
335
336
  assert_equal 12, @item.stats.first.pageviews
336
337
  end
337
-
338
+
338
339
  should "load the underlying models" do
339
340
  assert_instance_of Results::Item, @item
340
341
  assert_instance_of ActiveRecordArticle, @item.load
341
342
  assert_equal 'Test', @item.load.title
342
-
343
+
343
344
  assert_instance_of Results::Item, @item.comments.first
344
345
  assert_instance_of ActiveRecordComment, @item.comments.first.load
345
346
  assert_equal 'fool', @item.comments.first.load.author
346
347
  end
347
-
348
+
348
349
  should "load the underlying model with options" do
349
350
  ActiveRecordArticle.expects(:find).with(@id, :include => 'comments')
350
351
  @item.load(:include => 'comments')
351
352
  end
352
-
353
+
353
354
  end
355
+
356
+ context "with multiple class instances in one index" do
357
+ setup do
358
+ ActiveRecord::Schema.define do
359
+ create_table(:active_record_assets) { |t| t.string :title, :timestamp }
360
+ create_table(:active_record_model_one) { |t| t.string :title, :timestamp }
361
+ create_table(:active_record_model_two) { |t| t.string :title, :timestamp }
362
+ end
363
+
364
+ ActiveRecordModelOne.create :title => 'Title One', timestamp: Time.now.to_i
365
+ ActiveRecordModelTwo.create :title => 'Title Two', timestamp: Time.now.to_i
366
+ ActiveRecordModelOne.tire.index.refresh
367
+ ActiveRecordModelTwo.tire.index.refresh
368
+
369
+
370
+ ActiveRecordVideo.create! :title => 'Title One', timestamp: Time.now.to_i
371
+ ActiveRecordPhoto.create! :title => 'Title Two', timestamp: Time.now.to_i
372
+ ActiveRecordAsset.tire.index.refresh
373
+ end
374
+
375
+ teardown do
376
+ ActiveRecordModelOne.destroy_all
377
+ ActiveRecordModelTwo.destroy_all
378
+ ActiveRecordModelOne.tire.index.delete
379
+ ActiveRecordModelTwo.tire.index.delete
380
+
381
+ ActiveRecordAsset.destroy_all
382
+ ActiveRecordAsset.tire.index.delete
383
+ ActiveRecordModelOne.destroy_all
384
+ end
385
+
386
+ should "eagerly load instances of multiple classes, from multiple indices" do
387
+ s = Tire.search ['active_record_model_one', 'active_record_model_two'], :load => true do
388
+ query { string 'title' }
389
+ sort { by :timestamp }
390
+ end
391
+
392
+ # puts s.results[0].inspect
393
+
394
+ assert_equal 2, s.results.length
395
+ assert_instance_of ActiveRecordModelOne, s.results[0]
396
+ assert_instance_of ActiveRecordModelTwo, s.results[1]
397
+ end
398
+
399
+ should "eagerly load all STI descendant records" do
400
+ s = Tire.search('active_record_assets', :load => true) do
401
+ query { string 'title' }
402
+ sort { by :timestamp }
403
+ end
404
+
405
+ assert_equal 2, s.results.length
406
+ assert_instance_of ActiveRecordVideo, s.results[0]
407
+ assert_instance_of ActiveRecordPhoto, s.results[1]
408
+ end
409
+ end
410
+
411
+ context "with namespaced models" do
412
+ setup do
413
+ ActiveRecord::Schema.define { create_table(:active_record_namespace_my_models) { |t| t.string :title, :timestamp } }
414
+
415
+ ActiveRecordNamespace::MyModel.create :title => 'Test'
416
+ ActiveRecordNamespace::MyModel.tire.index.refresh
417
+ end
354
418
 
419
+ teardown do
420
+ ActiveRecordNamespace::MyModel.destroy_all
421
+ ActiveRecordNamespace::MyModel.tire.index.delete
422
+ end
423
+
424
+ should "save document into index on save and find it" do
425
+ results = ActiveRecordNamespace::MyModel.search 'test'
426
+
427
+ assert results.any?, "No results returned: #{results.inspect}"
428
+ assert_equal 1, results.count
429
+
430
+ assert_instance_of Results::Item, results.first
431
+ end
432
+
433
+ should "eagerly load the records from returned hits" do
434
+ results = ActiveRecordNamespace::MyModel.search 'test', :load => true
435
+
436
+ assert results.any?, "No results returned: #{results.inspect}"
437
+ assert_instance_of ActiveRecordNamespace::MyModel, results.first
438
+ assert_equal ActiveRecordNamespace::MyModel.find(1), results.first
439
+ end
440
+
441
+ end
355
442
  end
356
443
 
357
444
  end