tire 0.1.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.
- data/.gitignore +9 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +435 -0
- data/Rakefile +75 -0
- data/examples/dsl.rb +73 -0
- data/examples/rails-application-template.rb +144 -0
- data/examples/tire-dsl.rb +617 -0
- data/lib/tire.rb +35 -0
- data/lib/tire/client.rb +40 -0
- data/lib/tire/configuration.rb +29 -0
- data/lib/tire/dsl.rb +33 -0
- data/lib/tire/index.rb +209 -0
- data/lib/tire/logger.rb +60 -0
- data/lib/tire/model/callbacks.rb +23 -0
- data/lib/tire/model/import.rb +18 -0
- data/lib/tire/model/indexing.rb +50 -0
- data/lib/tire/model/naming.rb +30 -0
- data/lib/tire/model/persistence.rb +34 -0
- data/lib/tire/model/persistence/attributes.rb +60 -0
- data/lib/tire/model/persistence/finders.rb +61 -0
- data/lib/tire/model/persistence/storage.rb +75 -0
- data/lib/tire/model/search.rb +97 -0
- data/lib/tire/results/collection.rb +56 -0
- data/lib/tire/results/item.rb +39 -0
- data/lib/tire/results/pagination.rb +30 -0
- data/lib/tire/rubyext/hash.rb +3 -0
- data/lib/tire/rubyext/symbol.rb +11 -0
- data/lib/tire/search.rb +117 -0
- data/lib/tire/search/facet.rb +41 -0
- data/lib/tire/search/filter.rb +28 -0
- data/lib/tire/search/highlight.rb +37 -0
- data/lib/tire/search/query.rb +42 -0
- data/lib/tire/search/sort.rb +29 -0
- data/lib/tire/tasks.rb +88 -0
- data/lib/tire/version.rb +3 -0
- data/test/fixtures/articles/1.json +1 -0
- data/test/fixtures/articles/2.json +1 -0
- data/test/fixtures/articles/3.json +1 -0
- data/test/fixtures/articles/4.json +1 -0
- data/test/fixtures/articles/5.json +1 -0
- data/test/integration/active_model_searchable_test.rb +80 -0
- data/test/integration/active_record_searchable_test.rb +193 -0
- data/test/integration/facets_test.rb +65 -0
- data/test/integration/filters_test.rb +46 -0
- data/test/integration/highlight_test.rb +52 -0
- data/test/integration/index_mapping_test.rb +44 -0
- data/test/integration/index_store_test.rb +68 -0
- data/test/integration/persistent_model_test.rb +35 -0
- data/test/integration/query_string_test.rb +43 -0
- data/test/integration/results_test.rb +28 -0
- data/test/integration/sort_test.rb +36 -0
- data/test/models/active_model_article.rb +31 -0
- data/test/models/active_model_article_with_callbacks.rb +49 -0
- data/test/models/active_model_article_with_custom_index_name.rb +5 -0
- data/test/models/active_record_article.rb +12 -0
- data/test/models/article.rb +15 -0
- data/test/models/persistent_article.rb +11 -0
- data/test/models/persistent_articles_with_custom_index_name.rb +10 -0
- data/test/models/supermodel_article.rb +22 -0
- data/test/models/validated_model.rb +11 -0
- data/test/test_helper.rb +52 -0
- data/test/unit/active_model_lint_test.rb +17 -0
- data/test/unit/client_test.rb +43 -0
- data/test/unit/configuration_test.rb +71 -0
- data/test/unit/index_test.rb +390 -0
- data/test/unit/logger_test.rb +114 -0
- data/test/unit/model_callbacks_test.rb +90 -0
- data/test/unit/model_import_test.rb +71 -0
- data/test/unit/model_persistence_test.rb +400 -0
- data/test/unit/model_search_test.rb +289 -0
- data/test/unit/results_collection_test.rb +131 -0
- data/test/unit/results_item_test.rb +59 -0
- data/test/unit/rubyext_hash_test.rb +19 -0
- data/test/unit/search_facet_test.rb +69 -0
- data/test/unit/search_filter_test.rb +36 -0
- data/test/unit/search_highlight_test.rb +46 -0
- data/test/unit/search_query_test.rb +55 -0
- data/test/unit/search_sort_test.rb +50 -0
- data/test/unit/search_test.rb +204 -0
- data/test/unit/tire_test.rb +55 -0
- data/tire.gemspec +54 -0
- metadata +372 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tire
|
4
|
+
|
5
|
+
class IndexMappingIntegrationTest < Test::Unit::TestCase
|
6
|
+
include Test::Integration
|
7
|
+
|
8
|
+
context "Default mapping" do
|
9
|
+
teardown { Tire.index('mapped-index').delete }
|
10
|
+
|
11
|
+
should "create and return the default mapping" do
|
12
|
+
|
13
|
+
index = Tire.index 'mapped-index' do
|
14
|
+
create
|
15
|
+
store :article, :title => 'One'
|
16
|
+
refresh
|
17
|
+
end
|
18
|
+
sleep 1.5
|
19
|
+
|
20
|
+
assert_equal 'string', index.mapping['article']['properties']['title']['type'], index.mapping.inspect
|
21
|
+
assert_nil index.mapping['article']['properties']['title']['boost'], index.mapping.inspect
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "Creating index with mapping" do
|
26
|
+
teardown { Tire.index('mapped-index').delete; sleep 1 }
|
27
|
+
|
28
|
+
should "create the specified mapping" do
|
29
|
+
|
30
|
+
index = Tire.index 'mapped-index' do
|
31
|
+
create :mappings => { :article => { :properties => { :title => { :type => 'string', :boost => 2.0, :store => 'yes' } } } }
|
32
|
+
end
|
33
|
+
sleep 1
|
34
|
+
|
35
|
+
# p index.mapping
|
36
|
+
assert_equal 2.0, index.mapping['article']['properties']['title']['boost'], index.mapping.inspect
|
37
|
+
assert_equal 'yes', index.mapping['article']['properties']['title']['store'], index.mapping.inspect
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tire
|
4
|
+
|
5
|
+
class IndexStoreIntegrationTest < Test::Unit::TestCase
|
6
|
+
include Test::Integration
|
7
|
+
|
8
|
+
context "Storing the documents in index" do
|
9
|
+
|
10
|
+
teardown { Tire.index('articles-test-ids').delete }
|
11
|
+
|
12
|
+
should "store hashes under their IDs" do
|
13
|
+
|
14
|
+
Tire.index 'articles-test-ids' do
|
15
|
+
delete
|
16
|
+
create
|
17
|
+
|
18
|
+
store :id => 1, :title => 'One'
|
19
|
+
store :id => 2, :title => 'Two'
|
20
|
+
store :id => 3, :title => 'Three'
|
21
|
+
store :id => 4, :title => 'Four'
|
22
|
+
store :id => 4, :title => 'Four'
|
23
|
+
|
24
|
+
refresh
|
25
|
+
end
|
26
|
+
|
27
|
+
s = Tire.search('articles-test-ids') { query { string '*' } }
|
28
|
+
|
29
|
+
assert_equal 4, s.results.count
|
30
|
+
|
31
|
+
document = Tire.index('articles-test-ids').retrieve :document, 4
|
32
|
+
assert_equal 'Four', document.title
|
33
|
+
assert_equal 2, document._version.to_i
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
context "Removing documents from the index" do
|
40
|
+
|
41
|
+
teardown { Tire.index('articles-test-remove').delete }
|
42
|
+
|
43
|
+
setup do
|
44
|
+
Tire.index 'articles-test-remove' do
|
45
|
+
delete
|
46
|
+
create
|
47
|
+
store :id => 1, :title => 'One'
|
48
|
+
store :id => 2, :title => 'Two'
|
49
|
+
refresh
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
should "remove document from the index" do
|
54
|
+
|
55
|
+
assert_equal 2, Tire.search('articles-test-remove') { query { string '*' } }.results.count
|
56
|
+
|
57
|
+
assert_nothing_raised do
|
58
|
+
assert Tire.index('articles-test-remove').remove 1
|
59
|
+
assert ! Tire.index('articles-test-remove').remove(1)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tire
|
4
|
+
|
5
|
+
class PersistentModelIntegrationTest < Test::Unit::TestCase
|
6
|
+
include Test::Integration
|
7
|
+
|
8
|
+
def setup
|
9
|
+
super
|
10
|
+
PersistentArticle.index.delete
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
super
|
15
|
+
PersistentArticle.index.delete
|
16
|
+
end
|
17
|
+
|
18
|
+
context "PersistentModel" do
|
19
|
+
|
20
|
+
should "save documents into index and find them by IDs" do
|
21
|
+
one = PersistentArticle.create :id => 1, :title => 'One'
|
22
|
+
two = PersistentArticle.create :id => 2, :title => 'Two'
|
23
|
+
|
24
|
+
PersistentArticle.index.refresh
|
25
|
+
sleep(1.5)
|
26
|
+
|
27
|
+
results = PersistentArticle.find [1, 2]
|
28
|
+
|
29
|
+
assert_equal 2, results.size
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tire
|
4
|
+
|
5
|
+
class QueryStringIntegrationTest < Test::Unit::TestCase
|
6
|
+
include Test::Integration
|
7
|
+
|
8
|
+
context "Searching for query string" do
|
9
|
+
|
10
|
+
should "find article by title" do
|
11
|
+
q = 'title:one'
|
12
|
+
assert_equal 1, search(q).results.count
|
13
|
+
assert_equal 'One', search(q).results.first[:title]
|
14
|
+
end
|
15
|
+
|
16
|
+
should "find articles by title with boosting" do
|
17
|
+
q = 'title:one^100 OR title:two'
|
18
|
+
assert_equal 2, search(q).results.count
|
19
|
+
assert_equal 'One', search(q).results.first[:title]
|
20
|
+
end
|
21
|
+
|
22
|
+
should "find articles by tags" do
|
23
|
+
q = 'tags:ruby AND tags:python'
|
24
|
+
assert_equal 1, search(q).results.count
|
25
|
+
assert_equal 'Two', search(q).results.first[:title]
|
26
|
+
end
|
27
|
+
|
28
|
+
should "find any article with tags" do
|
29
|
+
q = 'tags:ruby OR tags:python OR tags:java'
|
30
|
+
assert_equal 4, search(q).results.count
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def search(q)
|
38
|
+
Tire.search('articles-test') { query { string q } }
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tire
|
4
|
+
|
5
|
+
class ResultsIntegrationTest < Test::Unit::TestCase
|
6
|
+
include Test::Integration
|
7
|
+
|
8
|
+
context "Query results" do
|
9
|
+
|
10
|
+
should "allow easy access to returned documents" do
|
11
|
+
q = 'title:one'
|
12
|
+
s = Tire.search('articles-test') { query { string q } }
|
13
|
+
assert_equal 'One', s.results.first.title
|
14
|
+
assert_equal 'ruby', s.results.first.tags[0]
|
15
|
+
end
|
16
|
+
|
17
|
+
should "allow easy access to returned documents with limited fields" do
|
18
|
+
q = 'title:one'
|
19
|
+
s = Tire.search('articles-test') { query { string q }.fields :title }
|
20
|
+
assert_equal 'One', s.results.first.title
|
21
|
+
assert_nil s.results.first.tags
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tire
|
4
|
+
|
5
|
+
class SortIntegrationTest < Test::Unit::TestCase
|
6
|
+
include Test::Integration
|
7
|
+
|
8
|
+
context "Sort" do
|
9
|
+
|
10
|
+
should "sort by title" do
|
11
|
+
q = '*'
|
12
|
+
s = Tire.search('articles-test') do
|
13
|
+
query { string q }
|
14
|
+
sort { title }
|
15
|
+
end
|
16
|
+
|
17
|
+
assert_equal 5, s.results.count
|
18
|
+
assert_equal 'Five', s.results.first[:title]
|
19
|
+
end
|
20
|
+
|
21
|
+
should "sort by title, descending" do
|
22
|
+
q = '*'
|
23
|
+
s = Tire.search('articles-test') do
|
24
|
+
query { string q }
|
25
|
+
sort { title :desc }
|
26
|
+
end
|
27
|
+
|
28
|
+
assert_equal 5, s.results.count
|
29
|
+
assert_equal 'Two', s.results.first[:title]
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Example ActiveModel class
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_model'
|
5
|
+
|
6
|
+
class ActiveModelArticle
|
7
|
+
|
8
|
+
extend ActiveModel::Naming
|
9
|
+
include ActiveModel::AttributeMethods
|
10
|
+
include ActiveModel::Serialization
|
11
|
+
include ActiveModel::Serializers::JSON
|
12
|
+
|
13
|
+
include Tire::Model::Search
|
14
|
+
|
15
|
+
attr_reader :attributes
|
16
|
+
|
17
|
+
def initialize(attributes = {})
|
18
|
+
@attributes = attributes
|
19
|
+
end
|
20
|
+
|
21
|
+
def id; attributes['id'] || attributes['_id']; end
|
22
|
+
def id=(value); attributes['id'] = value; end
|
23
|
+
|
24
|
+
def method_missing(name, *args, &block)
|
25
|
+
attributes[name.to_sym] || attributes[name.to_s] || super
|
26
|
+
end
|
27
|
+
|
28
|
+
def persisted?; true; end
|
29
|
+
def save; true; end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Example ActiveModel class with callbacks
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_model'
|
5
|
+
|
6
|
+
class ActiveModelArticleWithCallbacks
|
7
|
+
|
8
|
+
include ActiveModel::AttributeMethods
|
9
|
+
include ActiveModel::Validations
|
10
|
+
include ActiveModel::Serialization
|
11
|
+
include ActiveModel::Serializers::JSON
|
12
|
+
include ActiveModel::Naming
|
13
|
+
|
14
|
+
extend ActiveModel::Callbacks
|
15
|
+
define_model_callbacks :save, :destroy
|
16
|
+
|
17
|
+
include Tire::Model::Search
|
18
|
+
include Tire::Model::Callbacks
|
19
|
+
|
20
|
+
attr_reader :attributes
|
21
|
+
|
22
|
+
def initialize(attributes = {})
|
23
|
+
@attributes = attributes
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(id, *args, &block)
|
27
|
+
attributes[id.to_sym] || attributes[id.to_s] || super
|
28
|
+
end
|
29
|
+
|
30
|
+
def persisted?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def save
|
35
|
+
_run_save_callbacks do
|
36
|
+
STDERR.puts "[Saving ...]"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def destroy
|
41
|
+
_run_destroy_callbacks do
|
42
|
+
STDERR.puts "[Destroying ...]"
|
43
|
+
@destroyed = true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def destroyed?; !!@destroyed; end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
class ActiveRecordArticle < ActiveRecord::Base
|
5
|
+
include Tire::Model::Search
|
6
|
+
include Tire::Model::Callbacks
|
7
|
+
|
8
|
+
mapping do
|
9
|
+
indexes :title, :type => 'string', :boost => 10, :analyzer => 'snowball'
|
10
|
+
indexes :created_at, :type => 'date'
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Example non-ActiveModel custom wrapper for result item
|
2
|
+
|
3
|
+
class Article
|
4
|
+
attr_reader :id, :title, :body
|
5
|
+
|
6
|
+
def initialize(attributes={})
|
7
|
+
attributes.each { |k,v| instance_variable_set(:"@#{k}", v) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_json
|
11
|
+
{ :id => @id, :title => @title, :body => @body }.to_json
|
12
|
+
end
|
13
|
+
|
14
|
+
alias :to_indexed_json :to_json
|
15
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Example ActiveModel class for testing :searchable mode
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'supermodel'
|
5
|
+
|
6
|
+
class SupermodelArticle < SuperModel::Base
|
7
|
+
include SuperModel::RandomID
|
8
|
+
|
9
|
+
include Tire::Model::Search
|
10
|
+
include Tire::Model::Callbacks
|
11
|
+
|
12
|
+
mapping do
|
13
|
+
indexes :title, :type => 'string', :boost => 15, :analyzer => 'czech'
|
14
|
+
end
|
15
|
+
|
16
|
+
alias :persisted? :exists?
|
17
|
+
|
18
|
+
def destroyed?
|
19
|
+
!self.class.find(self.id) rescue true
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha'
|
5
|
+
require 'turn' unless ENV["TM_FILEPATH"]
|
6
|
+
require 'pathname'
|
7
|
+
|
8
|
+
require 'tire'
|
9
|
+
|
10
|
+
Dir[File.dirname(__FILE__) + '/models/**/*.rb'].each { |m| require m }
|
11
|
+
|
12
|
+
class Test::Unit::TestCase
|
13
|
+
|
14
|
+
def mock_response(body, code=200)
|
15
|
+
stub(:body => body, :code => code)
|
16
|
+
end
|
17
|
+
|
18
|
+
def fixtures_path
|
19
|
+
Pathname( File.expand_path( 'fixtures', File.dirname(__FILE__) ) )
|
20
|
+
end
|
21
|
+
|
22
|
+
def fixture_file(path)
|
23
|
+
File.read File.expand_path( path, fixtures_path )
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
module Test::Integration
|
29
|
+
URL = "http://localhost:9200"
|
30
|
+
|
31
|
+
def setup
|
32
|
+
begin
|
33
|
+
::RestClient.get URL
|
34
|
+
rescue Errno::ECONNREFUSED
|
35
|
+
abort "\n\n#{'-'*87}\n[ABORTED] You have to run ElasticSearch on #{URL} for integration tests\n#{'-'*87}\n\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
::RestClient.delete "#{URL}/articles-test" rescue nil
|
39
|
+
::RestClient.post "#{URL}/articles-test", ''
|
40
|
+
fixtures_path.join('articles').entries.each do |f|
|
41
|
+
filename = f.to_s
|
42
|
+
next if filename =~ /^\./
|
43
|
+
::RestClient.put "#{URL}/articles-test/article/#{File.basename(filename, '.*')}",
|
44
|
+
fixtures_path.join('articles').join(f).read
|
45
|
+
end
|
46
|
+
::RestClient.post "#{URL}/articles-test/_refresh", ''
|
47
|
+
end
|
48
|
+
|
49
|
+
def teardown
|
50
|
+
::RestClient.delete "#{URL}/articles-test" rescue nil
|
51
|
+
end
|
52
|
+
end
|