elastic_record 4.0.0 → 4.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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -6
  3. data/Gemfile +0 -2
  4. data/README.md +25 -9
  5. data/elastic_record.gemspec +1 -1
  6. data/lib/elastic_record/as_document.rb +41 -0
  7. data/lib/elastic_record/callbacks.rb +11 -56
  8. data/lib/elastic_record/connection.rb +1 -1
  9. data/lib/elastic_record/doctype.rb +43 -0
  10. data/lib/elastic_record/index/deferred.rb +13 -15
  11. data/lib/elastic_record/index/documents.rb +23 -13
  12. data/lib/elastic_record/index/manage.rb +4 -6
  13. data/lib/elastic_record/index/mapping.rb +7 -26
  14. data/lib/elastic_record/index/settings.rb +17 -2
  15. data/lib/elastic_record/index.rb +12 -24
  16. data/lib/elastic_record/model.rb +14 -4
  17. data/lib/elastic_record/percolator_model.rb +44 -0
  18. data/lib/elastic_record/relation.rb +1 -2
  19. data/lib/elastic_record/searching.rb +5 -1
  20. data/lib/elastic_record/tasks/index.rake +0 -10
  21. data/lib/elastic_record.rb +3 -0
  22. data/test/dummy/app/models/mock_model.rb +108 -0
  23. data/test/dummy/app/models/project.rb +1 -1
  24. data/test/dummy/app/models/test_model.rb +1 -102
  25. data/test/dummy/app/models/test_percolator_model.rb +8 -0
  26. data/test/dummy/app/models/widget.rb +2 -5
  27. data/test/dummy/app/models/widget_query.rb +14 -0
  28. data/test/dummy/config/environments/test.rb +3 -3
  29. data/test/dummy/config/initializers/elastic_record.rb +1 -1
  30. data/test/elastic_record/as_document_test.rb +65 -0
  31. data/test/elastic_record/callbacks_test.rb +6 -81
  32. data/test/elastic_record/config_test.rb +1 -1
  33. data/test/elastic_record/doctype_test.rb +45 -0
  34. data/test/elastic_record/index/documents_test.rb +1 -1
  35. data/test/elastic_record/index/mapping_test.rb +16 -13
  36. data/test/elastic_record/index/settings_test.rb +34 -1
  37. data/test/elastic_record/index_test.rb +7 -15
  38. data/test/elastic_record/model_test.rb +1 -7
  39. data/test/elastic_record/percolator_model_test.rb +54 -0
  40. data/test/elastic_record/relation/batches_test.rb +0 -1
  41. data/test/elastic_record/relation/delegation_test.rb +0 -1
  42. data/test/elastic_record/relation/finder_methods_test.rb +0 -1
  43. data/test/elastic_record/relation_test.rb +0 -2
  44. data/test/elastic_record/searching_test.rb +7 -0
  45. metadata +11 -10
  46. data/lib/elastic_record/index/configurator.rb +0 -18
  47. data/lib/elastic_record/index/percolator.rb +0 -50
  48. data/lib/elastic_record/index/warmer.rb +0 -24
  49. data/lib/elastic_record/relation/admin.rb +0 -13
  50. data/test/elastic_record/index/configurator_test.rb +0 -18
  51. data/test/elastic_record/index/percolator_test.rb +0 -45
  52. data/test/elastic_record/index/warmer_test.rb +0 -20
  53. data/test/elastic_record/relation/admin_test.rb +0 -28
@@ -1,41 +1,22 @@
1
1
  module ElasticRecord
2
2
  class Index
3
3
  module Mapping
4
- def mapping=(custom_mapping)
5
- mapping.deep_merge!(custom_mapping)
6
- end
7
-
8
4
  def update_mapping(index_name = alias_name)
9
- connection.json_put "/#{index_name}/#{type}/_mapping", type => mapping
5
+ connection.json_put "/#{index_name}/_mapping", mapping_body
10
6
  end
11
7
 
12
8
  def get_mapping(index_name = alias_name)
13
- json = connection.json_get "/#{index_name}/#{type}/_mapping"
9
+ json = connection.json_get "/#{index_name}/_mapping"
10
+
14
11
  unless json.empty?
15
12
  json.values.first['mappings']
16
13
  end
17
14
  end
18
15
 
