tire 0.4.2 → 0.4.3

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 (38) hide show
  1. data/.travis.yml +18 -2
  2. data/README.markdown +2 -2
  3. data/examples/tire-dsl.rb +1 -1
  4. data/lib/tire.rb +1 -0
  5. data/lib/tire/http/clients/faraday.rb +71 -0
  6. data/lib/tire/index.rb +30 -7
  7. data/lib/tire/logger.rb +1 -1
  8. data/lib/tire/model/persistence.rb +2 -3
  9. data/lib/tire/model/persistence/finders.rb +2 -2
  10. data/lib/tire/model/persistence/storage.rb +3 -5
  11. data/lib/tire/model/search.rb +2 -0
  12. data/lib/tire/results/collection.rb +8 -8
  13. data/lib/tire/results/item.rb +4 -1
  14. data/lib/tire/search.rb +30 -2
  15. data/lib/tire/search/facet.rb +5 -1
  16. data/lib/tire/search/query.rb +36 -1
  17. data/lib/tire/search/script_field.rb +23 -0
  18. data/lib/tire/tasks.rb +1 -1
  19. data/lib/tire/version.rb +8 -15
  20. data/test/integration/dis_max_queries_test.rb +68 -0
  21. data/test/integration/facets_test.rb +27 -0
  22. data/test/integration/index_store_test.rb +17 -1
  23. data/test/integration/index_update_document_test.rb +111 -0
  24. data/test/integration/persistent_model_test.rb +13 -0
  25. data/test/integration/prefix_query_test.rb +43 -0
  26. data/test/integration/reindex_test.rb +10 -0
  27. data/test/integration/script_fields_test.rb +38 -0
  28. data/test/test_helper.rb +6 -1
  29. data/test/unit/index_test.rb +58 -5
  30. data/test/unit/model_persistence_test.rb +14 -2
  31. data/test/unit/model_search_test.rb +14 -0
  32. data/test/unit/results_item_test.rb +9 -2
  33. data/test/unit/rubyext_test.rb +6 -0
  34. data/test/unit/search_query_test.rb +61 -2
  35. data/test/unit/search_script_field_test.rb +26 -0
  36. data/test/unit/search_test.rb +46 -2
  37. data/tire.gemspec +1 -1
  38. metadata +62 -53
@@ -0,0 +1,23 @@
1
+ module Tire
2
+ module Search
3
+
4
+ # http://www.elasticsearch.org/guide/reference/api/search/script-fields.html
5
+ # http://www.elasticsearch.org/guide/reference/modules/scripting.html
6
+
7
+ class ScriptField
8
+
9
+ def initialize(name, options)
10
+ @hash = { name => options }
11
+ end
12
+
13
+ def to_json
14
+ to_hash.to_json
15
+ end
16
+
17
+ def to_hash
18
+ @hash
19
+ end
20
+ end
21
+
22
+ end
23
+ end
data/lib/tire/tasks.rb CHANGED
@@ -61,7 +61,7 @@ namespace :tire do
61
61
  STDOUT.sync = true
62
62
  puts "[IMPORT] Starting import for the '#{ENV['CLASS']}' class"
63
63
  tty_cols = 80
64
- total = klass.count rescue nil
64
+ total = klass.all.count rescue nil
65
65
  offset = (total.to_s.size*2)+8
66
66
  done = 0
67
67
 
data/lib/tire/version.rb CHANGED
@@ -1,22 +1,15 @@
1
1
  module Tire
2
- VERSION = "0.4.2"
2
+ VERSION = "0.4.3"
3
3
 
4
4
  CHANGELOG =<<-END
5
5
  IMPORTANT CHANGES LATELY:
6
6
 
7
- Version 0.4.1
8
- -------------
9
- * Added a Index#settings method to retrieve index settings as a Hash
10
- * Added support for the "scan" search in the Ruby API
11
- * Added support for reindexing the index documents into new index
12
- * Added basic support for index aliases
13
- * Changed, that Index#bulk_store runs against an index endpoint, not against `/_bulk`
14
- * Refactorings, fixes, Ruby 1.8 compatibility
15
-
16
- Version 0.4.2
17
- -------------
18
- * Fixed incorrect handling of PUT requests in the Curb client
19
- * Fixed, that blocks passed to `Tire::Index.new` or `Tire.index` losed the scope
20
- * Added `Tire::Alias`, interface and DSL to manage aliases as resources
7
+ * Added a prefix query
8
+ * Added support for "script fields" (return a script evaluation for each hit)
9
+ * Added support for the Update API in Tire::Index
10
+ * [FIX] Fixed incorrect `Results::Item#to_hash` serialization
11
+ * 730813f Added support for aggregating over multiple fields in the "terms" facet
12
+ * Added the "Dis Max Query"
13
+ * Added the ability to transform documents when reindexing
21
14
  END
