rom-elasticsearch 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +24 -0
  5. data/.yardopts +7 -0
  6. data/CHANGELOG.md +3 -0
  7. data/CONTRIBUTING.md +29 -0
  8. data/Gemfile +20 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +40 -0
  11. data/Rakefile +19 -0
  12. data/lib/rom-elasticsearch.rb +1 -0
  13. data/lib/rom/elasticsearch.rb +8 -0
  14. data/lib/rom/elasticsearch/attribute.rb +30 -0
  15. data/lib/rom/elasticsearch/commands.rb +49 -0
  16. data/lib/rom/elasticsearch/dataset.rb +219 -0
  17. data/lib/rom/elasticsearch/errors.rb +23 -0
  18. data/lib/rom/elasticsearch/gateway.rb +81 -0
  19. data/lib/rom/elasticsearch/plugins/relation/query_dsl.rb +57 -0
  20. data/lib/rom/elasticsearch/query_methods.rb +64 -0
  21. data/lib/rom/elasticsearch/relation.rb +241 -0
  22. data/lib/rom/elasticsearch/schema.rb +26 -0
  23. data/lib/rom/elasticsearch/types.rb +33 -0
  24. data/lib/rom/elasticsearch/version.rb +5 -0
  25. data/rom-elasticsearch.gemspec +27 -0
  26. data/spec/integration/rom/elasticsearch/relation/command_spec.rb +47 -0
  27. data/spec/shared/setup.rb +16 -0
  28. data/spec/shared/unit/user_fixtures.rb +15 -0
  29. data/spec/shared/unit/users.rb +18 -0
  30. data/spec/spec_helper.rb +43 -0
  31. data/spec/unit/rom/elasticsearch/dataset/body_spec.rb +13 -0
  32. data/spec/unit/rom/elasticsearch/dataset/delete_spec.rb +17 -0
  33. data/spec/unit/rom/elasticsearch/dataset/params_spec.rb +13 -0
  34. data/spec/unit/rom/elasticsearch/dataset/put_spec.rb +14 -0
  35. data/spec/unit/rom/elasticsearch/dataset/query_string_spec.rb +12 -0
  36. data/spec/unit/rom/elasticsearch/dataset/search_spec.rb +20 -0
  37. data/spec/unit/rom/elasticsearch/gateway_spec.rb +10 -0
  38. data/spec/unit/rom/elasticsearch/plugins/relation/query_dsl_spec.rb +34 -0
  39. data/spec/unit/rom/elasticsearch/relation/create_index_spec.rb +75 -0
  40. data/spec/unit/rom/elasticsearch/relation/dataset_spec.rb +26 -0
  41. data/spec/unit/rom/elasticsearch/relation/delete_spec.rb +32 -0
  42. data/spec/unit/rom/elasticsearch/relation/get_spec.rb +22 -0
  43. data/spec/unit/rom/elasticsearch/relation/map_spec.rb +18 -0
  44. data/spec/unit/rom/elasticsearch/relation/pluck_spec.rb +18 -0
  45. data/spec/unit/rom/elasticsearch/relation/query_spec.rb +18 -0
  46. data/spec/unit/rom/elasticsearch/relation/query_string_spec.rb +18 -0
  47. data/spec/unit/rom/elasticsearch/relation/search_spec.rb +18 -0
  48. data/spec/unit/rom/elasticsearch/relation/to_a_spec.rb +28 -0
  49. metadata +186 -0
