tire 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gitignore +1 -1
  2. data/.yardopts +1 -0
  3. data/README.markdown +2 -2
  4. data/examples/rails-application-template.rb +20 -6
  5. data/lib/tire.rb +2 -0
  6. data/lib/tire/alias.rb +1 -1
  7. data/lib/tire/configuration.rb +8 -0
  8. data/lib/tire/dsl.rb +69 -2
  9. data/lib/tire/index.rb +33 -20
  10. data/lib/tire/model/indexing.rb +7 -1
  11. data/lib/tire/model/persistence.rb +7 -4
  12. data/lib/tire/model/persistence/attributes.rb +1 -1
  13. data/lib/tire/model/persistence/finders.rb +4 -16
  14. data/lib/tire/model/search.rb +21 -8
  15. data/lib/tire/multi_search.rb +263 -0
  16. data/lib/tire/results/collection.rb +78 -49
  17. data/lib/tire/results/item.rb +6 -3
  18. data/lib/tire/results/pagination.rb +15 -1
  19. data/lib/tire/rubyext/ruby_1_8.rb +1 -7
  20. data/lib/tire/rubyext/uri_escape.rb +74 -0
  21. data/lib/tire/search.rb +33 -11
  22. data/lib/tire/search/facet.rb +8 -3
  23. data/lib/tire/search/filter.rb +1 -1
  24. data/lib/tire/search/highlight.rb +1 -1
  25. data/lib/tire/search/queries/match.rb +40 -0
  26. data/lib/tire/search/query.rb +42 -6
  27. data/lib/tire/search/scan.rb +1 -1
  28. data/lib/tire/search/script_field.rb +1 -1
  29. data/lib/tire/search/sort.rb +1 -1
  30. data/lib/tire/tasks.rb +17 -14
  31. data/lib/tire/version.rb +26 -8
  32. data/test/integration/active_record_searchable_test.rb +248 -129
  33. data/test/integration/boosting_queries_test.rb +32 -0
  34. data/test/integration/custom_score_queries_test.rb +1 -0
  35. data/test/integration/dsl_search_test.rb +9 -1
  36. data/test/integration/facets_test.rb +19 -6
  37. data/test/integration/match_query_test.rb +79 -0
  38. data/test/integration/multi_search_test.rb +114 -0
  39. data/test/integration/persistent_model_test.rb +58 -0
  40. data/test/models/article.rb +1 -1
  41. data/test/models/persistent_article_in_index.rb +16 -0
  42. data/test/models/persistent_article_with_defaults.rb +4 -3
  43. data/test/test_helper.rb +3 -1
  44. data/test/unit/configuration_test.rb +10 -0
  45. data/test/unit/index_test.rb +69 -27
  46. data/test/unit/model_initialization_test.rb +31 -0
  47. data/test/unit/model_persistence_test.rb +21 -7
  48. data/test/unit/model_search_test.rb +56 -5
  49. data/test/unit/multi_search_test.rb +304 -0
  50. data/test/unit/results_collection_test.rb +42 -2
  51. data/test/unit/results_item_test.rb +4 -0
  52. data/test/unit/search_facet_test.rb +35 -11
  53. data/test/unit/search_query_test.rb +96 -0
  54. data/test/unit/search_test.rb +60 -3
  55. data/test/unit/tire_test.rb +14 -0
  56. data/tire.gemspec +0 -1
  57. metadata +75 -44
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ module Tire
4
+
5
+ class BoostingQueriesIntegrationTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ context "Boosting queries" do
9
+
10
+ should "allow to set multiple queries per condition" do
11
+ s = Tire.search('articles-test') do
12
+ query do
13
+ boosting(:negative_boost => 0.2) do
14
+ positive do
15
+ string "Two One"
16
+ end
17
+ negative do
18
+ term :tags, 'python'
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ assert_equal 'One', s.results[0].title
25
+ assert_equal 'Two', s.results[1].title
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -58,6 +58,7 @@ module Tire
58
58
  assert_equal 2, s.results.size
59
59
  assert_equal ['Three', 'Two'], s.results.map(&:title)
60
60
 
61
+ assert_equal 5.0, s.results.max_score
61
62
  assert_equal 5.0, s.results[0]._score
62
63
  assert_equal 3.0, s.results[1]._score
63
64
  end
@@ -10,11 +10,19 @@ module Tire
10
10
  should "allow passing search payload as a Hash" do
11
11
  s = Tire.search 'articles-test', :query => { :query_string => { :query => 'ruby' } },
12
12
  :facets => { 'tags' => { :filter => { :term => {:tags => 'ruby' } } } }
13
- # p s.results
13
+
14
14
  assert_equal 2, s.results.count