22
15
  end
@@ -0,0 +1,68 @@
1
+ require 'test_helper'
2
+
3
+ module Tire
4
+
5
+ class DisMaxQueriesIntegrationTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ context "Dis Max queries" do
9
+ setup do
10
+ Tire.index 'dis_max_test' do
11
+ delete
12
+ create
13
+
14
+ store title: "It's an Albino, Albino, Albino thing!", text: "Albino, albino, albino! Wanna know about albino? ..."
15
+ store title: "Albino Vampire Monkey Attacks!", text: "The night was just setting in when ..."
16
+ store title: "Pinky Elephant", text: "An albino walks into a ZOO and ..."
17
+ refresh
18
+ end
19
+ end
20
+
21
+ teardown do
22
+ Tire.index('dis_max_test').delete
23
+ end
24
+
25
+ should "boost matches in both fields" do
26
+ dis_max = Tire.search 'dis_max_test' do
27
+ query do
28
+ dis_max do
29
+ query { string "albino elephant", fields: ['title', 'text'] }
30
+ end
31
+ end
32
+ end
33
+ # p "DisMax:", dis_max.results.map(&:title)
34
+
35
+ assert_equal 'Pinky Elephant', dis_max.results.first.title
36
+
37
+ # NOTE: This gives exactly the same result as a boolean query:
38
+ # boolean = Tire.search 'dis_max_test' do
39
+ # query do
40
+ # boolean do
41
+ # should { string "albino", fields: ['title', 'text'] }
42
+ # should { string "elephant", fields: ['title', 'text'] }
43
+ # end
44
+ # end
45
+ # end
46
+ # p "Boolean:", boolean.results.map(&:title)
47
+ end
48
+
49
+ should "allow to set multiple queries" do
50
+ s = Tire.search('articles-test') do
51
+ query do
52
+ dis_max do
53
+ query { term :tags, 'ruby' }
54
+ query { term :tags, 'python' }
55
+ end
56
+ end
57
+ end
58
+
59
+ assert_equal 2, s.results.size
60
+ assert_equal 'Two', s.results[0].title
61
+ assert_equal 'One', s.results[1].title
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -59,6 +59,33 @@ module Tire
59
59
  assert_equal 1, s.results.facets['tags']['terms'].first['count'].to_i
60
60
  end
61
61
 
62
+ context "terms" do
63
+ setup do
64
+ @s = Tire.search('articles-test') do
65
+ query { string 'tags:ruby' }
66
+ end
67
+ end
68
+
69
+ should "return results ordered by term" do
70
+ @s.facet('tags') { terms :tags }
71
+ @s.facet('term-ordered-tags') { terms :tags, order: 'term' }
72
+
73
+ facets = @s.results.facets
74
+ # p facets
75
+ assert_equal 'ruby', facets['tags']['terms'] .first['term']
76
+ assert_equal 'python', facets['term-ordered-tags']['terms'].first['term']
77
+ end
78
+
79
+ should "return results aggregated over multiple fields" do
80
+ @s.facet('multiple-fields') { terms ['tags', 'words'] }
81
+
82
+ facets = @s.results.facets
83
+ # p facets
84
+ assert_equal 4, facets['multiple-fields']['terms'].size
85
+ end
86
+
87
+ end
88
+
62
89
  context "date histogram" do
63
90
 
64
91
  should "return aggregated values for all results" do
@@ -22,7 +22,10 @@ module Tire
22
22
  end
23
23
  end
24
24
 
25
- teardown { Tire.index('articles-test-ids').delete }
25
+ teardown do
26
+ Tire.index('articles-test-ids').delete
27
+ Tire.index('articles-test-types').delete
28
+ end
26
29
 
27
30
  should "happen in existing index" do
28
31
  assert Tire.index("articles-test-ids").exists?
@@ -40,6 +43,19 @@ module Tire
40
43
 
41
44
  end
42
45
 