19
- def mapping
20
- @mapping ||= {
21
- properties: {
22
- },
23
- _all: {
24
- enabled: false
25
- },
26
- dynamic_templates: [
27
- {
28
- no_string_analyzing: {
29
- match: "*",
30
- match_mapping_type: "string",
31
- mapping: {
32
- type: "string",
33
- index: "not_analyzed"
34
- }
35
- }
36
- }
37
- ]
38
- }
16
+ def mapping_body
17
+ doctypes.each_with_object({}) do |doctype, result|
18
+ result[doctype.name] = doctype.mapping
19
+ end
39
20
  end
40
21
  end
41
22
  end
@@ -6,12 +6,27 @@ module ElasticRecord
6
6
  end
7
7
 
8
8
  def settings
9
- @settings ||= {}
9
+ @settings ||=
10
+ begin
11
+ result = {}
12
+
13
+ if (analysis = analysis_body).any?
14
+ result['analysis'] = analysis
15
+ end
16
+
17
+ result
18
+ end
10
19
  end
11
20
 
12
21
  def update_settings(index_name = alias_name)
13
22
  connection.json_put "/#{index_name}/_settings", settings
14
23
  end
24
+
25
+ def analysis_body
26
+ doctypes.each_with_object({}) do |doctype, result|
27
+ result.deep_merge!(doctype.analysis)
28
+ end
29
+ end
15
30
  end
16
31
  end
17
- end
32
+ end
@@ -1,12 +1,9 @@
1
1
  require 'elastic_record/index/analyze'
2
- require 'elastic_record/index/configurator'
3
2
  require 'elastic_record/index/deferred'
4
3
  require 'elastic_record/index/documents'
5
4
  require 'elastic_record/index/manage'
6
5
  require 'elastic_record/index/mapping'
7
- require 'elastic_record/index/percolator'
8
6
  require 'elastic_record/index/settings'
9
- require 'elastic_record/index/warmer'
10
7
 
11
8
  require 'active_support/core_ext/object/deep_dup'
12
9
 
@@ -26,30 +23,30 @@ module ElasticRecord
26
23
  # Returns if the index exists
27
24
  # [get_mapping]
28
25
  # Returns the mapping currently stored by elastic search.
29
- # [put_mapping]
26
+ # [update_mapping]
30
27
  # Update elastic search's mapping
31
28
  class Index
32
29
  include Documents
33
30
  include Manage
34
31
  include Mapping, Settings
35
- include Percolator, Warmer
36
32
  include Analyze
37
33
  include Deferred
38
34
 
39
- attr_accessor :model
35
+ attr_accessor :doctypes
40
36
 
41
37
  attr_accessor :disabled
42
- attr_accessor :has_percolator
38
+ attr_accessor :model
43
39
  attr_accessor :partial_updates
44
40
 
45
- def initialize(model)
46
- @model = model
41
+ def initialize(models)
42
+ models = Array.wrap(models)
43
+ @model = models.first
44
+ @doctypes = models.map(&:doctype)
47
45
  @disabled = false
48
46
  end
49
47
 
50
48
  def initialize_copy(other)
51
49
  @settings = settings.deep_dup
52
- @mapping = mapping.deep_dup
53
50
  end
54
51
 
55
52
  def alias_name=(name)
@@ -60,14 +57,6 @@ module ElasticRecord
60
57
  @alias_name ||= model.base_class.name.demodulize.underscore.pluralize
61
58
  end
62
59
 
63
- def type=(name)
64
- @type = name
65
- end
66
-
67
- def type
68
- @type ||= model.base_class.name.demodulize.underscore
69
- end
70
-
71
60
  def disable!
72
61
  @disabled = true
73
62
  end
@@ -80,12 +69,12 @@ module ElasticRecord
80
69
  model.elastic_connection
81
70
  end
82
71
 
83
- def configure(&block)
84
- Configurator.new(self).instance_eval(&block)
85
- end
72
+ def get(end_path, doctype = nil, json = nil)
73
+ path = "/#{alias_name}"
74
+ path += "/#{doctype.name}" if doctype
75
+ path += "/#{end_path}"
86
76
 
87
- def get(end_path, json = nil)
88
- connection.json_get "/#{alias_name}/#{type}/#{end_path}", json
77
+ connection.json_get path, json
89
78
  end
90
79
 
91
80
  private
@@ -93,6 +82,5 @@ module ElasticRecord
93
82
  def new_index_name
