elastic-rails 0.6.4 → 0.7.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.
@@ -18,7 +18,7 @@ module Elastic::Nodes
18
18
  @must_nots = []
19
19
  @shoulds = []
20
20
  @filters = []
21
- @disable_coord = !Elastic::Configuration.coord_similarity
21
+ @disable_coord = !Elastic.config.coord_similarity
22
22
  end
23
23
 
24
24
  def must(_node)
data/lib/elastic/query.rb CHANGED
@@ -86,7 +86,7 @@ module Elastic
86
86
  end
87
87
 
88
88
  def execute(_query)
89
- raw = @index.adaptor.query(
89
+ raw = @index.connector.query(
90
90
  type: @index.definition.types,
91
91
  query: _query.render
92
92
  )
@@ -4,13 +4,16 @@ require "elastic/railties/ar_middleware"
4
4
  require "elastic/railties/configuration_extensions"
5
5
  require "elastic/railties/type_extensions"
6
6
  require "elastic/railties/query_extensions"
7
- require "elastic/railties/indexing_job"
7
+ # disabled for now: require "elastic/railties/indexing_job"
8
8
  require "elastic/railties/indexable_record"
9
9
 
10
10
  module Elastic
11
11
  class Railtie < Rails::Railtie
12
12
  initializer "elastic.configure_rails_initialization" do
13
- Elastic.configure Rails.application.config_for(:elastic).merge(logger: Rails.logger)
13
+ Elastic.configure Rails.application.config_for(:elastic).merge(
14
+ time_zone: Rails.application.config.time_zone,
15
+ logger: Rails.logger
16
+ )
14
17
 
15
18
  # Make every activerecord model indexable
16
19
  ActiveRecord::Base.send(:include, Elastic::Railties::IndexableRecord)
@@ -30,7 +33,7 @@ module Elastic
30
33
  end
31
34
 
32
35
  # Add activerecord related configuration parameters
33
- module Elastic::Configuration
36
+ class Elastic::Configuration
34
37
  include Elastic::Railties::ConfigurationExtensions
35
38
  end
36
39
 
@@ -1,13 +1,10 @@
1
1
  module Elastic::Railties
2
2
  module ConfigurationExtensions
3
3
  def self.included(_klass)
4
- _klass.extend ClassMethods
4
+ _klass::DEFAULTS[:active_job_queue] = :default
5
+ _klass::DEFAULTS[:indices_path] = 'app/indices'
5
6
  end
6
7
 
7
- module ClassMethods
8
- def active_job_queue
9
- config[:active_job_queue] || :default
10
- end
11
- end
8
+ attr_accessor :active_job_queue, :indices_path
12
9
  end
13
10
  end
@@ -18,21 +18,31 @@ module Elastic::Railties
18
18
  @constantized_index_class ||= index_class.constantize
19
19
  end
20
20
 
21
- def index(_options)
22
- on = _options.delete(:on)
21
+ def index(on: nil, unindex: true, delayed: false)
22
+ raise NotImplementedError, 'delayed indexing not implemented' if delayed
23
+
23
24
  if on == :create
24
- index_on_create _options
25
+ index_on_create
25
26
  elsif on == :save
26
- index_on_save _options
27
+ index_on_save
28
+ else
29
+ raise ArgumentError, 'must provide an indexing target when calling index \
30
+ (ie: `index on: :save`)'
27
31
  end
32
+
33
+ unindex_on_destroy if unindex
28
34
  end
29
35
 
30
36
  def index_on_create(_options = {})
31
- after_create(_options) { index_later }
37
+ after_create(_options) { index_now }
32
38
  end
33
39
 
34
40
  def index_on_save(_options = {})
35
- after_save(_options) { index_later }
41
+ after_save(_options) { index_now }
42
+ end
43
+
44
+ def unindex_on_destroy(_options = {})
45
+ before_destroy(_options) { unindex_now }
36
46
  end
37
47
  end
38
48
 
@@ -40,8 +50,8 @@ module Elastic::Railties
40
50
  self.class.constantized_index_class.index self
41
51
  end
42
52
 
43
- def index_later
44
- IndexingJob.set(queue: Elastic::Configuration.active_job_queue).perform_later(self)
53
+ def unindex_now
54
+ self.class.constantized_index_class.delete self
45
55
  end
46
56
  end
47
57
  end
@@ -1,19 +1,25 @@
1
1
  namespace :es do
2
2
  desc "Elastic: Updates indices mappings"
3
- task migrate: :environment do
3
+ task :remap, [:index] => :environment do |_, args|
4
4
  Elastic.configure logger: Logger.new(STDOUT)
5
- Elastic.migrate
5
+ Elastic.remap args.index
6
+ end
7
+
8
+ desc "Elastic: Updates indices mappings, rebuilding index if necessary"
9
+ task :migrate, [:index] => :environment do |_, args|
10
+ Elastic.configure logger: Logger.new(STDOUT)
11
+ Elastic.migrate args.index
6
12
  end
7
13
 
8
14
  desc "Elastic: Rebuilds indices from source data"
9
- task reindex: :environment do
15
+ task :reindex, [:index] => :environment do |_, args|
10
16
  Elastic.configure logger: Logger.new(STDOUT)
11
- Elastic.reindex
17
+ Elastic.reindex args.index
12
18
  end
13
19
 
14
20
  desc "Elastic: Lists indices stats"
15
- task stats: :environment do
21
+ task :stats, [:index] => :environment do |_, args|
16
22
  Elastic.configure logger: Logger.new(STDOUT)
17
- Elastic.stats
23
+ Elastic.stats args.index
18
24
  end
19
25
  end
@@ -8,11 +8,23 @@ module Elastic::Railties
8
8
  end
9
9
  end
10
10
 
11
+ def remap(_index = nil)
12
+ logger.info "Remapping all indices" if _index.nil?
13
+ indices(_index).each do |index|
14
+ logger.info "Remapping index #{index.suffix}"
15
+ handle_errors do
16
+ unless index.connector.remap
17
+ logger.info 'Mapping couldnt be changed, make sure you call migrate'
18
+ end
19
+ end
20
+ end
21
+ end
22
+
11
23
  def migrate(_index = nil)
12
24
  logger.info "Migrating all indices" if _index.nil?
13
25
  indices(_index).each do |index|
14
26
  logger.info "Migrating index #{index.suffix}"
15
- handle_errors { index.mapping.migrate }
27
+ handle_errors { index.migrate }
16
28
  end
17
29
  end
18
30
 
@@ -45,7 +57,7 @@ module Elastic::Railties
45
57
  end
46
58
 
47
59
  def indices_paths
48
- Rails.root.join(Elastic::Configuration.indices_path)
60
+ Rails.root.join(Elastic.config.indices_path)
49
61
  end
50
62
 
51
63
  def handle_errors
@@ -56,7 +68,7 @@ module Elastic::Railties
56
68
  end
57
69
 
58
70
  def logger
59
- Elastic::Configuration.logger
71
+ Elastic.logger
60
72
  end
61
73
  end
62
74
  end
data/lib/elastic/type.rb CHANGED
@@ -10,6 +10,10 @@ module Elastic
10
10
  :coord_similarity, :limit, :offset, :pluck, :ids, :total
11
11
  end
12
12
 
13
+ def self.default_suffix
14
+ to_s.underscore
15
+ end
16
+
13
17
  def self.suffix
14
18
  @suffix || default_suffix
15
19
  end
@@ -19,38 +23,49 @@ module Elastic
19
23
  end
20
24
 
21
25
  def self.import_batch_size
22
- @import_batch_size || Configuration.import_batch_size
26
+ @import_batch_size || Elastic.config.import_batch_size
23
27
  end
24
28
 
25
29
  def self.import_batch_size=(_value)
26
30
  @import_batch_size = _value
27
31
  end
28
32
 
29
- def self.adaptor
30
- @adaptor ||= Elastic::Core::Adaptor.new(suffix)
33
+ def self.connector
34
+ @connector ||= begin
35
+ Elastic::Core::Connector.new(
36
+ suffix,
37
+ definition.types,
38
+ definition.as_es_mapping
39
+ ).tap do |conn|
40
+ if Elastic.config.whiny_indices && conn.status != :ready
41
+ raise 'elastic index out of sync, try migrating'
42
+ end
43
+ end
44
+ end
31
45
  end
32
46
 
33
- def self.mapping
34
- @mapping ||= load_mapping
47
+ def self.index_name
48
+ connector.index_name
35
49
  end
36
50
 
37
- def self.reindex(verbose: true, batch_size: nil)
38
- drop
39
- mapping.migrate
40
- batch_size = batch_size || import_batch_size
51
+ def self.migrate
52
+ connector.migrate(batch_size: import_batch_size)
53
+ self
54
+ end
41
55
 
42
- Commands::ImportIndexDocuments.for(
43
- index: self,
44
- verbose: verbose,
45
- batch_size: batch_size
46
- )
56
+ def self.reindex(verbose: true)
57
+ connector.rollover do
58
+ Commands::ImportIndexDocuments.for(
59
+ index: self,
60
+ verbose: verbose,
61
+ batch_size: import_batch_size
62
+ )
63
+ end
47
64
 
48
- ensure_full_mapping
49
65
  self
50
66
  end
51
67
 
52
68
  def self.import(_collection, batch_size: nil)
53
- enforce_mapping!
54
69
  batch_size = batch_size || import_batch_size
55
70
 
56
71
  Commands::ImportIndexDocuments.for(
@@ -59,7 +74,6 @@ module Elastic
59
74
  batch_size: batch_size
60
75
  )
61
76
 
62
- ensure_full_mapping
63
77
  self
64
78
  end
65
79
 
@@ -67,52 +81,37 @@ module Elastic
67
81
  new(_object).save
68
82
  end
69
83
 
84
+ def self.delete(_object)
85
+ wrapper = new(_object)
86
+ id = wrapper.read_elastic_id
87
+ raise ArgumentError, 'index does not provide an id' if id.nil?
88
+
89
+ connector.delete(
90
+ wrapper.read_elastic_type,
91
+ wrapper.read_elastic_id
92
+ )
93
+
94
+ nil
95
+ end
96
+
70
97
  def self.query
71
- enforce_mapping!
72
- ensure_full_mapping
73
98
  Query.new self
74
99
  end
75
100
 
76
101
  def self.drop
77
- adaptor.drop if adaptor.exists?
102
+ connector.drop
78
103
  self
79
104
  end
80
105
 
81
106
  def self.refresh
82
- adaptor.refresh
107
+ connector.refresh
83
108
  self
84
109
  end
85
110
 
86
- def self.enforce_mapping!
87
- if mapping.out_of_sync?
88
- raise 'elastic mapping out of sync, run `rake es:migrate`'
89
- end
90
- end
91
-
92
- def self.ensure_full_mapping
93
- if mapping.incomplete?
94
- mapping.fetch
95
- end
96
- end
97
-
98
111
  def save
99
112
  self.class.tap do |klass|
100
- klass.enforce_mapping!
101
- klass.adaptor.index as_es_document
102
- klass.ensure_full_mapping
113
+ klass.connector.index as_elastic_document
103
114
  end
104
115
  end
105
-
106
- def self.load_mapping
107
- Elastic::Core::MappingManager.new(adaptor, definition).tap(&:fetch)
108
- end
109
-
110
- private_class_method :load_mapping
111
-
112
- def self.default_suffix
113
- to_s.underscore
114
- end
115
-
116
- private_class_method :default_suffix
117
116
  end
118
117
  end
@@ -1,3 +1,3 @@
1
1
  module Elastic
2
- VERSION = "0.6.4"
2
+ VERSION = "0.7.0"
3
3
  end
data/lib/elastic.rb CHANGED
@@ -2,6 +2,7 @@ require "elasticsearch"
2
2
 
3
3
  require "elastic/version"
4
4
  require "elastic/configuration"
5
+ require "elastic/errors"
5
6
 
6
7
  require "elastic/support/command"
7
8
  require "elastic/support/transform"
@@ -11,6 +12,7 @@ require "elastic/commands/import_index_documents"
11
12
  require "elastic/commands/build_query_from_params"
12
13
  require "elastic/commands/build_agg_from_params"
13
14
  require "elastic/commands/build_sort_from_params"
15
+ require "elastic/commands/compare_mappings"
14
16
 
15
17
  require "elastic/results/base"
16
18
  require "elastic/results/scored_item"
@@ -64,9 +66,8 @@ require "elastic/datatypes/time"
64
66
  require "elastic/fields/value"
65
67
  require "elastic/fields/nested"
66
68
 
69
+ require "elastic/core/connector"
67
70
  require "elastic/core/definition"
68
- require "elastic/core/adaptor"
69
- require "elastic/core/mapping_manager"
70
71
  require "elastic/core/serializer"
71
72
  require "elastic/core/middleware"
72
73
  require "elastic/core/base_middleware"
@@ -89,8 +90,17 @@ require "elastic/query"
89
90
  require "elastic/nested_query"
90
91
 
91
92
  module Elastic
92
- def self.configure(*_args)
93
- Configuration.configure(*_args)
93
+ def self.config
94
+ @config ||= Configuration.new
95
+ end
96
+
97
+ def self.logger
98
+ config.logger
99
+ end
100
+
101
+ def self.configure(_options = nil, &_block)
102
+ config.assign_attributes(_options) unless _options.nil?
103
+ _block.call(config) unless _block.nil?
94
104
  end
95
105
 
96
106
  def self.register_middleware(_middleware)
@@ -2,6 +2,7 @@ development:
2
2
  host: '127.0.0.1'
3
3
  port: 9200
4
4
  index: '<%= Rails.application.class.parent_name.to_s.underscore %>_develop'
5
+ whiny_indices: true
5
6
 
6
7
  test:
7
8
  host: '127.0.0.1'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elastic-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.4
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ignacio Baixas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-08-18 00:00:00.000000000 Z
11
+ date: 2016-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: elasticsearch
@@ -228,13 +228,13 @@ files:
228
228
  - lib/elastic/commands/build_agg_from_params.rb
229
229
  - lib/elastic/commands/build_query_from_params.rb
230
230
  - lib/elastic/commands/build_sort_from_params.rb
231
+ - lib/elastic/commands/compare_mappings.rb
231
232
  - lib/elastic/commands/import_index_documents.rb
232
233
  - lib/elastic/configuration.rb
233
- - lib/elastic/core/adaptor.rb
234
234
  - lib/elastic/core/base_middleware.rb
235
+ - lib/elastic/core/connector.rb
235
236
  - lib/elastic/core/default_middleware.rb
236
237
  - lib/elastic/core/definition.rb
237
- - lib/elastic/core/mapping_manager.rb
238
238
  - lib/elastic/core/middleware.rb
239
239
  - lib/elastic/core/query_assembler.rb
240
240
  - lib/elastic/core/query_config.rb
@@ -248,6 +248,7 @@ files:
248
248
  - lib/elastic/dsl/bool_query_builder.rb
249
249
  - lib/elastic/dsl/bool_query_context.rb
250
250
  - lib/elastic/dsl/metric_builder.rb
251
+ - lib/elastic/errors.rb
251
252
  - lib/elastic/fields/nested.rb
252
253
  - lib/elastic/fields/value.rb
253
254
  - lib/elastic/nested_query.rb
@@ -1,126 +0,0 @@
1
- module Elastic::Core
2
- class Adaptor
3
- DEFAULT_SETTINGS = {
4
- refresh_interval: '1s',
5
- number_of_replicas: 1
6
- }
7
-
8
- def initialize(_suffix)
9
- @suffix = _suffix
10
- end
11
-
12
- def index_name
13
- @index_name ||= "#{Elastic::Configuration.index_name}_#{@suffix}"
14
- end
15
-
16
- def remap(_type, _mapping)
17
- # TODO
18
- end
19
-
20
- def exists?
21
- api_client.indices.exists? build_options
22
- end
23
-
24
- def ensure_index
25
- create unless exists?
26
- self
27
- end
28
-
29
- def create
30
- api_client.indices.create build_options
31
- api_client.cluster.health wait_for_status: 'yellow'
32
- self
33
- end
34
-
35
- def drop
36
- api_client.indices.delete build_options
37
- self
38
- end
39
-
40
- def exists_type?(_type)
41
- api_client.indices.exists_type build_options(type: _type)
42
- end
43
-
44
- def exists_mapping?(_type)
45
- !api_client.indices.get_mapping(build_options(type: _type)).empty?
46
- end
47
-
48
- def get_mappings(type: nil)
49
- mappings = api_client.indices.get_mapping build_options(type: type)
50
- mappings[index_name]['mappings']
51
- end
52
-
53
- def set_mapping(_type, _mapping)
54
- api_client.indices.put_mapping build_options(
55
- type: _type,
56
- body: _mapping
57
- )
58
- self
59
- end
60
-
61
- def index(_document)
62
- api_client.index build_options(
63
- id: _document['_id'],
64
- type: _document['_type'],
65
- body: _document['data']
66
- )
67
- self
68
- end
69
-
70
- def bulk_index(_documents)
71
- body = _documents.map { |doc| { 'index' => doc } }
72
-
73
- retry_on_temporary_error('bulk indexing') do
74
- api_client.bulk build_options(body: body)
75
- end
76
-
77
- self
78
- end
79
-
80
- def with_settings(_options)
81
- _options = DEFAULT_SETTINGS.merge _options
82
- api_client.indices.put_settings build_options(body: { index: _options })
83
- yield
84
- ensure
85
- api_client.indices.put_settings build_options(body: { index: DEFAULT_SETTINGS })
86
- end
87
-
88
- def refresh
89
- api_client.indices.refresh build_options
90
- self
91
- end
92
-
93
- def find(_id, type: '_all')
94
- api_client.get build_options(type: type, id: _id)
95
- end
96
-
97
- def count(type: nil, query: nil)
98
- api_client.count(build_options(type: type, body: query))['count']
99
- end
100
-
101
- def query(type: nil, query: nil)
102
- api_client.search build_options(type: type, body: query)
103
- end
104
-
105
- private
106
-
107
- def retry_on_temporary_error(_action, retries: 3)
108
- return yield
109
- rescue Elasticsearch::Transport::Transport::Errors::ServiceUnavailable,
110
- Elasticsearch::Transport::Transport::Errors::GatewayTimeout => exc
111
- raise if retries <= 0
112
-
113
- Configuration.logger.warn("#{exc.class} error during '#{_action}', retrying!")
114
- retries -= 1
115
- retry
116
- end
117
-
118
- def api_client
119
- Elastic::Configuration.api_client
120
- end
121
-
122
- def build_options(_options = {})
123
- { index: index_name }.merge! _options
124
- end
125
- end
126
- end
@@ -1,120 +0,0 @@
1
- module Elastic::Core
2
- class MappingManager
3
- attr_reader :adaptor, :definition
4
-
5
- def initialize(_adaptor, _definition)
6
- @adaptor = _adaptor
7
- @definition = _definition
8
- @status = :pending
9
- end
10
-
11
- def out_of_sync?
12
- @status == :out_of_sync
13
- end
14
-
15
- def incomplete?
16
- @status == :incomplete
17
- end
18
-
19
- def fetch
20
- begin
21
- mappings = @adaptor.get_mappings
22
- mappings = @definition.types.map { |t| mappings[t] }.reject(&:nil?)
23
- @index = merge_mappings_into_index mappings
24
- rescue Elasticsearch::Transport::Transport::Errors::NotFound
25
- # ignore not-found errors when fetching mappings
26
- @index = nil
27
- end
28
-
29
- @status = compute_status
30
- self
31
- end
32
-
33
- def unmapped_fields
34
- @definition.expanded_field_names.reject { |f| has_field? f }
35
- end
36
-
37
- def has_field?(_name)
38
- @index.key? _name
39
- end
40
-
41
- def get_field_options(_name)
42
- @index[_name]
43
- end
44
-
45
- def migrate
46
- # TODO: make this a command
47
- @adaptor.create unless @adaptor.exists?
48
- begin
49
- @definition.types.each { |t| @adaptor.set_mapping(t, user_mapping) }
50
- rescue Elasticsearch::Transport::Transport::Errors::BadRequest
51
- # TODO: https://www.elastic.co/guide/en/elasticsearch/guide/current/reindex.html
52
- end
53
- fetch
54
- end
55
-
56
- private
57
-
58
- def compute_status
59
- if !synchronized?
60
- :out_of_sync
61
- elsif unmapped_fields.count > 0
62
- :incomplete
63
- else
64
- :ready
65
- end
66
- end
67
-
68
- def synchronized?
69
- return false if @index.nil?
70
- flatten(user_mapping).all? do |field, properties|
71
- compare_field_properties(@index[field], properties)
72
- end
73
- end
74
-
75
- def compare_field_properties(_current, _user)
76
- return false if _current.nil?
77
-
78
- case _current['type']
79
- when 'date'
80
- _current = { 'format' => 'dateOptionalTime' }.merge(_user)
81
- else
82
- _current == _user
83
- end
84
- end
85
-
86
- def user_mapping
87
- @user_mapping ||= definition.as_es_mapping
88
- end
89
-
90
- def flatten(_raw, _prefix = '')
91
- _raw['properties'].flat_map do |name, raw_field|
92
- if raw_field['type'] == 'nested'
93
- childs = flatten(raw_field, name + '.')
94
- childs << [
95
- _prefix + name,
96
- raw_field.slice(*(raw_field.keys - ['properties']))
97
- ]
98
- else
99
- [[_prefix + name, raw_field.dup]]
100
- end
101
- end
102
- end
103
-
104
- def merge_mappings_into_index(_mappings)
105
- {}.tap do |result|
106
- _mappings.each do |mapping|
107
- index = flatten(mapping)
108
- index.each do |field, properties|
109
- if result.key? field
110
- result[field].merge! properties
111
- else
112
- result[field] = properties
113
- end
114
- end
115
- end
116
- result.each_value(&:freeze)
117
- end
118
- end
119
- end
120
- end