46
+ should "store documents as proper types" do
47
+ Tire.index 'articles-test-types' do
48
+ delete
49
+ create
50
+ store :type => 'my_type', :title => 'One'
51
+ refresh
52
+ end
53
+
54
+ s = Tire.search('articles-test-types/my_type') { query { all } }
55
+ assert_equal 1, s.results.count
56
+ assert_equal 'my_type', s.results.first.type
57
+ end
58
+
43
59
  end
44
60
 
45
61
  context "Removing documents from the index" do
@@ -0,0 +1,111 @@
1
+ require 'test_helper'
2
+
3
+ module Tire
4
+
5
+ class IndexUpdateDocumentIntegrationTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ context "Updating a document" do
9
+
10
+ setup do
11
+ Tire.index 'articles-with-tags' do
12
+ delete
13
+ create
14
+
15
+ store :type => 'article', :id => 1, :title => 'One', :tags => ['foo'], :views => 0
16
+ store :type => 'article', :id => 2, :title => 'Two', :tags => ['foo', 'bar'], :views => 10
17
+ store :type => 'article', :id => 3, :title => 'Three', :tags => ['foobar']
18
+
19
+ refresh
20
+ end
21
+ end
22
+
23
+ teardown { Tire.index('articles-with-tags').delete }
24
+
25
+ should "increment a counter" do
26
+ Tire.index('articles-with-tags') { update( 'article', '1', :script => "ctx._source.views += 1" ) and refresh }
27
+
28
+ document = Tire.search('articles-with-tags') { query { string 'title:one' } }.results.first
29
+ assert_equal 1, document.views, document.inspect
30
+
31
+ Tire.index('articles-with-tags') { update( 'article', '2', :script => "ctx._source.views += 1" ) and refresh }
32
+
33
+ document = Tire.search('articles-with-tags') { query { string 'title:two' } }.results.first
34
+ assert_equal 11, document.views, document.inspect
35
+ end
36
+
37
+ should "add a tag to document" do
38
+ Tire.index('articles-with-tags') do
39
+ update 'article', '1', :script => "ctx._source.tags += tag",
40
+ :params => { :tag => 'new' }
41
+ refresh
42
+ end
43
+
44
+ document = Tire.search('articles-with-tags') { query { string 'title:one' } }.results.first
45
+ assert_equal ['foo', 'new'], document.tags, document.inspect
46
+ end
47
+
48
+ should "remove a tag from document" do
49
+ Tire.index('articles-with-tags') do
50
+ update 'article', '1', :script => "ctx._source.tags = tags",
51
+ :params => { :tags => [] }
52
+ refresh
53
+ end
54
+
55
+ document = Tire.index('articles-with-tags').retrieve 'article', '1'
56
+ assert_equal [], document.tags, document.inspect
57
+ end
58
+
59
+ should "remove the entire document if specific condition is met" do
60
+ Tire.index('articles-with-tags') do
61
+ # Remove document when it contains tag 'foobar'
62
+ update 'article', '3', :script => "ctx._source.tags.contains(tag) ? ctx.op = 'delete' : 'none'",
63
+ :params => { :tag => 'foobar' }
64
+ refresh
65
+ end
66
+
67
+ assert_nil Tire.index('articles-with-tags').retrieve 'article', '3'
68
+ end
69
+
70
+ should "pass the operation parameters to the API" do
71
+ Tire.index('articles-with-tags').update 'article', '2', { :script => "ctx._source.tags += tag",
72
+ :params => { :tag => 'new' }
73
+ },
74
+ {
75
+ :refresh => true
76
+ }
77
+
78
+ document = Tire.search('articles-with-tags') { query { string 'title:two' } }.results.first
79
+ assert_equal 3, document.tags.size, document.inspect
80
+ end
81
+
82
+ should "access variables from the outer scope" do
83
+ $t = self
84
+
85
+ class Updater
86
+ @tags = ['foo', 'bar', 'baz']
87
+
88
+ def self.perform_update!
89
+ $t.assert_not_nil @tags
90
+
91
+ Tire.index('articles-with-tags') do |index|
92
+ $t.assert_not_nil @tags
93
+
94
+ index.update 'article', '3', :script => "ctx._source.tags = tags",
95
+ :params => { :tags => @tags }
96
+ index.refresh
97
+ end
98
+ end
99
+ end
100
+
101
+ Updater.perform_update!
102
+
103
+ document = Tire.search('articles-with-tags') { query { string 'title:three' } }.results.first
104
+ assert_equal 3, document.tags.size, document.inspect
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -50,7 +50,20 @@ module Tire
50
50
  results = PersistentArticle.find [1, 2]