@@ -0,0 +1,26 @@
1
+ require 'rom/types'
2
+ require 'rom/schema'
3
+
4
+ module ROM
5
+ module Elasticsearch
6
+ # Elasticsearch relation schema
7
+ #
8
+ # @api public
9
+ class Schema < ROM::Schema
10
+ # Return a hash with mapping properties
11
+ #
12
+ # @api private
13
+ def to_properties
14
+ select(&:properties?).map { |attr| [attr.name, attr.properties] }.to_h
15
+ end
16
+
17
+ # Customized output hash constructor which symbolizes keys
18
+ # and optionally applies custom read-type coercions
19
+ #
20
+ # @api private
21
+ def to_output_hash
22
+ Types::Hash.symbolized(map { |attr| [attr.key, attr.to_read_type] }.to_h)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ require 'rom/types'
2
+
3
+ module ROM
4
+ module Elasticsearch
5
+ # Elasticsearch types use by schema attributes
6
+ #
7
+ # @api public
8
+ module Types
9
+ include ROM::Types
10
+
11
+ # Default integer primary key
12
+ ID = Int.meta(primary_key: true)
13
+
14
+ # Define a keywoard attribute type
15
+ #
16
+ # @return [Dry::Types::Type]
17
+ #
18
+ # @api public
19
+ def self.Keyword(meta = {})
20
+ String.meta(type: "keyword").meta(meta)
21
+ end
22
+
23
+ # Define a keywoard attribute type
24
+ #
25
+ # @return [Dry::Types::Type]
26
+ #
27
+ # @api public
28
+ def self.Text(meta = {})
29
+ String.meta(type: "text").meta(meta)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ module ROM
2
+ module Elasticsearch
3
+ VERSION = '0.1.0'.freeze
4
+ end
5
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rom/elasticsearch/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'rom-elasticsearch'
8
+ spec.version = ROM::Elasticsearch::VERSION
9
+ spec.authors = ['Hannes Nevalainen', 'Piotr Solnica']
10
+ spec.email = ['hannes.nevalainen@me.com', 'piotr.solnica+oss@gmail.com']
11
+ spec.summary = %q{ROM adapter for Elasticsearch}
12
+ spec.description = %q{}
13
+ spec.homepage = 'http://rom-rb.org'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_runtime_dependency 'rom-core', '~> 4.1'
22
+ spec.add_runtime_dependency 'elasticsearch', '~> 6.0'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.6'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'rspec'
27
+ end
@@ -0,0 +1,47 @@
1
+ RSpec.describe ROM::Elasticsearch::Relation, '#command' do
2
+ subject(:relation) { relations[:users] }
3
+
4
+ include_context 'setup'
5
+
6
+ before do
7
+ conf.relation(:users) do
8
+ schema(:users) do
9
+ attribute :id, ROM::Elasticsearch::Types::ID
10
+ attribute :name, ROM::Types::String
11
+ end
12
+ end
13
+ end
14
+
15
+ describe ':create' do
16
+ it 'returns a create command' do
17
+ command = relation.command(:create, result: :one)
18
+
19
+ expect(command.call(id: 1, name: 'Jane')).to eql(id: 1, name: 'Jane')
20
+ end
21
+
22
+ it 'applies input function' do
23
+ command = relation.command(:create, result: :one)
24
+
25
+ input = double(:user, to_hash: { id: 1, name: 'Jane' })
26
+
27
+ expect(command.call(input)).to eql(id: 1, name: 'Jane')
28
+ end
29
+ end
30
+
31
+ describe ':delete' do
32
+ before do
33
+ relation.command(:create).call(id: 1, name: 'Jane')
34
+ relation.command(:create).call(id: 2, name: 'John')
35
+
36
+ relation.refresh
37
+ end
38
+
39
+ it 'deletes matching data' do
40
+ relation.get(2).command(:delete).call
41
+
42
+ relation.refresh
43
+
44
+ expect(relation.to_a).to eql([{ id: 1, name: 'Jane' }])
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,16 @@
1
+ RSpec.shared_context 'setup' do
2
+ let(:uri) { "http://127.0.0.1:9200" }
3
+
4
+ let(:conf) { ROM::Configuration.new(:elasticsearch, uri) }
5
+ let(:container) { ROM.container(conf) }
6
+
7
+ let(:gateway) { conf.gateways[:default] }
8
+ let(:client) { gateway.client }
9
+
10
+ let(:relations) { container[:relations] }
11
+ let(:commands) { container[:commands] }
12
+
13
+ after do
14
+ client.indices.delete(index: :users) if gateway.index?(:users)
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ RSpec.shared_context 'user fixtures' do
2
+ include_context 'setup'
3
+
4
+ before do
5
+ dataset.put(username: 'eve')
6
+ dataset.put(username: 'bob')
7
+ dataset.put(username: 'alice')
8
+
9
+ dataset.refresh
10
+ end
11
+
12
+ after do
13
+ gateway[:users].refresh.delete
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ RSpec.shared_context 'users' do
2
+ include_context 'setup'
3
+
4
+ before do
5
+ conf.relation(:users) do
6
+ schema(:users) do
7
+ attribute :id, ROM::Types::Int
8
+ attribute :name, ROM::Types::String
9
+
10
+ primary_key :id
11
+ end
12
+ end
13
+ end
14
+
15
+ after do
16
+ gateway[:users].refresh.delete
17
+ end
18
+ end
@@ -0,0 +1,43 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ if RUBY_ENGINE == 'ruby' && ENV['COVERAGE'] == 'true'
5
+ require 'yaml'
6
+ rubies = YAML.load(File.read(File.join(__dir__, '..', '.travis.yml')))['rvm']
7
+ latest_mri = rubies.select { |v| v =~ /\A\d+\.\d+.\d+\z/ }.max
8
+
9
+ if RUBY_VERSION == latest_mri
10
+ require 'simplecov'
11
+ SimpleCov.start do
12
+ add_filter '/spec/'
13
+ end
14
+ end
15
+ end
16
+
17
+ begin
18
+ require 'pry-byebug'
19
+ rescue LoadError
20
+ require 'pry'
21
+ end
22
+
23
+ require 'rom-elasticsearch'
24
+
25
+ SPEC_ROOT = Pathname(__FILE__).dirname
26
+
27
+ Dir[SPEC_ROOT.join('shared/**/*.rb')].each { |f| require f }
28
+
29
+ RSpec.configure do |config|
30
+ config.disable_monkey_patching!
31
+
32
+ # elasticsearch-dsl warnings are killing me - solnic
33
+ config.warnings = false
34
+
35
+ config.before do
36
+ module Test
37
+ end
38
+ end
39
+
40
+ config.after do
41
+ Object.send(:remove_const, :Test)
42
+ end
43
+ end
@@ -0,0 +1,13 @@
1
+ RSpec.describe ROM::Elasticsearch::Dataset, '#body' do
2
+ subject(:dataset) do
3
+ ROM::Elasticsearch::Dataset.new(client, params: { index: :users, type: :user })
4
+ end
5
+
6
+ include_context 'user fixtures'
7
+
8
+ it 'returns a new dataset with updated body' do
9
+ new_ds = dataset.body(id: 1)
10
+
11
+ expect(new_ds.body).to eql(id: 1)
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ RSpec.describe ROM::Elasticsearch::Dataset, '#delete' do
2
+ subject(:dataset) do
3
+ ROM::Elasticsearch::Dataset.new(client, params: { index: :users, type: :user })
4
+ end
5
+
6
+ include_context 'user fixtures'
7
+
8
+ it 'deletes data' do
9
+ expect(dataset.to_a.size).to eql(3)
10
+
11
+ dataset.refresh.delete
12
+
13
+ dataset.refresh
14
+
15
+ expect(dataset.to_a.size).to eql(0)
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ RSpec.describe ROM::Elasticsearch::Dataset, '#params' do
2
+ subject(:dataset) do
3
+ ROM::Elasticsearch::Dataset.new(client, params: { index: :users, type: :user })
4
+ end
5
+
6
+ include_context 'user fixtures'
7
+
8
+ it 'returns a new dataset with updated params' do
9
+ new_ds = dataset.params(size: 100)
10
+
11
+ expect(new_ds.params).to eql(size: 100, index: :users, type: :user)
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ RSpec.describe ROM::Elasticsearch::Dataset, '#put' do
2
+ subject(:dataset) do
3
+ ROM::Elasticsearch::Dataset.new(client, params: { index: :users, type: :user })
4
+ end
5
+
6
+ include_context 'setup'
7
+
8
+ it 'puts new data' do
9
+ result = dataset.put(username: 'eve')
10
+
11
+ expect(result['_id']).to_not be(nil)
12
+ expect(result['result']).to eql('created')
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ RSpec.describe ROM::Elasticsearch::Dataset, '#query_string' do
2
+ subject(:dataset) do
3
+ ROM::Elasticsearch::Dataset.new(client, params: { index: :users, type: :user })
4
+ end
5
+
6
+ include_context 'user fixtures'
7
+
8
+ it 'returns data matching query string' do
9
+ expect(dataset.query_string('username:alice').to_a).to eql([{'username' => 'alice'}])
10
+ expect(dataset.query_string('username:nisse').to_a).to eql([])
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ RSpec.describe ROM::Elasticsearch::Dataset, '#search' do
2
+ subject(:dataset) do
3
+ ROM::Elasticsearch::Dataset.new(client, params: { index: :users, type: :user })
4
+ end
5
+
6
+ include_context 'setup'
7
+
8
+ before do
9
+ dataset.put(username: 'eve')
10
+ dataset.put(username: 'bob')
11
+ dataset.put(username: 'alice')
12
+
13
+ dataset.refresh
14
+ end
15
+
16
+ it 'returns data matching query options' do
17
+ expect(dataset.search(query: {query_string: {query: 'username:eve'}}).to_a).
18
+ to eql([{'username' => 'eve'}])
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ require 'rom/lint/spec'
2
+
3
+ RSpec.describe ROM::Elasticsearch::Gateway do
4
+ let(:uri) { 'http://localhost:9200/rom-test' }
5
+
6
+ it_behaves_like 'a rom gateway' do
7
+ let(:identifier) { :elasticsearch }
8
+ let(:gateway) { ROM::Elasticsearch::Gateway }
9
+ end
10
+ end
@@ -0,0 +1,34 @@
1
+ require 'rom/elasticsearch/plugins/relation/query_dsl'
2
+ require 'rom/elasticsearch/relation'
3
+
4
+ RSpec.describe ROM::Elasticsearch::Relation, '#search' do
5
+ subject(:relation) { relations[:users] }
6
+
7
+ include_context 'setup'
8
+
9
+ before do
10
+ conf.relation(:users) do
11
+ schema do
12
+ attribute :id, ROM::Types::Int.meta(type: "integer")
13
+ attribute :name, ROM::Types::Int.meta(type: "text")
14
+ end
15
+
16
+ use :query_dsl
17
+ end
18
+
19
+ relation.command(:create).(id: 1, name: 'Jane')
20
+ relation.command(:create).(id: 2, name: 'John')
21
+
22
+ relation.refresh
23
+ end
24
+
25
+ it 'builds a query using a block-based DSL' do
26
+ result = relation.search do
27
+ query do
28
+ match name: 'Jane'
29
+ end
30
+ end
31
+
32
+ expect(result.to_a).to eql([{ id: 1, name: 'Jane' }])
33
+ end
34
+ end
@@ -0,0 +1,75 @@
1
+ require 'rom/elasticsearch/relation'
2
+
3
+ RSpec.describe ROM::Elasticsearch::Relation, '#create_index' do
4
+ subject(:relation) { relations[:users] }
5
+
6
+ include_context 'setup'
7
+
8
+ context 'when custom :index is configured' do
9
+ after do
10
+ relation.delete_index
11
+ end
12
+
13
+ context 'with default settings' do
14
+ before do
15
+ conf.relation(:users) do
16
+ schema do
17
+ attribute :id, ROM::Elasticsearch::Types::ID
18
+ attribute :name, ROM::Types::String
19
+ end
20
+ end
21
+ end
22
+
23
+ it 'creates an index' do
24
+ relation.create_index
25
+
26
+ expect(gateway.index?(:users)).to be(true)
27
+ end
28
+ end
29
+
30
+ context 'with customized settings' do
31
+ before do
32
+ conf.relation(:users) do
33
+ schema do
34
+ attribute :id, ROM::Types::Int
35
+ attribute :name, ROM::Types::String
36
+ end
37
+
38
+ index_settings number_of_shards: 2
39
+ end
40
+ end
41
+
42
+ it 'creates an index' do
43
+ relation.create_index
44
+
45
+ expect(gateway.index?(:users)).to be(true)
46
+ expect(relation.dataset.settings['number_of_shards']).to eql("2")
47
+ end
48
+ end
49
+
50
+ context 'with customized attribute mappings' do
51
+ before do
52
+ conf.relation(:users) do
53
+ schema do
54
+ attribute :id, ROM::Elasticsearch::Types::ID
55
+ attribute :name, ROM::Elasticsearch::Types.Keyword
56
+ attribute :desc, ROM::Elasticsearch::Types.Text(analyzer: "snowball")
57
+ end
58
+
59
+ index_settings number_of_shards: 2
60
+ end
61
+ end
62
+
63
+ it 'creates an index' do
64
+ relation.create_index
65
+
66
+ expect(gateway.index?(:users)).to be(true)
67
+
68
+ expect(relation.dataset.mappings).
69
+ to eql("properties" => {
70
+ "name" => { "type" => "keyword" },
71
+ "desc" => { "type" => "text", "analyzer" => "snowball" }})
72
+ end
73
+ end
74
+ end
75
+ end