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
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