tire 0.4.3 → 0.5.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.
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