94
83
  "#{alias_name}_#{Time.now.utc.strftime('%Y%m%d_%H%M%S')}"
95
84
  end
96
-
97
85
  end
98
86
  end
@@ -5,6 +5,7 @@ module ElasticRecord
5
5
  extend Searching
6
6
  extend ClassMethods
7
7
  include Callbacks
8
+ include AsDocument
8
9
 
9
10
  class_attribute :elastic_connection
10
11
  self.elastic_connection = ElasticRecord::Connection.new(ElasticRecord::Config.servers, ElasticRecord::Config.connection_options)
@@ -17,17 +18,22 @@ module ElasticRecord
17
18
 
18
19
  if child < child.base_class
19
20
  child.elastic_index = elastic_index.dup
21
+ child.doctype = doctype.dup
20
22
  end
21
23
  end
22
24
 
23
- def elastic_relation
24
- ElasticRecord::Relation.new(self)
25
- end
26
-
27
25
  def arelastic
28
26
  Arelastic::Builders::Search
29
27
  end
30
28
 
29
+ def doctype
30
+ @doctype ||= Doctype.new(base_class.name.demodulize.underscore)
31
+ end
32
+
33
+ def doctype=(new_doctype)
34
+ @doctype = new_doctype
35
+ end
36
+
31
37
  def elastic_index
32
38
  @elastic_index ||= ElasticRecord::Index.new(self)
33
39
  end
@@ -44,5 +50,9 @@ module ElasticRecord
44
50
  def elastic_index
45
51
  self.class.elastic_index
46
52
  end
53
+
54
+ def doctype
55
+ self.class.doctype
56
+ end
47
57
  end
48
58
  end
@@ -0,0 +1,44 @@
1
+ module ElasticRecord
2
+ module PercolatorModel
3
+ def self.included(base)
4
+ base.class_eval do
5
+ class_attribute :percolates_model
6
+
7
+ include Model
8
+ extend ClassMethods
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+ def elastic_index
14
+ @elastic_index ||=
15
+ begin
16
+ index = ElasticRecord::Index.new([self, percolates_model])
17
+ index.partial_updates = false
18
+ index
19
+ end
20
+ end
21
+
22
+ def doctype
23
+ @doctype ||= Doctype.percolator_doctype
24
+ end
25
+
26
+ def percolate(document)
27
+ query = {
28
+ "query" => {
29
+ "percolate" => {
30
+ "field" => "query",
31
+ "document_type" => percolates_model.doctype.name,
32
+ "document" => document
33
+ }
34
+ }
35
+ }
36
+
37
+ hits = elastic_index.search(query)['hits']['hits']
38
+ ids = hits.map { |hits| hits['_id'] }
39
+
40
+ where(id: ids)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,5 +1,4 @@
1
1
  require 'elastic_record/relation/value_methods'
2
- require 'elastic_record/relation/admin'
3
2
  require 'elastic_record/relation/batches'
4
3
  require 'elastic_record/relation/delegation'
5
4
  require 'elastic_record/relation/finder_methods'
@@ -9,7 +8,7 @@ require 'elastic_record/relation/search_methods'
9
8
 
10
9
  module ElasticRecord
11
10
  class Relation
12
- include Admin, Batches, Delegation, FinderMethods, Merging, SearchMethods
11
+ include Batches, Delegation, FinderMethods, Merging, SearchMethods
13
12
 
14
13
  attr_reader :klass, :values
15
14
 
@@ -1,5 +1,9 @@
1
1
  module ElasticRecord
2
2
  module Searching
3
+ def elastic_relation
4
+ ElasticRecord::Relation.new(self)
5
+ end
6
+
3
7
  def elastic_search
4
8
  if current_elastic_search
5
9
  current_elastic_search.clone
@@ -27,4 +31,4 @@ module ElasticRecord
27
31
  Thread.current["#{self}_current_elastic_search"] = relation
28
32
  end
29
33
  end
30
- end
34
+ end
@@ -17,11 +17,6 @@ namespace :index do
17
17
  index = model.elastic_index
18
18
  index_name = index.create_and_deploy
19
19
  puts "Created #{model.name} index (#{index_name})"
20
-
21
- if index.has_percolator
22
- index_name = index.create_percolator_index
23
- puts "Created #{model.name} percolator index (#{index_name})"
24
- end
25
20
  end