15
15
  assert_equal 2, s.results.facets['tags']['count']
16
16
  end
17
17
 
18
+ should "allow building search query iteratively" do
19
+ s = Tire.search 'articles-test'
20
+ s.query { string 'T*' }
21
+ s.filter :terms, :tags => ['java']
22
+
23
+ assert_equal 1, s.results.count
24
+ end
25
+
18
26
  end
19
27
 
20
28
  end
@@ -59,6 +59,21 @@ module Tire
59
59
  assert_equal 1, s.results.facets['tags']['terms'].first['count'].to_i
60
60
  end
61
61
 
62
+ should "allow to define the facet filter with DSL" do
63
+ s = Tire.search('articles-test', :search_type => 'count') do
64
+ facet 'tags' do
65
+ terms :tags
66
+ facet_filter :range, { :published_on => { :from => '2011-01-01', :to => '2011-01-01' } }
67
+ end
68
+ end
69
+
70
+ assert_equal 1, s.results.facets.size
71
+ assert_equal 'ruby', s.results.facets['tags']['terms'].first['term']
72
+ assert_equal 1, s.results.facets['tags']['terms'].first['count'].to_i
73
+ end
74
+
75
+ end
76
+
62
77
  context "terms" do
63
78
  setup do
64
79
  @s = Tire.search('articles-test') do
@@ -238,19 +253,17 @@ module Tire
238
253
  end
239
254
 
240
255
  context "filter" do
241
- should "return a filtered facet" do
242
- s = Tire.search('articles-test') do
243
- query { all }
256
+
257
+ should "return a filter facet" do
258
+ s = Tire.search('articles-test', :search_type => 'count') do
244
259
  facet 'filtered' do
245
- filter :tags, 'ruby'
260
+ filter :range, :words => { :from => 100, :to => 200 }
246
261
  end
247
262
  end
248
263
 
249
- assert_equal 5, s.results.size, s.results.inspect
250
264
  facets = s.results.facets["filtered"]
251
265
  assert_equal 2, facets["count"], facets.inspect
252
266
  end
253
- end
254
267
 
255
268
  end
256
269
 
