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.
- data/README.markdown +6 -10
- data/examples/rails-application-template.rb +6 -6
- data/examples/tire-dsl.rb +10 -9
- data/lib/tire.rb +8 -0
- data/lib/tire/dsl.rb +7 -6
- data/lib/tire/index.rb +22 -12
- data/lib/tire/model/import.rb +3 -2
- data/lib/tire/model/indexing.rb +21 -13
- data/lib/tire/model/naming.rb +2 -1
- data/lib/tire/model/persistence.rb +1 -1
- data/lib/tire/results/collection.rb +20 -17
- data/lib/tire/results/item.rb +1 -1
- data/lib/tire/rubyext/ruby_1_8.rb +7 -0
- data/lib/tire/search.rb +33 -19
- data/lib/tire/search/facet.rb +5 -0
- data/lib/tire/search/query.rb +8 -3
- data/lib/tire/tasks.rb +47 -14
- data/lib/tire/utils.rb +17 -0
- data/lib/tire/version.rb +13 -2
- data/test/integration/active_model_indexing_test.rb +1 -0
- data/test/integration/active_model_searchable_test.rb +7 -5
- data/test/integration/active_record_searchable_test.rb +159 -72
- data/test/integration/count_test.rb +34 -0
- data/test/integration/dsl_search_test.rb +22 -0
- data/test/integration/explanation_test.rb +44 -0
- data/test/integration/facets_test.rb +15 -0
- data/test/integration/fuzzy_queries_test.rb +20 -0
- data/test/integration/mongoid_searchable_test.rb +1 -0
- data/test/integration/persistent_model_test.rb +22 -1
- data/test/integration/text_query_test.rb +17 -3
- data/test/models/active_record_models.rb +43 -1
- data/test/models/mongoid_models.rb +0 -1
- data/test/models/persistent_article_in_namespace.rb +12 -0
- data/test/models/supermodel_article.rb +5 -10
- data/test/test_helper.rb +16 -2
- data/test/unit/index_test.rb +90 -16
- data/test/unit/model_import_test.rb +4 -4
- data/test/unit/model_search_test.rb +13 -10
- data/test/unit/results_collection_test.rb +6 -0
- data/test/unit/results_item_test.rb +8 -0
- data/test/unit/search_facet_test.rb +9 -0
- data/test/unit/search_query_test.rb +36 -7
- data/test/unit/search_test.rb +70 -1
- data/test/unit/tire_test.rb +23 -12
- data/tire.gemspec +11 -8
- metadata +90 -48
data/lib/tire/results/item.rb
CHANGED
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
|
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 "#{
|
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
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
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)
|
data/lib/tire/search/facet.rb
CHANGED
data/lib/tire/search/query.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
7
|
-
|
6
|
+
full_comment = <<-DESC.gsub(/ /, '')
|
7
|
+
Import data from your model using paginate: rake environment tire:import CLASS='MyModel'.
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
Pass params for the `paginate` method:
|
10
|
+
$ rake environment tire:import CLASS='Article' PARAMS='{:page => 1}'
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
Force rebuilding the index (delete and create):
|
13
|
+
$ rake environment tire:import CLASS='Article' PARAMS='{:page => 1}' FORCE=1
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
Set target index name:
|
16
|
+
$ rake environment tire:import CLASS='Article' INDEX='articles-new'
|
18
17
|
DESC
|
19
|
-
|
20
|
-
|
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 '='*
|
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,
|
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.
|
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
|
-
*
|
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
|
|
@@ -7,13 +8,14 @@ module Tire
|
|
7
8
|
|
8
9
|
def setup
|
9
10
|
super
|
10
|
-
|
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.
|
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
|
76
|
-
SupermodelArticle.create
|
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
|
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
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|