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.
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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7ea7ae9dbc43c75ca9583e5df195db789ea28430
4
+ data.tar.gz: 2d1b2523c9acc5c248b1841685588c73943e8990
5
+ SHA512:
6
+ metadata.gz: 887ffb629635a427b714a6e66cc8f4ba54251efa5b17ec8c70d1f8704f9b79de460b30402c437b38bb0da75b09d860e1ae5048f6feda933d5560b029f85fae9d
7
+ data.tar.gz: 46b872af00f24af35f1ea18cfbcda7dc5d2b2fedbdeea110c08c142c7199424d80218a7deab7c65e3fdfab90271320a2e6fa25eaf9e219856465a8f76ec9bc60
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --order random
3
+ --require ./spec/spec_helper.rb
data/.travis.yml ADDED
@@ -0,0 +1,24 @@
1
+ language: ruby
2
+ sudo: false
3
+ cache: bundler
4
+ services:
5
+ - elasticsearch
6
+ bundler_args: --without yard guard benchmarks tools
7
+ before_script:
8
+ - curl -XPUT http://localhost:9200/rom-test
9
+ script: "bundle exec rake ci"
10
+ rvm:
11
+ - 2.3.4
12
+ - 2.4.1
13
+ - jruby-9.1.12.0
14
+ env:
15
+ global:
16
+ - JRUBY_OPTS='--dev -J-Xmx1024M'
17
+ - COVERAGE='true'
18
+ notifications:
19
+ webhooks:
20
+ urls:
21
+ - https://webhooks.gitter.im/e/39e1225f489f38b0bd09
22
+ on_success: change
23
+ on_failure: always
24
+ on_start: false
data/.yardopts ADDED
@@ -0,0 +1,7 @@
1
+ --plugin junk
2
+ --query '@api.text != "private"'
3
+ --embed-mixins
4
+ -r README.md
5
+ --markup-provider=redcarpet
6
+ --markup=markdown
7
+ --files CHANGELOG.md
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## v0.1.0 2017-11-17
2
+
3
+ First public release
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,29 @@
1
+ # Issue Guidelines
2
+
3
+ ## Reporting bugs
4
+
5
+ If you found a bug, report an issue and describe what's the expected behavior versus what actually happens. If the bug causes a crash, attach a full backtrace. If possible, a reproduction script showing the problem is highly appreciated.
6
+
7
+ ## Reporting feature requests
8
+
9
+ Report a feature request **only after discussing it first on [discourse.rom-rb.org](https://discourse.rom-rb.org)** where it was accepted. Please provide a concise description of the feature, don't link to a discussion thread, and instead summarize what was discussed.
10
+
11
+ ## Reporting questions, support requests, ideas, concerns etc.
12
+
13
+ **PLEASE DON'T** - use [discourse.rom-rb.org](http://discourse.rom-rb.org) instead.
14
+
15
+ # Pull Request Guidelines
16
+
17
+ A Pull Request will only be accepted if it addresses a specific issue that was reported previously, or fixes typos, mistakes in documentation etc.
18
+
19
+ Other requirements:
20
+
21
+ 1) Do not open a pull request if you can't provide tests along with it. If you have problems writing tests, ask for help in the related issue.
22
+ 2) Follow the style conventions of the surrounding code. In most cases, this is standard ruby style.
23
+ 3) Add API documentation if it's a new feature
24
+ 4) Update API documentation if it changes an existing feature
25
+ 5) Bonus points for sending a PR to [github.com/rom-rb/rom-rb.org](https://github.com/rom-rb/rom-rb.org) which updates user documentation and guides
26
+
27
+ # Asking for help
28
+
29
+ If these guidelines aren't helpful, and you're stuck, please post a message on [discourse.rom-rb.org](https://discourse.rom-rb.org).
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rom-elasticsearch.gemspec
4
+ gemspec
5
+
6
+ gem 'rom', git: 'https://github.com/rom-rb/rom', branch: 'master' do
7
+ gem 'rom-core'
8
+ gem 'rom-mapper'
9
+ end
10
+
11
+ gem 'codeclimate-test-reporter', require: false
12
+ gem 'simplecov', require: false
13
+
14
+ gem 'pry-byebug', platform: :mri
15
+ gem 'pry', platform: :jruby
16
+ gem 'elasticsearch-dsl'
17
+
18
+ group :tools do
19
+ gem 'kramdown' # for yard
20
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) rom-rb team
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ [gem]: https://rubygems.org/gems/rom-elasticsearch
2
+ [travis]: https://travis-ci.org/rom-rb/rom-elasticsearch
3
+ [gemnasium]: https://gemnasium.com/rom-rb/rom-elasticsearch
4
+ [codeclimate]: https://codeclimate.com/github/rom-rb/rom-elasticsearch
5
+ [inchpages]: http://inch-ci.org/github/rom-rb/rom-elasticsearch
6
+
7
+ # rom-elasticsearch
8
+
9
+ [![Gem Version](https://badge.fury.io/rb/rom-elasticsearch.svg)][gem]
10
+ [![Build Status](https://travis-ci.org/rom-rb/rom-elasticsearch.svg?branch=master)][travis]
11
+ [![Dependency Status](https://gemnasium.com/rom-rb/rom-elasticsearch.svg)][gemnasium]
12
+ [![Code Climate](https://codeclimate.com/github/rom-rb/rom-elasticsearch/badges/gpa.svg)][codeclimate]
13
+ [![Test Coverage](https://codeclimate.com/github/rom-rb/rom-elasticsearch/badges/coverage.svg)][codeclimate]
14
+ [![Inline docs](http://inch-ci.org/github/rom-rb/rom-elasticsearch.svg?branch=master)][inchpages]
15
+
16
+ ElasticSearch support for [rom-rb](https://github.com/rom-rb/rom).
17
+
18
+ Resources:
19
+
20
+ - [API Documentation](http://api.rom-rb.org/rom-elasticsearch)
21
+
22
+ ## Installation
23
+
24
+ Add this line to your application's Gemfile:
25
+
26
+ ```ruby
27
+ gem 'rom-elasticsearch'
28
+ ```
29
+
30
+ And then execute:
31
+
32
+ $ bundle
33
+
34
+ Or install it yourself as:
35
+
36
+ $ gem install rom-elasticsearch
37
+
38
+ ## License
39
+
40
+ See `LICENSE` file.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task default: [:ci]
6
+
7
+ desc "Run CI tasks"
8
+ task ci: [:spec]
9
+
10
+ begin
11
+ require "rubocop/rake_task"
12
+
13
+ Rake::Task[:default].enhance [:rubocop]
14
+
15
+ RuboCop::RakeTask.new do |task|
16
+ task.options << "--display-cop-names"
17
+ end
18
+ rescue LoadError
19
+ end
@@ -0,0 +1 @@
1
+ require 'rom/elasticsearch'
@@ -0,0 +1,8 @@
1
+ require 'rom'
2
+
3
+ require 'rom/elasticsearch/version'
4
+ require 'rom/elasticsearch/gateway'
5
+ require 'rom/elasticsearch/relation'
6
+ require 'rom/elasticsearch/commands'
7
+
8
+ ROM.register_adapter(:elasticsearch, ROM::Elasticsearch)
@@ -0,0 +1,30 @@
1
+ require 'rom/attribute'
2
+
3
+ module ROM
4
+ module Elasticsearch
5
+ # ES-specific attribute types for schemas
6
+ #
7
+ # @api public
8
+ class Attribute < ROM::Attribute
9
+ INTERNAL_META_KEYS = %i[name source primary_key].freeze
10
+
11
+ # Return ES mapping properties
12
+ #
13
+ # @return [Hash]
14
+ #
15
+ # @api public
16
+ memoize def properties
17
+ type.meta.reject { |k, _| INTERNAL_META_KEYS.include?(k) }
18
+ end
19
+
20
+ # Return if an attribute has any ES mappings
21
+ #
22
+ # @return [Bool]
23
+ #
24
+ # @api public
25
+ def properties?
26
+ properties.size > 0
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,49 @@
1
+ require 'rom/commands'
2
+
3
+ module ROM
4
+ module Elasticsearch
5
+ # ElasticSearch relation commands
6
+ #
7
+ # @api public
8
+ class Commands
9
+ # Create command
10
+ #
11
+ # @api public
12
+ class Create < ROM::Commands::Create
13
+ # @api private
14
+ def execute(attributes)
15
+ tuple = input[attributes]
16
+
17
+ result =
18
+ if _id
19
+ dataset.params(id: tuple.fetch(_id)).put(tuple)
20
+ else
21
+ dataset.put(tuple)
22
+ end
23
+ [relation.get(result['_id']).one]
24
+ end
25
+
26
+ private
27
+
28
+ # @api private
29
+ def dataset
30
+ relation.dataset
31
+ end
32
+
33
+ def _id
34
+ relation.schema.primary_key_name
35
+ end
36
+ end
37
+
38
+ # Delete command
39
+ #
40
+ # @api public
41
+ class Delete < ROM::Commands::Delete
42
+ # @api private
43
+ def execute
44
+ relation.dataset.delete
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,219 @@
1
+ require 'rom/initializer'
2
+
3
+ require 'rom/elasticsearch/query_methods'
4
+ require 'rom/elasticsearch/errors'
5
+
6
+ module ROM
7
+ module Elasticsearch
8
+ # Elasticsearch dataset
9
+ #
10
+ # Uses an elasticsearch client object provided by the gateway, holds basic
11
+ # params with information about index name and type, and optional body for
12
+ # additional queries.
13
+ #
14
+ # Dataset object also provide meta information about indices, like custom
15
+ # settings and mappings.
16
+ #
17
+ # @api public
18
+ class Dataset
19
+ extend Initializer
20
+
21
+ include QueryMethods
22
+
23
+ # Default query options
24
+ ALL = { query: { match_all: EMPTY_HASH } }.freeze
25
+
26
+ # The source key in raw results
27
+ SOURCE_KEY = '_source'.freeze
28
+
29
+ # @!attribute [r] client
30
+ # @return [::Elasticsearch::Client] configured client from the gateway
31
+ param :client
32
+
33
+ # @!attribute [r] params
34
+ # @return [Hash] default params
35
+ option :params, default: -> { EMPTY_HASH }
36
+
37
+ # @!attribute [r] client
38
+ # @return [Hash] default body
39
+ option :body, default: -> { EMPTY_HASH }
40
+
41
+ # Put new data under configured index
42
+ #
43
+ # @param [Hash] data
44
+ #
45
+ # @return [Hash]
46
+ #
47
+ # @api public
48
+ def put(data)
49
+ client.index(**params, body: data)
50
+ end
51
+
52
+ # Return index settings
53
+ #
54
+ # @return [Hash]
55
+ #
56
+ # @api public
57
+ def settings
58
+ client.indices.get_settings[index.to_s]['settings']['index']
59
+ end
60
+
61
+ # Return index mappings
62
+ #
63
+ # @return [Hash]
64
+ #
65
+ # @api public
66
+ def mappings
67
+ client.indices.get_mapping[index.to_s]['mappings'][type.to_s]
68
+ end
69
+
70
+ # Delete everything matching configured params and/or body
71
+ #
72
+ # If body is empty it *will delete everything**
73
+ #
74
+ # @return [Hash] raw response hash from the client
75
+ #
76
+ # @api public
77
+ def delete
78
+ if body.empty? && params[:id]
79
+ client.delete(params)
80
+ elsif body.empty?
81
+ client.delete_by_query(params.merge(body: body.merge(ALL)))
82
+ else
83
+ client.delete_by_query(params.merge(body: body))
84
+ end
85
+ end
86
+
87
+ # Materialize the dataset
88
+ #
89
+ # @return [Array<Hash>]
90
+ #
91
+ # @api public
92
+ def to_a
93
+ to_enum.to_a
94
+ end
95
+
96
+ # Materialize and iterate over results
97
+ #
98
+ # @yieldparam [Hash]
99
+ #
100
+ # @raise [SearchError] in case of the client raising an exception
101
+ #
102
+ # @api public
103
+ def each
104
+ return to_enum unless block_given?
105
+ view.each do |result|
106
+ yield(result[SOURCE_KEY])
107
+ end
108
+ rescue ::Elasticsearch::Transport::Transport::Error => e
109
+ raise SearchError.new(e, options)
110
+ end
111
+
112
+ # Map dataset tuples
113
+ #
114
+ # @yieldparam [Hash]
115
+ #
116
+ # @return [Array]
117
+ #
118
+ # @api public
119
+ def map(&block)
120
+ to_a.map(&block)
121
+ end
122
+
123
+ # Return configured type from params
124
+ #
125
+ # @return [Symbol]
126
+ #
127
+ # @api public
128
+ def type
129
+ params[:type]
130
+ end
131
+
132
+ # Return configured index name
133
+ #
134
+ # @return [Symbol]
135
+ #
136
+ # @api public
137
+ def index
138
+ params[:index]
139
+ end
140
+
141
+ # Return a new dataset with new body
142
+ #
143
+ # @param [Hash] new New body data
144
+ #
145
+ # @return [Hash]
146
+ #
147
+ # @api public
148
+ def body(new = nil)
149
+ if new.nil?
150
+ @body
151
+ else
152
+ with(body: body.merge(new))
153
+ end
154
+ end
155
+
156
+ # Return a new dataset with new params
157
+ #
158
+ # @param [Hash] new New params data
159
+ #
160
+ # @return [Hash]
161
+ #
162
+ # @api public
163
+ def params(new = nil)
164
+ if new.nil?
165
+ @params
166
+ else
167
+ with(params: params.merge(new))
168
+ end
169
+ end
170
+
171
+ # Refresh index
172
+ #
173
+ # @return [Dataset]
174
+ #
175
+ # @api public
176
+ def refresh
177
+ client.indices.refresh(index: index)
178
+ self
179
+ end
180
+
181
+ # Create an index
182
+ #
183
+ # @param [Hash] opts ES options
184
+ #
185
+ # @api public
186
+ #
187
+ # @return [Hash]
188
+ def create_index(opts = EMPTY_HASH)
189
+ client.indices.create(params.merge(opts))
190
+ end
191
+
192
+ # Delete an index
193
+ #
194
+ # @param [Hash] opts ES options
195
+ #
196
+ # @api public
197
+ #
198
+ # @return [Hash]
199
+ def delete_index(opts = EMPTY_HASH)
200
+ client.indices.delete(params.merge(opts))
201
+ end
202
+
203
+ private
204
+
205
+ # Return results of a query based on configured params and body
206
+ #
207
+ # @return [Array<Hash>]
208
+ #
209
+ # @api private
210
+ def view
211
+ if params[:id]
212
+ [client.get(params)]
213
+ else
214
+ client.search(**params, body: body).fetch('hits').fetch('hits')
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end