@@ -0,0 +1,79 @@
1
+ require 'test_helper'
2
+
3
+ module Tire
4
+
5
+ class MatchQueryIntegrationTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ context "Match query" do
9
+ setup do
10
+ Tire.index 'match-query-test' do
11
+ delete
12
+ create settings: { index: { number_of_shards: 1, number_of_replicas: 0 } },
13
+ mappings: {
14
+ document: { properties: {
15
+ last_name: { type: 'string', analyzer: 'english' },
16
+ age: { type: 'integer' }
17
+ } }
18
+ }
19
+
20
+ store first_name: 'John', last_name: 'Smith', age: 30, gender: 'male'
21
+ store first_name: 'John', last_name: 'Smithson', age: 25, gender: 'male'
22
+ store first_name: 'Adam', last_name: 'Smith', age: 75, gender: 'male'
23
+ store first_name: 'Mary', last_name: 'John', age: 30, gender: 'female'
24
+ refresh
25
+ end
26
+ end
27
+
28
+ teardown do
29
+ Tire.index('match-query-test').delete
30
+ end
31
+
32
+ should "find documents by single field" do
33
+ s = Tire.search 'match-query-test' do
34
+ query do
35
+ match :last_name, 'Smith'
36
+ end
37
+ end
38
+
39
+ assert_equal 2, s.results.count
40
+ end
41
+
42
+ should "find document by multiple fields with multi_match" do
43
+ s = Tire.search 'match-query-test' do
44
+ query do
45
+ match [:first_name, :last_name], 'John'
46
+ end
47
+ end
48
+
49
+ assert_equal 3, s.results.count
50
+ end
51
+
52
+ should "find documents by prefix" do
53
+ s = Tire.search 'match-query-test' do
54
+ query do
55
+ match :last_name, 'Smi', type: 'phrase_prefix'
56
+ end
57
+ end
58
+
59
+ assert_equal 3, s.results.count
60
+ end
61
+
62
+ should "automatically create a boolean query when called repeatedly" do
63
+ s = Tire.search 'match-query-test' do
64
+ query do
65
+ match [:first_name, :last_name], 'John'
66
+ match :age, 30
67
+ match :gender, 'male'
68
+ end
69
+ # puts to_curl
70
+ end
71
+
72
+ assert_equal 1, s.results.count
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -0,0 +1,114 @@
1
+ require 'test_helper'
2
+
3
+ module Tire
4
+
5
+ class MultiSearchIntegrationTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ context "Multi search" do
9
+ # Tire.configure { logger STDERR }
10
+ setup do
11
+ Tire.index 'multi-search-test-1' do
12
+ delete
13
+ create
14
+ store first_name: 'John', last_name: 'Smith', age: 30, gender: 'male'
15
+ store first_name: 'John', last_name: 'Smithson', age: 25, gender: 'male'
16
+ store first_name: 'Mary', last_name: 'Smith', age: 20, gender: 'female'
17
+ refresh
18
+ end
19
+ Tire.index 'multi-search-test-2' do
20
+ delete
21
+ create
22
+ store first_name: 'John', last_name: 'Milton', age: 35, gender: 'male'
23
+ store first_name: 'Mary', last_name: 'Milson', age: 44, gender: 'female'
24
+ store first_name: 'Mary', last_name: 'Reilly', age: 55, gender: 'female'
25
+ refresh
26
+ end
27
+ end
28
+
29
+ teardown do
30
+ Tire.index('multi-search-test-1').delete
31
+ Tire.index('multi-search-test-2').delete
32
+ end
33
+
34
+ should "return multiple results" do
35
+ s = Tire.multi_search 'multi-search-test-1' do
36
+ search :johns do
37
+ query { match :_all, 'john' }
38
+ end
39
+ search :males do
40
+ query { match :gender, 'male' }
41
+ end
42
+ search :facets, search_type: 'count' do
43
+ facet('age') { statistical :age }
44
+ end
45
+ end
46
+
47
+ assert_equal 3, s.results.size
48
+
49
+ assert_equal 2, s.results[:johns].size
50
+ assert_equal 2, s.results[:males].size
51
+
52
+ assert s.results[:facets].results.empty?, "Results not empty? #{s.results[:facets].results}"
53
+ assert_equal 75.0, s.results[:facets].facets['age']['total']
54
+ end
55
+
56
+ should "mix named and numbered searches" do
57
+ s = Tire.multi_search 'multi-search-test-1' do
58
+ search(:johns) { query { match :_all, 'john' } }
59
+ search { query { match :_all, 'mary' } }
60
+ end
61
+
62
+ assert_equal 2, s.results.size
63
+
64
+ assert_equal 2, s.results[:johns].size
65
+ assert_equal 1, s.results[1].size
66
+ end
67
+
68
+ should "iterate over mixed searches" do
69
+ s = Tire.multi_search 'multi-search-test-1' do
70
+ search(:johns) { query { match :_all, 'john' } }
71
+ search { query { match :_all, 'mary' } }
72
+ end
73
+
74
+ assert_equal [:johns, 1], s.searches.names
75
+ assert_equal [:johns, 1], s.results.to_hash.keys
76
+
77
+ s.results.each_with_index do |results, i|
78
+ assert_equal 2, results.size if i == 0
79
+ assert_equal 1, results.size if i == 1
80
+ end
81
+
82
+ s.results.each_pair do |name, results|
83
+ assert_equal 2, results.size if name == :johns
84
+ assert_equal 1, results.size if name == 1
85
+ end
86
+ end
87
+
88
+ should "return results from different indices" do
89
+ s = Tire.multi_search do
90
+ search( index: 'multi-search-test-1' ) { query { match :_all, 'john' } }
91
+ search( index: 'multi-search-test-2' ) { query { match :_all, 'john' } }
92
+ end
93
+
94
+ assert_equal 2, s.results[0].size
95
+ assert_equal 1, s.results[1].size
96
+ end
97
+
98
+ should "return error for failed searches" do
99
+ s = Tire.multi_search 'multi-search-test-1' do
100
+ search() { query { match :_all, 'john' } }
101
+ search() { query { string '[x' } }
102
+ end
103
+
104
+ assert_equal 2, s.results[0].size
105
+ assert s.results[0].success?
106
+
107
+ assert_equal 0, s.results[1].size
108
+ assert s.results[1].failure?
109
+ end
110
+ end
111
+
112
+ end
113
+
114
+ end
@@ -124,6 +124,64 @@ module Tire
124
124
 
125
125
  end
126
126
 
