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.
- data/.travis.yml +18 -2
- data/README.markdown +2 -2
- data/examples/tire-dsl.rb +1 -1
- data/lib/tire.rb +1 -0
- data/lib/tire/http/clients/faraday.rb +71 -0
- data/lib/tire/index.rb +30 -7
- data/lib/tire/logger.rb +1 -1
- data/lib/tire/model/persistence.rb +2 -3
- data/lib/tire/model/persistence/finders.rb +2 -2
- data/lib/tire/model/persistence/storage.rb +3 -5
- data/lib/tire/model/search.rb +2 -0
- data/lib/tire/results/collection.rb +8 -8
- data/lib/tire/results/item.rb +4 -1
- data/lib/tire/search.rb +30 -2
- data/lib/tire/search/facet.rb +5 -1
- data/lib/tire/search/query.rb +36 -1
- data/lib/tire/search/script_field.rb +23 -0
- data/lib/tire/tasks.rb +1 -1
- data/lib/tire/version.rb +8 -15
- data/test/integration/dis_max_queries_test.rb +68 -0
- data/test/integration/facets_test.rb +27 -0
- data/test/integration/index_store_test.rb +17 -1
- data/test/integration/index_update_document_test.rb +111 -0
- data/test/integration/persistent_model_test.rb +13 -0
- data/test/integration/prefix_query_test.rb +43 -0
- data/test/integration/reindex_test.rb +10 -0
- data/test/integration/script_fields_test.rb +38 -0
- data/test/test_helper.rb +6 -1
- data/test/unit/index_test.rb +58 -5
- data/test/unit/model_persistence_test.rb +14 -2
- data/test/unit/model_search_test.rb +14 -0
- data/test/unit/results_item_test.rb +9 -2
- data/test/unit/rubyext_test.rb +6 -0
- data/test/unit/search_query_test.rb +61 -2
- data/test/unit/search_script_field_test.rb +26 -0
- data/test/unit/search_test.rb +46 -2
- data/tire.gemspec +1 -1
- 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
data/lib/tire/version.rb
CHANGED
@@ -1,22 +1,15 @@
|
|
1
1
|
module Tire
|
2
|
-
VERSION = "0.4.
|
2
|
+
VERSION = "0.4.3"
|
3
3
|
|
4
4
|
CHANGELOG =<<-END
|
5
5
|
IMPORTANT CHANGES LATELY:
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
* Added
|
10
|
-
*
|
11
|
-
* Added support for
|
12
|
-
* Added
|
13
|
-
*
|
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
|
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"] ||
|
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
|