rom-elasticsearch 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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +3 -0
- data/.travis.yml +24 -0
- data/.yardopts +7 -0
- data/CHANGELOG.md +3 -0
- data/CONTRIBUTING.md +29 -0
- data/Gemfile +20 -0
- data/LICENSE.txt +22 -0
- data/README.md +40 -0
- data/Rakefile +19 -0
- data/lib/rom-elasticsearch.rb +1 -0
- data/lib/rom/elasticsearch.rb +8 -0
- data/lib/rom/elasticsearch/attribute.rb +30 -0
- data/lib/rom/elasticsearch/commands.rb +49 -0
- data/lib/rom/elasticsearch/dataset.rb +219 -0
- data/lib/rom/elasticsearch/errors.rb +23 -0
- data/lib/rom/elasticsearch/gateway.rb +81 -0
- data/lib/rom/elasticsearch/plugins/relation/query_dsl.rb +57 -0
- data/lib/rom/elasticsearch/query_methods.rb +64 -0
- data/lib/rom/elasticsearch/relation.rb +241 -0
- data/lib/rom/elasticsearch/schema.rb +26 -0
- data/lib/rom/elasticsearch/types.rb +33 -0
- data/lib/rom/elasticsearch/version.rb +5 -0
- data/rom-elasticsearch.gemspec +27 -0
- data/spec/integration/rom/elasticsearch/relation/command_spec.rb +47 -0
- data/spec/shared/setup.rb +16 -0
- data/spec/shared/unit/user_fixtures.rb +15 -0
- data/spec/shared/unit/users.rb +18 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/unit/rom/elasticsearch/dataset/body_spec.rb +13 -0
- data/spec/unit/rom/elasticsearch/dataset/delete_spec.rb +17 -0
- data/spec/unit/rom/elasticsearch/dataset/params_spec.rb +13 -0
- data/spec/unit/rom/elasticsearch/dataset/put_spec.rb +14 -0
- data/spec/unit/rom/elasticsearch/dataset/query_string_spec.rb +12 -0
- data/spec/unit/rom/elasticsearch/dataset/search_spec.rb +20 -0
- data/spec/unit/rom/elasticsearch/gateway_spec.rb +10 -0
- data/spec/unit/rom/elasticsearch/plugins/relation/query_dsl_spec.rb +34 -0
- data/spec/unit/rom/elasticsearch/relation/create_index_spec.rb +75 -0
- data/spec/unit/rom/elasticsearch/relation/dataset_spec.rb +26 -0
- data/spec/unit/rom/elasticsearch/relation/delete_spec.rb +32 -0
- data/spec/unit/rom/elasticsearch/relation/get_spec.rb +22 -0
- data/spec/unit/rom/elasticsearch/relation/map_spec.rb +18 -0
- data/spec/unit/rom/elasticsearch/relation/pluck_spec.rb +18 -0
- data/spec/unit/rom/elasticsearch/relation/query_spec.rb +18 -0
- data/spec/unit/rom/elasticsearch/relation/query_string_spec.rb +18 -0
- data/spec/unit/rom/elasticsearch/relation/search_spec.rb +18 -0
- data/spec/unit/rom/elasticsearch/relation/to_a_spec.rb +28 -0
- 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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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,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
|