127
+ context "multi search" do
128
+ setup do
129
+ # Tire.configure { logger STDERR }
130
+ PersistentArticle.create :title => 'Test'
131
+ PersistentArticle.create :title => 'Pest'
132
+ PersistentArticle.index.refresh
133
+ end
134
+
135
+ should "return multiple result sets" do
136
+ results = PersistentArticle.multi_search do
137
+ search do
138
+ query { match :title, 'test' }
139
+ end
140
+ search search_type: 'count' do
141
+ query { match :title, 'pest' }
142
+ end
143
+ end
144
+
145
+ assert_equal 2, results.size
146
+
147
+ assert_equal 1, results[0].size
148
+ assert_equal 1, results[0].total
149
+
150
+ assert_equal 0, results[1].size
151
+ assert_equal 1, results[1].total
152
+ end
153
+ end
154
+
155
+ context "with multiple types within single index" do
156
+
157
+ setup do
158
+ # Create documents of two types within single index
159
+ PersistentArticleInIndex.create :title => "TestInIndex", :tags => ['in_index']
160
+ PersistentArticle.create :title => "Test", :tags => []
161
+ PersistentArticle.index.refresh
162
+ end
163
+
164
+ should "returns all documents with proper type" do
165
+ results = PersistentArticle.all
166
+
167
+ assert_equal 1, results.size
168
+ assert results.all? { |r| r.tags == [] }, "Incorrect results? " + results.to_a.inspect
169
+
170
+ results = PersistentArticleInIndex.all
171
+
172
+ assert_equal 1, results.size
173
+ assert results.all? { |r| r.tags == ['in_index'] }, "Incorrect results? " + results.to_a.inspect
174
+ end
175
+
176
+ should "returns first document with proper type" do
177
+ assert_instance_of PersistentArticle, PersistentArticle.first
178
+ assert_instance_of PersistentArticleInIndex, PersistentArticleInIndex.first
179
+
180
+ assert_equal [], PersistentArticle.first.tags
181
+ assert_equal ['in_index'], PersistentArticleInIndex.first.tags
182
+ end
183
+ end
184
+
127
185
  end
128
186
 
129
187
  end
@@ -7,7 +7,7 @@ class Article
7
7
  attributes.each { |k,v| instance_variable_set(:"@#{k}", v) }
8
8
  end
9
9
 
10
- def to_json
10
+ def to_json(options={})
11
11
  { :id => @id, :title => @title, :body => @body }.to_json
12
12
  end
13
13
 
@@ -0,0 +1,16 @@
1
+ # Example class with ElasticSearch persistence in index `persistent_articles`
2
+ #
3
+ # The `index` is `persistent_articles`
4
+ #
5
+
6
+ class PersistentArticleInIndex
7
+
8
+ include Tire::Model::Persistence
9
+
10
+ property :title
11
+ property :published_on
12
+ property :tags
13
+
14
+ index_name "persistent_articles"
15
+
16
+ end
@@ -4,8 +4,9 @@ class PersistentArticleWithDefaults
4
4
 
5
5
  property :title
6
6
  property :published_on
7
- property :tags, :default => []
8
- property :hidden, :default => false
9
- property :options, :default => {:switches => []}
7
+ property :tags, :default => []
8
+ property :hidden, :default => false
9
+ property :options, :default => {:switches => []}
10
+ property :created_at, :default => lambda { Time.now }
10
11
 
11
12
  end
data/test/test_helper.rb CHANGED
@@ -24,6 +24,7 @@ require File.dirname(__FILE__) + '/models/active_model_article_with_custom_index
24
24
  require File.dirname(__FILE__) + '/models/active_record_models'
25
25
  require File.dirname(__FILE__) + '/models/article'
26
26
  require File.dirname(__FILE__) + '/models/persistent_article'
27
+ require File.dirname(__FILE__) + '/models/persistent_article_in_index'
27
28
  require File.dirname(__FILE__) + '/models/persistent_article_in_namespace'
28
29
  require File.dirname(__FILE__) + '/models/persistent_article_with_casting'
29
30
  require File.dirname(__FILE__) + '/models/persistent_article_with_defaults'
@@ -86,7 +87,8 @@ module Test::Integration
86
87
  mongoid_class_with_tire_methods
87
88
  supermodel_articles
88
89
  dynamic_index
89
- model_with_nested_documents ].each do |index|
90
+ model_with_nested_documents
91
+ model_with_incorrect_mappings ].each do |index|
90
92
  ::RestClient.delete "#{URL}/#{index}" rescue nil
91
93
  end
92
94
  end
@@ -52,6 +52,16 @@ module Tire
52
52
  assert_instance_of Tire::Logger, Configuration.logger
53
53
  end
54
54
 
55
+ should "set pretty option to true by default" do
56
+ assert_not_nil Configuration.pretty
57
+ assert Configuration.pretty, "Should be true, but is: #{Configuration.pretty.inspect}"
58
+ end
59
+
60
+ should "set the pretty option to false" do
61
+ Configuration.pretty(false)
62
+ assert ! Configuration.pretty, "Should be falsy, but is: #{Configuration.pretty.inspect}"
63
+ end
64
+
55
65
  should "allow to reset the configuration for specific property" do
56
66
  Configuration.url 'http://example.com'
57
67
  assert_equal 'http://example.com', Configuration.url