tire 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
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