26
21
  end
27
22
 
@@ -31,11 +26,6 @@ namespace :index do
31
26
  index = model.elastic_index
32
27
  index.delete_all
33
28
  puts "Dropped #{model.name} index"
34
-
35
- if index.has_percolator
36
- index.delete_percolator_index
37
- puts "Dropped #{model.name} percolator index (#{index.percolator_name})"
38
- end
39
29
  end
40
30
  end
41
31
 
@@ -3,13 +3,16 @@ require 'active_support/concern'
3
3
  require 'active_model'
4
4
 
5
5
  module ElasticRecord
6
+ autoload :AsDocument, 'elastic_record/as_document'
6
7
  autoload :Callbacks, 'elastic_record/callbacks'
7
8
  autoload :Config, 'elastic_record/config'
8
9
  autoload :Connection, 'elastic_record/connection'
10
+ autoload :Doctype, 'elastic_record/doctype'
9
11
  autoload :Index, 'elastic_record/index'
10
12
  autoload :JSON, 'elastic_record/json'
11
13
  autoload :Lucene, 'elastic_record/lucene'
12
14
  autoload :Model, 'elastic_record/model'
15
+ autoload :PercolatorModel, 'elastic_record/percolator_model'
13
16
  autoload :Relation, 'elastic_record/relation'
14
17
  autoload :Searching, 'elastic_record/searching'
15
18
 
@@ -0,0 +1,108 @@
1
+ # Essentially a fake ActiveRecord::Base
2
+ module MockModel
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ extend ActiveModel::Naming
7
+ extend ActiveModel::Callbacks
8
+ define_model_callbacks :save, :update, :create, :destroy
9
+
10
+ include ActiveModel::Dirty
11
+ include ActiveModel::Validations
12
+ end
13
+
14
+ module ClassMethods
15
+ def find(ids)
16
+ ids.map { |id| new(id: id) }
17
+ end
18
+
19
+ def where(query)
20
+ find query[:id]
21
+ end
22
+
23
+ def primary_key
24
+ 'id'
25
+ end
26
+
27
+ def base_class
28
+ self
29
+ end
30
+
31
+ def create(attributes = {})
32
+ record = new(attributes)
33
+ record.save
34
+ record
35
+ end
36
+
37
+ def define_attributes(attributes)
38
+ define_attribute_methods attributes
39
+
40
+ attributes.each do |attribute|
41
+ define_method attribute do
42
+ instance_variable_get("@#{attribute}")
43
+ end
44
+
45
+ define_method "#{attribute}=" do |value|
46
+ send("#{attribute}_will_change!")
47
+ instance_variable_set("@#{attribute}", value)
48
+ end
49
+ end
50
+
51
+ define_method 'attributes' do
52
+ Hash[attributes.map { |attr| [attr.to_s, send(attr)] }]
53
+ end
54
+ end
55
+ end
56
+
57
+ def initialize(attrs = {})
58
+ self.attributes = attrs
59
+ end
60
+
61
+ def attributes=(attrs)
62
+ attrs.each do |key, val|
63
+ send("#{key}=", val)
64
+ end
65
+ end
66
+
67
+ def id=(value)
68
+ @id = value
69
+ end
70
+
71
+ def id
72
+ @id ||= rand(10000).to_s
73
+ end
74
+
75
+ def save
76
+ @persisted = true
77
+ callback_method = new_record? ? :create : :update
78
+ run_callbacks callback_method
79
+ end
80
+
81
+ def destroy
82
+ @destroyed = true
83
+ run_callbacks :destroy
84
+ end
85
+
86
+ def ==(other)
87
+ id == other.id
88
+ end
89
+
90
+ def changed?
91
+ true
92
+ end
93
+
94
+ def new_record?
95
+ !@persisted
96
+ end
97
+
98
+ def persisted?
99
+ @persisted
100
+ end
101
+
102
+ def destroyed?
103
+ @destroyed
104
+ end
105
+
106
+ def reload
107
+ end
108
+ end
@@ -1,7 +1,7 @@
1
1
  class Project < ActiveRecord::Base
2
2
  include ElasticRecord::Model
3
3
 
4
- self.elastic_index.mapping[:properties].update(
4
+ self.doctype.mapping[:properties].update(
5
5
  'name' => { type: 'string', index: 'not_analyzed' }
6
6
  )
7
7
  end