51
51
 
52
52
  assert_equal 2, results.size
53
+ end
54
+
55
+ should "be persisted" do
56
+ one = PersistentArticle.create :id => 1, :title => 'One'
57
+ PersistentArticle.index.refresh
58
+
59
+ a = PersistentArticle.all.first
60
+ assert a.persisted?, a.inspect
61
+
62
+ b = PersistentArticle.first
63
+ assert b.persisted?, b.inspect
53
64
 
65
+ c = PersistentArticle.search { query { string 'one' } }.first
66
+ assert c.persisted?, c.inspect
54
67
  end
55
68
 
56
69
  should "return default values for properties without value" do
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+
3
+ module Tire
4
+
5
+ class PrefixQueryTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ context "Prefix queries" do
9
+
10
+ should "search by a prefix" do
11
+ s = Tire.search('articles-test') do
12
+ query do
13
+ # "on" => "One"
14
+ prefix :title, "on"
15
+ end
16
+ end
17
+
18
+ assert_equal 1, s.results.size
19
+ assert_equal ['One'], s.results.map(&:title)
20
+ end
21
+
22
+ should "allow to specify boost" do
23
+ s = Tire.search('articles-test') do
24
+ query do
25
+ boolean do
26
+ # "on" => "One", boost it
27
+ should { prefix :title, "on", :boost => 2.0 }
28
+ should { all }
29
+ end
30
+ end
31
+ sort { by :_score }
32
+ end
33
+
34
+ assert_equal 5, s.results.size
35
+ assert_equal 'One', s.results.first.title
36
+
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -39,6 +39,16 @@ module Tire
39
39
  assert_equal 2, Tire.search('reindex-test-new').results.total
40
40
  end
41
41
 
42
+ should "transform documents with a passed lambda" do
43
+ Tire.index('reindex-test').reindex 'reindex-test-new', transform: lambda { |document|
44
+ document[:title] += 'UPDATED'
45
+ document
46
+ }
47
+
48
+ Tire.index('reindex-test-new').refresh
49
+ assert_match /UPDATED/, Tire.search('reindex-test-new').results.first.title
50
+ end
51
+
42
52
  end
43
53
 
44
54
  end
@@ -0,0 +1,38 @@
1
+ require 'test_helper'
2
+
3
+ module Tire
4
+
5
+ class ScriptFieldsIntegrationTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ context "ScriptFields" do
9
+
10
+ should "add multiple fields to the results" do
11
+ # 1.json > title: "One", words: 125
12
+
13
+ s = Tire.search('articles-test') do
14
+ query { string "One" }
15
+ script_field :double_words, :script => "doc['words'].value * 2"
16
+ script_field :triple_words, :script => "doc['words'].value * 3"
17
+ end
18
+
19
+ assert_equal 250, s.results.first.double_words
20
+ assert_equal 375, s.results.first.triple_words
21
+ end
22
+
23
+ should "allow passing parameters to the script" do
24
+ # 1.json > title: "One", words: 125
25
+
26
+ s = Tire.search('articles-test') do
27
+ query { string "One" }
28
+ script_field :double_words, :script => "doc['words'].value * factor", :params => { :factor => 2 }
29
+ end
30
+
31
+ assert_equal 250, s.results.first.double_words
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+
38
+ end
data/test/test_helper.rb CHANGED
@@ -8,7 +8,7 @@ require 'yajl/json_gem'
8
8
  require 'sqlite3'
9
9
 
10
10
  require 'shoulda'
11
- require 'turn/autorun' unless ENV["TM_FILEPATH"] || ENV["CI"] || defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
11
+ require 'turn/autorun' unless ENV["TM_FILEPATH"] || defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
12
12
  require 'mocha'
13
13
 
14
14
  require 'active_support/core_ext/hash/indifferent_access'
@@ -32,6 +32,11 @@ require File.dirname(__FILE__) + '/models/validated_model'
32
32
 
33
33
  class Test::Unit::TestCase
34
34
 
35
+ def assert_block(message=nil)
36
+ raise Test::Unit::AssertionFailedError.new(message.to_s) if (! yield)
37
+ return true
38
+ end if defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
39
+
35
40
  def mock_response(body, code=200, headers={})
36
41
  Tire::HTTP::Response.new(body, code, headers)
37
42
  end