gom-elasticsearch-adapter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Philipp Brüll
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,4 @@
1
+
2
+ = Generic Object Mapper - ElasticSearch adapter
3
+
4
+ {<img src="http://travis-ci.org/phifty/gom-elasticsearch-adapter.png" />}[http://travis-ci.org/phifty/gom-elasticsearch-adapter]
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ gem 'rspec'
3
+ gem 'reek'
4
+ require 'rspec'
5
+ require 'rake/rdoctask'
6
+ require 'rspec/core/rake_task'
7
+ require 'reek/rake/task'
8
+
9
+ task :default => :spec
10
+
11
+ namespace :gem do
12
+
13
+ desc "Builds the gem"
14
+ task :build do
15
+ system "gem build *.gemspec && mkdir -p pkg/ && mv *.gem pkg/"
16
+ end
17
+
18
+ desc "Builds and installs the gem"
19
+ task :install => :build do
20
+ system "gem install pkg/"
21
+ end
22
+
23
+ end
24
+
25
+ Reek::Rake::Task.new do |task|
26
+ task.fail_on_error = true
27
+ end
28
+
29
+ desc "Generate the rdoc"
30
+ Rake::RDocTask.new do |rdoc|
31
+ rdoc.rdoc_files.add [ "README.rdoc", "lib/**/*.rb" ]
32
+ rdoc.main = "README.rdoc"
33
+ rdoc.title = ""
34
+ end
35
+
36
+ desc "Run all specs in spec directory"
37
+ RSpec::Core::RakeTask.new do |task|
38
+ task.pattern = "spec/lib/**/*_spec.rb"
39
+ end
40
+
41
+ namespace :spec do
42
+
43
+ desc "Run all integration specs in spec/acceptance directory"
44
+ RSpec::Core::RakeTask.new(:acceptance) do |task|
45
+ task.pattern = "spec/acceptance/**/*_spec.rb"
46
+ end
47
+
48
+ end
@@ -0,0 +1,5 @@
1
+ require 'gom'
2
+ require 'gom/couchdb-adapter'
3
+ require File.join(File.dirname(__FILE__), "storage")
4
+
5
+ GOM::Storage::Adapter.register :elasticsearch, GOM::Storage::ElasticSearch::Adapter
@@ -0,0 +1,6 @@
1
+
2
+ module GOM::Storage
3
+
4
+ autoload :ElasticSearch, File.join(File.dirname(__FILE__), "storage", "elastic_search")
5
+
6
+ end
@@ -0,0 +1,9 @@
1
+
2
+ module GOM::Storage::ElasticSearch
3
+
4
+ autoload :Adapter, File.join(File.dirname(__FILE__), "elastic_search", "adapter")
5
+ autoload :Collection, File.join(File.dirname(__FILE__), "elastic_search", "collection")
6
+ autoload :CouchDB, File.join(File.dirname(__FILE__), "elastic_search", "couchdb")
7
+ autoload :Draft, File.join(File.dirname(__FILE__), "elastic_search", "draft")
8
+
9
+ end
@@ -0,0 +1,94 @@
1
+ require 'tire'
2
+
3
+ # The ElasticSearch storage adapter.
4
+ class GOM::Storage::ElasticSearch::Adapter < GOM::Storage::Adapter
5
+
6
+ attr_reader :storage_configuration
7
+
8
+ def setup
9
+ setup_connection
10
+ setup_index
11
+ assign_storage
12
+ setup_river
13
+ end
14
+
15
+ def teardown
16
+
17
+ end
18
+
19
+ def fetch(id)
20
+ storage_adapter.fetch id
21
+ end
22
+
23
+ def store(draft)
24
+ storage_adapter.store draft
25
+ end
26
+
27
+ def remove(id)
28
+ storage_adapter.remove id
29
+ end
30
+
31
+ def collection(name, options = { })
32
+ view = configuration.views[name.to_sym]
33
+ raise ArgumentError, "the given view #{name} isn't defined!'" unless view
34
+
35
+ query_string = build_query view.class_name, options[:query]
36
+ search = Tire.search @index_name do
37
+ query { string query_string }
38
+ end
39
+
40
+ GOM::Object::Collection.new GOM::Storage::ElasticSearch::Collection::Fetcher.new(search.results), configuration.name
41
+ end
42
+
43
+ private
44
+
45
+ def storage_adapter
46
+ storage_configuration.adapter
47
+ end
48
+
49
+ def setup_connection
50
+ @host, @port = *configuration.values_at(:host, :port)
51
+ @host ||= "localhost"
52
+ @port ||= 9200
53
+
54
+ # Tire.configure { url "http://#{@host}:#{@port}" }
55
+ end
56
+
57
+ def setup_index
58
+ @index_name, delete_index_if_exists, create_index_if_missing =
59
+ *configuration.values_at(:index_name, :delete_index_if_exists, :create_index_if_missing)
60
+
61
+ Tire.index @index_name do
62
+ delete if delete_index_if_exists && exists?
63
+ create if create_index_if_missing && !exists?
64
+ end
65
+ end
66
+
67
+ def assign_storage
68
+ assign_to_storage = configuration[:assign_to_storage]
69
+
70
+ @storage_configuration = GOM::Storage::Configuration[assign_to_storage]
71
+ raise ArgumentError, "the specified storage #{assign_to_storage} doesn't exists" unless @storage_configuration
72
+ end
73
+
74
+ def setup_river
75
+ delete_river_if_exists, create_river_if_missing =
76
+ *configuration.values_at(:delete_river_if_exists, :create_river_if_missing)
77
+
78
+ river = GOM::Storage::ElasticSearch::CouchDB::River.new :couch_db => storage_configuration,
79
+ :elastic_search => {
80
+ :host => @host,
81
+ :port => @port,
82
+ :index_name => @index_name
83
+ }
84
+
85
+ river.delete_if_exists if delete_river_if_exists
86
+ river.create_if_missing if create_river_if_missing
87
+ end
88
+
89
+ def build_query(model_class, query)
90
+ terms = [ model_class ].flatten.compact.map{ |klass| "model_class:\"#{klass}\"" }
91
+ "(#{terms.join(" OR ")})" + (query && query != "" ? " AND #{query}" : "")
92
+ end
93
+
94
+ end
@@ -0,0 +1,6 @@
1
+
2
+ module GOM::Storage::ElasticSearch::Collection
3
+
4
+ autoload :Fetcher, File.join(File.dirname(__FILE__), "collection", "fetcher")
5
+
6
+ end
@@ -0,0 +1,16 @@
1
+
2
+ class GOM::Storage::ElasticSearch::Collection::Fetcher
3
+
4
+ def initialize(results)
5
+ @results = results
6
+ end
7
+
8
+ def drafts
9
+ drafts = [ ]
10
+ @results.each do |item|
11
+ drafts << GOM::Storage::ElasticSearch::Draft::Builder.new(item).draft
12
+ end
13
+ drafts
14
+ end
15
+
16
+ end
@@ -0,0 +1,6 @@
1
+
2
+ module GOM::Storage::ElasticSearch::CouchDB
3
+
4
+ autoload :River, File.join(File.dirname(__FILE__), "couchdb", "river")
5
+
6
+ end
@@ -0,0 +1,56 @@
1
+ require 'transport'
2
+
3
+ class GOM::Storage::ElasticSearch::CouchDB::River
4
+
5
+ def initialize(options)
6
+ @couch_db = options[:couch_db]
7
+ @elastic_search = options[:elastic_search]
8
+ end
9
+
10
+ def create
11
+ Transport::JSON.request :put, "#{elastic_search_base_url}/_river/#{@elastic_search[:index_name]}/_meta",
12
+ :body => {
13
+ :type => "couchdb",
14
+ :couchdb => {
15
+ :host => @couch_db[:host],
16
+ :port => @couch_db[:port],
17
+ :db => @couch_db[:database],
18
+ :user => @couch_db[:username],
19
+ :password => @couch_db[:password]
20
+ },
21
+ :index => {
22
+ :index => @elastic_search[:index_name],
23
+ :type => @elastic_search[:index_name],
24
+ :bulk_size => "100",
25
+ :bulk_timeout => "10ms"
26
+ }
27
+ },
28
+ :expected_status_code => [ 200, 201 ]
29
+ end
30
+
31
+ def create_if_missing
32
+ create unless exists?
33
+ end
34
+
35
+ def delete
36
+ Transport::JSON.request :delete, "#{elastic_search_base_url}/_river/#{@elastic_search[:index_name]}", :expected_status_code => 200
37
+ end
38
+
39
+ def delete_if_exists
40
+ delete if exists?
41
+ end
42
+
43
+ def exists?
44
+ Transport::JSON.request :get, "#{elastic_search_base_url}/_river/#{@elastic_search[:index_name]}/_meta", :expected_status_code => 200
45
+ true
46
+ rescue Transport::UnexpectedStatusCodeError
47
+ false
48
+ end
49
+
50
+ private
51
+
52
+ def elastic_search_base_url
53
+ "http://#{@elastic_search[:host]}:#{@elastic_search[:port]}"
54
+ end
55
+
56
+ end
@@ -0,0 +1,6 @@
1
+
2
+ module GOM::Storage::ElasticSearch::Draft
3
+
4
+ autoload :Builder, File.join(File.dirname(__FILE__), "draft", "builder")
5
+
6
+ end
@@ -0,0 +1,56 @@
1
+
2
+ # Builds a draft out of a search result item document.
3
+ class GOM::Storage::ElasticSearch::Draft::Builder
4
+
5
+ def initialize(item)
6
+ @item = item.to_hash
7
+ end
8
+
9
+ def draft
10
+ initialize_draft
11
+ set_object_id
12
+ set_class
13
+ set_properties_and_relations
14
+ @draft
15
+ end
16
+
17
+ private
18
+
19
+ def initialize_draft
20
+ @draft = GOM::Object::Draft.new
21
+ end
22
+
23
+ def set_object_id
24
+ @draft.object_id = @item[:_id]
25
+ end
26
+
27
+ def set_class
28
+ @draft.class_name = @item[:model_class]
29
+ end
30
+
31
+ def set_properties_and_relations
32
+ @item.each do |key, value|
33
+ set_property key.to_s, value if property_key?(key)
34
+ set_relation key.to_s, value if relation_key?(key)
35
+ end
36
+ end
37
+
38
+ def set_property(key, value)
39
+ @draft.properties[key.to_sym] = value
40
+ end
41
+
42
+ def set_relation(key, value)
43
+ name = key.sub /_id$/, ""
44
+ id = GOM::Object::Id.new value
45
+ @draft.relations[name.to_sym] = GOM::Object::Proxy.new id
46
+ end
47
+
48
+ def property_key?(key)
49
+ !relation_key?(key) && ![ "_id", "_rev", "model_class" ].include?(key)
50
+ end
51
+
52
+ def relation_key?(key)
53
+ !!(key =~ /.+_id$/)
54
+ end
55
+
56
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper"))
2
+
3
+ describe "couchdb adapter" do
4
+
5
+ it_should_behave_like "an adapter with search view"
6
+
7
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ gem 'rspec', '>= 2'
3
+ require 'rspec'
4
+
5
+ require 'gom/spec'
6
+ require 'gom/couchdb-adapter'
7
+
8
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "gom", "elasticsearch-adapter"))
9
+ require File.join(File.dirname(__FILE__), "storage_configuration")
@@ -0,0 +1,27 @@
1
+
2
+ GOM::Storage.configure {
3
+ storage {
4
+ name :test_couchdb_storage
5
+ adapter :couchdb
6
+ username "test"
7
+ password "test"
8
+ database "test"
9
+ delete_database_if_exists false
10
+ create_database_if_missing true
11
+ }
12
+ storage {
13
+ name :test_storage
14
+ adapter :elasticsearch
15
+ index_name "test"
16
+ delete_index_if_exists false
17
+ create_index_if_missing true
18
+ delete_river_if_exists false
19
+ create_river_if_missing true
20
+ assign_to_storage :test_couchdb_storage
21
+ view {
22
+ name :test_search_view
23
+ kind :search
24
+ model_class GOM::Spec::Object
25
+ }
26
+ }
27
+ }
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gom-elasticsearch-adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Philipp Brüll
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-19 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: gom
16
+ requirement: &18084220 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.5.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *18084220
25
+ - !ruby/object:Gem::Dependency
26
+ name: gom-couchdb-adapter
27
+ requirement: &18083500 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 0.5.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *18083500
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &18082780 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '2'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *18082780
47
+ - !ruby/object:Gem::Dependency
48
+ name: reek
49
+ requirement: &18082120 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *18082120
58
+ description: ElasticSearch adapter for the General Object Mapper. Current versions
59
+ of ElasticSearch are supported.
60
+ email: b.phifty@gmail.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files:
64
+ - README.rdoc
65
+ files:
66
+ - README.rdoc
67
+ - LICENSE
68
+ - Rakefile
69
+ - lib/gom/elasticsearch-adapter.rb
70
+ - lib/gom/storage/elastic_search.rb
71
+ - lib/gom/storage/elastic_search/collection/fetcher.rb
72
+ - lib/gom/storage/elastic_search/couchdb/river.rb
73
+ - lib/gom/storage/elastic_search/adapter.rb
74
+ - lib/gom/storage/elastic_search/draft/builder.rb
75
+ - lib/gom/storage/elastic_search/draft.rb
76
+ - lib/gom/storage/elastic_search/couchdb.rb
77
+ - lib/gom/storage/elastic_search/collection.rb
78
+ - lib/gom/storage.rb
79
+ - spec/helper.rb
80
+ - spec/storage_configuration.rb
81
+ - spec/acceptance/adapter_spec.rb
82
+ homepage: http://github.com/phifty/gom-elasticsearch-adapter
83
+ licenses: []
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ segments:
95
+ - 0
96
+ hash: 41961588728074855
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ segments:
104
+ - 0
105
+ hash: 41961588728074855
106
+ requirements: []
107
+ rubyforge_project: gom-elasticsearch-adapter
108
+ rubygems_version: 1.8.10
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: ElasticSearch adapter for the General Object Mapper.
112
+ test_files:
113
+ - spec/acceptance/adapter_spec.rb