elastic_record 0.8.2 → 0.9.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.
- data/elastic_record.gemspec +1 -1
- data/lib/elastic_record/index/deferred.rb +81 -0
- data/lib/elastic_record/index.rb +6 -4
- data/lib/elastic_record/model.rb +3 -1
- data/lib/elastic_record/relation/merging.rb +6 -5
- data/lib/elastic_record/relation/search_methods.rb +8 -8
- data/lib/elastic_record/searches_many/association.rb +134 -0
- data/lib/elastic_record/searches_many/autosave.rb +72 -0
- data/lib/elastic_record/searches_many/builder.rb +42 -0
- data/lib/elastic_record/searches_many/collection_proxy.rb +20 -0
- data/lib/elastic_record/searches_many/reflection.rb +41 -0
- data/lib/elastic_record/searches_many.rb +91 -0
- data/lib/elastic_record.rb +3 -1
- data/test/elastic_record/callbacks_test.rb +2 -7
- data/test/elastic_record/config_test.rb +1 -1
- data/test/elastic_record/index/documents_test.rb +1 -1
- data/test/elastic_record/index/manage_test.rb +5 -1
- data/test/elastic_record/index/percolator_test.rb +5 -0
- data/test/elastic_record/relation/batches_test.rb +1 -3
- data/test/elastic_record/relation/delegation_test.rb +0 -5
- data/test/elastic_record/relation/finder_methods_test.rb +2 -4
- data/test/elastic_record/relation/none_test.rb +0 -5
- data/test/elastic_record/relation/search_methods_test.rb +19 -0
- data/test/elastic_record/relation_test.rb +0 -7
- data/test/elastic_record/searches_many/autosave_test.rb +31 -0
- data/test/elastic_record/searches_many/collection_proxy_test.rb +23 -0
- data/test/elastic_record/searches_many/reflection_test.rb +20 -0
- data/test/elastic_record/searches_many_test.rb +65 -0
- data/test/helper.rb +16 -1
- data/test/support/connect.rb +1 -1
- data/test/support/models/test_model.rb +98 -0
- data/test/support/models/warehouse.rb +6 -0
- data/test/support/{widget.rb → models/widget.rb} +10 -19
- metadata +16 -3
data/elastic_record.gemspec
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module ElasticRecord
|
|
2
|
+
class Index
|
|
3
|
+
module Deferred
|
|
4
|
+
class DeferredConnection
|
|
5
|
+
class DeferredAction < Struct.new(:method, :args, :block)
|
|
6
|
+
def run(index)
|
|
7
|
+
index.send(method, *args, &block)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
attr_accessor :index
|
|
12
|
+
attr_accessor :deferred_actions
|
|
13
|
+
attr_accessor :writes_made
|
|
14
|
+
|
|
15
|
+
def initialize(index)
|
|
16
|
+
self.index = index
|
|
17
|
+
reset!
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def reset!
|
|
21
|
+
if writes_made
|
|
22
|
+
begin
|
|
23
|
+
index.disable_deferring!
|
|
24
|
+
index.reset
|
|
25
|
+
ensure
|
|
26
|
+
index.enable_deferring!
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
self.deferred_actions = []
|
|
30
|
+
self.writes_made = false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def flush!
|
|
34
|
+
deferred_actions.each do |queued_action|
|
|
35
|
+
self.writes_made = true
|
|
36
|
+
queued_action.run(index.real_connection)
|
|
37
|
+
end
|
|
38
|
+
deferred_actions.clear
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
READ_METHODS = [:json_get, :head]
|
|
43
|
+
def method_missing(method, *args, &block)
|
|
44
|
+
super unless index.real_connection.respond_to?(method)
|
|
45
|
+
|
|
46
|
+
if READ_METHODS.include?(method)
|
|
47
|
+
flush!
|
|
48
|
+
index.real_connection.json_post "/#{index.alias_name}/_refresh"
|
|
49
|
+
index.real_connection.send(method, *args, &block)
|
|
50
|
+
else
|
|
51
|
+
deferred_actions << DeferredAction.new(method, args, block)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def enable_deferring!
|
|
57
|
+
@deferring_enabled = true
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def disable_deferring!
|
|
61
|
+
@deferring_enabled = false
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def connection
|
|
65
|
+
if @deferring_enabled
|
|
66
|
+
deferred_connection
|
|
67
|
+
else
|
|
68
|
+
real_connection
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def reset_deferring!
|
|
73
|
+
deferred_connection.reset!
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def deferred_connection
|
|
77
|
+
@deferred_connection ||= DeferredConnection.new(self)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
data/lib/elastic_record/index.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require 'elastic_record/index/deferred'
|
|
1
2
|
require 'elastic_record/index/documents'
|
|
2
3
|
require 'elastic_record/index/manage'
|
|
3
4
|
require 'elastic_record/index/mapping'
|
|
@@ -10,6 +11,7 @@ module ElasticRecord
|
|
|
10
11
|
include Manage
|
|
11
12
|
include Mapping
|
|
12
13
|
include Percolator
|
|
14
|
+
include Deferred
|
|
13
15
|
|
|
14
16
|
attr_accessor :model
|
|
15
17
|
attr_accessor :disabled
|
|
@@ -35,13 +37,13 @@ module ElasticRecord
|
|
|
35
37
|
@disabled = false
|
|
36
38
|
end
|
|
37
39
|
|
|
40
|
+
def real_connection
|
|
41
|
+
model.elastic_connection
|
|
42
|
+
end
|
|
43
|
+
|
|
38
44
|
private
|
|
39
45
|
def new_index_name
|
|
40
46
|
"#{alias_name}_#{Time.now.to_i}"
|
|
41
47
|
end
|
|
42
|
-
|
|
43
|
-
def connection
|
|
44
|
-
model.elastic_connection
|
|
45
|
-
end
|
|
46
48
|
end
|
|
47
49
|
end
|
data/lib/elastic_record/model.rb
CHANGED
|
@@ -18,16 +18,17 @@ module ElasticRecord
|
|
|
18
18
|
@values = other.values
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def normal_values
|
|
22
|
-
Relation::MULTI_VALUE_METHODS + Relation::SINGLE_VALUE_METHODS
|
|
23
|
-
end
|
|
24
|
-
|
|
25
21
|
def merge
|
|
26
|
-
|
|
22
|
+
Relation::SINGLE_VALUE_METHODS.each do |name|
|
|
27
23
|
value = values[name]
|
|
28
24
|
relation.send("#{name}!", value) unless value.blank?
|
|
29
25
|
end
|
|
30
26
|
|
|
27
|
+
Relation::MULTI_VALUE_METHODS.each do |name|
|
|
28
|
+
value = values[name]
|
|
29
|
+
relation.send("#{name}!", *value) unless value.blank?
|
|
30
|
+
end
|
|
31
|
+
|
|
31
32
|
relation
|
|
32
33
|
end
|
|
33
34
|
end
|
|
@@ -35,7 +35,7 @@ module ElasticRecord
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def filter!(*args)
|
|
38
|
-
self.filter_values += args
|
|
38
|
+
self.filter_values += args
|
|
39
39
|
self
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -137,9 +137,9 @@ module ElasticRecord
|
|
|
137
137
|
if query && filter
|
|
138
138
|
arelastic.query.filtered(query, filter)
|
|
139
139
|
elsif query
|
|
140
|
-
query
|
|
140
|
+
Arelastic::Searches::Query.new(query)
|
|
141
141
|
elsif filter
|
|
142
|
-
arelastic.query.constant_score(filter)
|
|
142
|
+
arelastic.query.constant_score(Arelastic::Searches::Filter.new(filter))
|
|
143
143
|
else
|
|
144
144
|
arelastic.query.match_all
|
|
145
145
|
end
|
|
@@ -150,9 +150,7 @@ module ElasticRecord
|
|
|
150
150
|
query = Arelastic::Queries::QueryString.new query
|
|
151
151
|
end
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
Arelastic::Searches::Query.new query
|
|
155
|
-
end
|
|
153
|
+
query
|
|
156
154
|
end
|
|
157
155
|
|
|
158
156
|
def build_filter(filters)
|
|
@@ -161,6 +159,8 @@ module ElasticRecord
|
|
|
161
159
|
filters.map do |filter|
|
|
162
160
|
if filter.is_a?(Arelastic::Filters::Filter)
|
|
163
161
|
nodes << filter
|
|
162
|
+
elsif filter.is_a?(ElasticRecord::Relation)
|
|
163
|
+
nodes << Arelastic::Filters::HasChild.new(filter.elastic_index.type, filter.as_elastic['query'])
|
|
164
164
|
else
|
|
165
165
|
filter.each do |field, terms|
|
|
166
166
|
case terms
|
|
@@ -176,9 +176,9 @@ module ElasticRecord
|
|
|
176
176
|
end
|
|
177
177
|
|
|
178
178
|
if nodes.size == 1
|
|
179
|
-
|
|
179
|
+
nodes.first
|
|
180
180
|
elsif nodes.size > 1
|
|
181
|
-
Arelastic::
|
|
181
|
+
Arelastic::Filters::And.new(nodes)
|
|
182
182
|
end
|
|
183
183
|
end
|
|
184
184
|
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
|
2
|
+
|
|
3
|
+
module ElasticRecord
|
|
4
|
+
module SearchesMany
|
|
5
|
+
class Association
|
|
6
|
+
attr_reader :owner, :reflection, :collection
|
|
7
|
+
|
|
8
|
+
delegate :klass, :options, to: :reflection
|
|
9
|
+
|
|
10
|
+
def initialize(owner, reflection)
|
|
11
|
+
@owner = owner
|
|
12
|
+
@reflection = reflection
|
|
13
|
+
@collection = []
|
|
14
|
+
@loaded = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def writer(other_records)
|
|
18
|
+
other_records = other_records.map do |other_record|
|
|
19
|
+
other_record.is_a?(Hash) ? klass.new(other_record) : other_record
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if reflection.counter_cache_column
|
|
23
|
+
owner.send("#{reflection.counter_cache_column}=", other_records.size)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if reflection.touch_column
|
|
27
|
+
owner.send("#{reflection.touch_column}=", Time.current)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
delete(load_collection - other_records)
|
|
31
|
+
merge_collections(load_collection, other_records)
|
|
32
|
+
concat(other_records - load_collection)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def reader
|
|
36
|
+
CollectionProxy.new(self)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def loaded?
|
|
40
|
+
@loaded
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def concat(*records)
|
|
44
|
+
load_collection if owner.new_record?
|
|
45
|
+
|
|
46
|
+
result = true
|
|
47
|
+
|
|
48
|
+
records.flatten.each do |record|
|
|
49
|
+
add_to_collection(record) do |r|
|
|
50
|
+
result &&= record.save unless owner.new_record?
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
result && records
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def delete(records)
|
|
58
|
+
if options[:autosave] || owner.new_record?
|
|
59
|
+
records.each(&:mark_for_destruction)
|
|
60
|
+
else
|
|
61
|
+
record.destroy
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def scope
|
|
66
|
+
search = klass.elastic_search.filter "#{reflection.belongs_to}_id" => owner.id
|
|
67
|
+
if options[:as]
|
|
68
|
+
search.filter! "#{reflection.belongs_to}_type" => owner.class.name
|
|
69
|
+
end
|
|
70
|
+
search
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def load_collection
|
|
74
|
+
unless @loaded
|
|
75
|
+
@collection = merge_collections(persisted_collection, collection)
|
|
76
|
+
@loaded = true
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
loaded = true
|
|
80
|
+
collection
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
def load_persisted_collection?
|
|
85
|
+
!loaded? || owner.new_record?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def persisted_collection
|
|
89
|
+
scope.to_a
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def merge_collections(existing_records, new_records)
|
|
93
|
+
return existing_records if new_records.empty?
|
|
94
|
+
return new_records if existing_records.empty?
|
|
95
|
+
|
|
96
|
+
existing_records.map! do |existing_record|
|
|
97
|
+
if new_record = new_records.delete(existing_record)
|
|
98
|
+
(existing_record.attributes.keys - new_record.changes.keys).each do |name|
|
|
99
|
+
new_record.send("#{name}=", existing_record.send(name))
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
new_record
|
|
103
|
+
else
|
|
104
|
+
existing_record
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
existing_records + new_records
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def add_to_collection(record)
|
|
112
|
+
callback(:before_add, record)
|
|
113
|
+
|
|
114
|
+
record.send("#{reflection.belongs_to}=", owner)
|
|
115
|
+
yield(record) if block_given?
|
|
116
|
+
@collection << record
|
|
117
|
+
|
|
118
|
+
callback(:after_add, record)
|
|
119
|
+
|
|
120
|
+
record
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def callback(method, record)
|
|
124
|
+
reflection.callbacks[method].each do |callback|
|
|
125
|
+
if callback.is_a?(Symbol)
|
|
126
|
+
owner.send(callback, record)
|
|
127
|
+
else
|
|
128
|
+
callback.call(owner, record)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module ElasticRecord
|
|
2
|
+
module SearchesMany
|
|
3
|
+
module Autosave
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
module ClassMethods
|
|
7
|
+
def add_autosave_callbacks(reflection)
|
|
8
|
+
add_autosave_after_save_callbacks(reflection)
|
|
9
|
+
add_autosave_validation_callbacks(reflection)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_autosave_after_save_callbacks(reflection)
|
|
13
|
+
before_save { save_autosave_records(reflection) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def add_autosave_validation_callbacks(reflection)
|
|
17
|
+
validate { validate_autosave_records(reflection) }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def save_autosave_records(reflection)
|
|
23
|
+
if association = searches_many_instance_get(reflection.name)
|
|
24
|
+
associated_records_to_autosave(association).each do |record|
|
|
25
|
+
if record.marked_for_destruction?
|
|
26
|
+
record.destroy
|
|
27
|
+
elsif record.changed?
|
|
28
|
+
record.save
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def validate_autosave_records(reflection)
|
|
35
|
+
if association = searches_many_instance_get(reflection.name)
|
|
36
|
+
associated_records_to_autosave(association).each do |record|
|
|
37
|
+
unless record.valid?
|
|
38
|
+
record.errors.each do |attribute, message|
|
|
39
|
+
attribute = "#{reflection.name}.#{attribute}"
|
|
40
|
+
errors[attribute] << message
|
|
41
|
+
errors[attribute].uniq!
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def associated_records_to_autosave(association)
|
|
49
|
+
if association.loaded?
|
|
50
|
+
association.load_collection
|
|
51
|
+
else
|
|
52
|
+
[]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Marks this record to be destroyed as part of the parents save transaction.
|
|
57
|
+
# This does _not_ actually destroy the record instantly, rather child record will be destroyed
|
|
58
|
+
# when <tt>parent.save</tt> is called.
|
|
59
|
+
#
|
|
60
|
+
# Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
|
|
61
|
+
def mark_for_destruction
|
|
62
|
+
@marked_for_destruction = true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns whether or not this record will be destroyed as part of the parents save transaction.
|
|
66
|
+
#
|
|
67
|
+
# Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
|
|
68
|
+
def marked_for_destruction?
|
|
69
|
+
@marked_for_destruction
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module ElasticRecord
|
|
2
|
+
module SearchesMany
|
|
3
|
+
class Builder
|
|
4
|
+
def self.build(model, name, options)
|
|
5
|
+
new(model, name, options).build
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
attr_reader :model, :name, :options
|
|
9
|
+
def initialize(model, name, options)
|
|
10
|
+
@model, @name, @options = model, name, options
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def build
|
|
14
|
+
define_writer
|
|
15
|
+
define_reader
|
|
16
|
+
|
|
17
|
+
reflection = ElasticRecord::SearchesMany::Reflection.new(model, name, options)
|
|
18
|
+
model.searches_many_reflections = model.searches_many_reflections.merge(name => reflection)
|
|
19
|
+
|
|
20
|
+
model.add_autosave_callbacks(reflection) # if options[:autosave]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def mixin
|
|
24
|
+
model.generated_searches_many_methods
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def define_writer
|
|
28
|
+
name = self.name
|
|
29
|
+
mixin.redefine_method("#{name}=") do |records|
|
|
30
|
+
searches_many_association(name).writer(records)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def define_reader
|
|
35
|
+
name = self.name
|
|
36
|
+
mixin.redefine_method(name) do
|
|
37
|
+
searches_many_association(name).reader
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module ElasticRecord
|
|
2
|
+
module SearchesMany
|
|
3
|
+
class CollectionProxy < ElasticRecord::Relation
|
|
4
|
+
def initialize(association)
|
|
5
|
+
@association = association
|
|
6
|
+
super association.klass, association.klass.arelastic
|
|
7
|
+
merge! association.scope
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def to_a
|
|
11
|
+
@association.load_collection.reject(&:destroyed?)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def <<(*records)
|
|
15
|
+
@association.concat(records) && self
|
|
16
|
+
end
|
|
17
|
+
alias_method :push, :<<
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module ElasticRecord
|
|
2
|
+
module SearchesMany
|
|
3
|
+
class Reflection
|
|
4
|
+
attr_reader :model, :name, :options
|
|
5
|
+
attr_reader :callbacks
|
|
6
|
+
def initialize(model, name, options)
|
|
7
|
+
@model, @name, @options = model, name, options
|
|
8
|
+
@callbacks = define_callbacks(options)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def klass
|
|
12
|
+
klass_name.constantize
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def klass_name
|
|
16
|
+
name.to_s.classify
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def belongs_to
|
|
20
|
+
options[:as] ? options[:as].to_s : model.name.to_s.demodulize.underscore
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
|
|
24
|
+
def define_callbacks(options)
|
|
25
|
+
Hash[CALLBACKS.map { |callback_name| [callback_name, Array(options[callback_name.to_sym])] }]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def touch_column
|
|
29
|
+
if options[:touch]
|
|
30
|
+
options[:touch] == true ? :updated_at : options[:touch].to_sym
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def counter_cache_column
|
|
35
|
+
if options[:counter_cache]
|
|
36
|
+
(options[:counter_cache] == true ? "#{name}_count" : options[:counter_cache]).to_sym
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require 'elastic_record/searches_many/association'
|
|
2
|
+
require 'elastic_record/searches_many/autosave'
|
|
3
|
+
require 'elastic_record/searches_many/builder'
|
|
4
|
+
require 'elastic_record/searches_many/collection_proxy'
|
|
5
|
+
require 'elastic_record/searches_many/reflection'
|
|
6
|
+
|
|
7
|
+
module ElasticRecord
|
|
8
|
+
module SearchesMany
|
|
9
|
+
def self.included(base)
|
|
10
|
+
base.class_eval do
|
|
11
|
+
extend ClassMethods
|
|
12
|
+
|
|
13
|
+
class_attribute :searches_many_reflections
|
|
14
|
+
self.searches_many_reflections = {}
|
|
15
|
+
|
|
16
|
+
include ElasticRecord::SearchesMany::Autosave
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module ClassMethods
|
|
21
|
+
# Specifies a one-to-many association. The following methods for retrieval and query of
|
|
22
|
+
# collections of associated objects will be added:
|
|
23
|
+
#
|
|
24
|
+
# [collection]
|
|
25
|
+
# Returns an array of all the associated objects.
|
|
26
|
+
# [collection=objects]
|
|
27
|
+
# Replaces the collections content by deleting and adding objects as appropriate.
|
|
28
|
+
# [collection_params=objects]
|
|
29
|
+
# Support for nested assignment from a form
|
|
30
|
+
# === Options
|
|
31
|
+
# [:as]
|
|
32
|
+
# Specifies a polymorphic interface (See <tt>belongs_to</tt>).
|
|
33
|
+
# [:touch]
|
|
34
|
+
# Specify to update the owner when changed. Specify <tt>true</tt>
|
|
35
|
+
# to update the updated_at field. If you specify a symbol, that attribute
|
|
36
|
+
# will be updated with the current time in addition to the updated_at/on attribute.
|
|
37
|
+
# [:autosave]
|
|
38
|
+
# If true, always save the associated objects or destroy them if marked for destruction, when
|
|
39
|
+
# saving the parent object.
|
|
40
|
+
# [:counter_cache]
|
|
41
|
+
# Caches the number of belonging objects on the associate class. This requires that a column
|
|
42
|
+
# named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
|
|
43
|
+
# is used on the associate class (such as a Post class). You can also specify a custom counter
|
|
44
|
+
# cache column by providing a column name instead of a +true+/+false+ value to this
|
|
45
|
+
# option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
|
|
46
|
+
#
|
|
47
|
+
# === Example
|
|
48
|
+
#
|
|
49
|
+
# Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
|
|
50
|
+
# * <tt>Firm#clients</tt>
|
|
51
|
+
# * <tt>Firm#clients=(objects)</tt>
|
|
52
|
+
# * <tt>Firm#client_params=(params)</tt>
|
|
53
|
+
def searches_many(name, options = {})
|
|
54
|
+
ElasticRecord::SearchesMany::Builder.build(self, name, options)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def generated_searches_many_methods
|
|
58
|
+
@generated_searches_many_methods ||= begin
|
|
59
|
+
mod = const_set(:GeneratedSearchesManyMethods, Module.new)
|
|
60
|
+
include mod
|
|
61
|
+
mod
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns the searches_many instance for the given name, instantiating it if it doesn't already exist
|
|
67
|
+
def searches_many_association(name)
|
|
68
|
+
association = searches_many_instance_get(name)
|
|
69
|
+
|
|
70
|
+
if association.nil?
|
|
71
|
+
association = ElasticRecord::SearchesMany::Association.new(self, searches_many_reflections[name])
|
|
72
|
+
searches_many_instance_set(name, association)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
association
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
def searches_many_cache
|
|
80
|
+
@searches_many_cache ||= {}
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def searches_many_instance_get(name)
|
|
84
|
+
searches_many_cache[name.to_sym]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def searches_many_instance_set(name, association)
|
|
88
|
+
searches_many_cache[name.to_sym] = association
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/lib/elastic_record.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'arelastic'
|
|
2
2
|
require 'active_support/core_ext/object/blank' # required because ActiveModel depends on this but does not require it
|
|
3
|
+
require 'active_support/concern'
|
|
3
4
|
require 'active_model'
|
|
4
5
|
|
|
5
6
|
module ElasticRecord
|
|
@@ -10,6 +11,7 @@ module ElasticRecord
|
|
|
10
11
|
autoload :Lucene, 'elastic_record/lucene'
|
|
11
12
|
autoload :Model, 'elastic_record/model'
|
|
12
13
|
autoload :Relation, 'elastic_record/relation'
|
|
14
|
+
autoload :SearchesMany, 'elastic_record/searches_many'
|
|
13
15
|
autoload :Searching, 'elastic_record/searching'
|
|
14
16
|
|
|
15
17
|
class << self
|
|
@@ -19,4 +21,4 @@ module ElasticRecord
|
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
require 'elastic_record/railtie' if defined?(Rails)
|
|
24
|
+
require 'elastic_record/railtie' if defined?(Rails)
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
require 'helper'
|
|
2
2
|
|
|
3
3
|
class ElasticRecord::CallbacksTest < MiniTest::Spec
|
|
4
|
-
def setup
|
|
5
|
-
super
|
|
6
|
-
Widget.elastic_index.reset
|
|
7
|
-
end
|
|
8
|
-
|
|
9
4
|
def test_added_to_index
|
|
10
5
|
widget = Widget.new id: '10', color: 'green'
|
|
11
6
|
refute Widget.elastic_index.record_exists?(widget.id)
|
|
12
7
|
|
|
13
|
-
widget.
|
|
8
|
+
widget.save
|
|
14
9
|
|
|
15
10
|
assert Widget.elastic_index.record_exists?(widget.id)
|
|
16
11
|
end
|
|
@@ -21,7 +16,7 @@ class ElasticRecord::CallbacksTest < MiniTest::Spec
|
|
|
21
16
|
|
|
22
17
|
assert Widget.elastic_index.record_exists?(widget.id)
|
|
23
18
|
|
|
24
|
-
widget.
|
|
19
|
+
widget.destroy
|
|
25
20
|
|
|
26
21
|
refute Widget.elastic_index.record_exists?(widget.id)
|
|
27
22
|
end
|
|
@@ -2,7 +2,7 @@ require 'helper'
|
|
|
2
2
|
|
|
3
3
|
class ElasticRecord::Relation::BatchesTest < MiniTest::Spec
|
|
4
4
|
def setup
|
|
5
|
-
|
|
5
|
+
super
|
|
6
6
|
create_widgets
|
|
7
7
|
end
|
|
8
8
|
|
|
@@ -37,7 +37,5 @@ class ElasticRecord::Relation::BatchesTest < MiniTest::Spec
|
|
|
37
37
|
Widget.new(id: 10, color: 'blue'),
|
|
38
38
|
Widget.new(id: 15, color: 'green'),
|
|
39
39
|
]
|
|
40
|
-
|
|
41
|
-
Widget.elastic_index.refresh
|
|
42
40
|
end
|
|
43
41
|
end
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
require 'helper'
|
|
2
2
|
|
|
3
3
|
class ElasticRecord::Relation::DelegationTest < MiniTest::Spec
|
|
4
|
-
def setup
|
|
5
|
-
Widget.elastic_index.reset
|
|
6
|
-
end
|
|
7
|
-
|
|
8
4
|
def test_delegate_to_array
|
|
9
5
|
Widget.elastic_index.index_document('5', color: 'red')
|
|
10
|
-
Widget.elastic_index.refresh
|
|
11
6
|
|
|
12
7
|
records = []
|
|
13
8
|
Widget.elastic_relation.each do |record|
|
|
@@ -2,7 +2,7 @@ require 'helper'
|
|
|
2
2
|
|
|
3
3
|
class ElasticRecord::Relation::FinderMethodsTest < MiniTest::Spec
|
|
4
4
|
def setup
|
|
5
|
-
|
|
5
|
+
super
|
|
6
6
|
create_widgets
|
|
7
7
|
end
|
|
8
8
|
|
|
@@ -37,8 +37,6 @@ class ElasticRecord::Relation::FinderMethodsTest < MiniTest::Spec
|
|
|
37
37
|
Widget.elastic_index.bulk_add [
|
|
38
38
|
Widget.new(color: 'red', id: '05'),
|
|
39
39
|
Widget.new(color: 'blue', id: '10'),
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
Widget.elastic_index.refresh
|
|
40
|
+
]
|
|
43
41
|
end
|
|
44
42
|
end
|
|
@@ -57,6 +57,25 @@ class ElasticRecord::Relation::SearchMethodsTest < MiniTest::Spec
|
|
|
57
57
|
assert_equal expected, relation.as_elastic['query']
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
def test_filter_with_another_relation
|
|
61
|
+
relation.filter! Widget.elastic_search.query('red')
|
|
62
|
+
|
|
63
|
+
expected = {
|
|
64
|
+
"constant_score" => {
|
|
65
|
+
"filter" => {
|
|
66
|
+
"has_child" => {
|
|
67
|
+
"type" => "widget",
|
|
68
|
+
"query" => {
|
|
69
|
+
"query_string" => {"query"=>"red"}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
assert_equal expected, relation.as_elastic['query']
|
|
77
|
+
end
|
|
78
|
+
|
|
60
79
|
def test_query_with_only_query
|
|
61
80
|
relation.query!('foo')
|
|
62
81
|
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
require 'helper'
|
|
2
2
|
|
|
3
3
|
class ElasticRecord::RelationTest < MiniTest::Spec
|
|
4
|
-
def setup
|
|
5
|
-
super
|
|
6
|
-
Widget.elastic_index.reset
|
|
7
|
-
end
|
|
8
|
-
|
|
9
4
|
def test_count
|
|
10
5
|
create_widgets [Widget.new(id: 5, color: 'red'), Widget.new(id: 10, color: 'blue')]
|
|
11
6
|
|
|
@@ -32,7 +27,6 @@ class ElasticRecord::RelationTest < MiniTest::Spec
|
|
|
32
27
|
create_widgets [Widget.new(id: 10, color: 'blue')]
|
|
33
28
|
|
|
34
29
|
# explain = Widget.elastic_relation.filter(color: 'blue').explain('10')
|
|
35
|
-
# p "explain = #{explain}"
|
|
36
30
|
end
|
|
37
31
|
|
|
38
32
|
def test_to_hits
|
|
@@ -70,6 +64,5 @@ class ElasticRecord::RelationTest < MiniTest::Spec
|
|
|
70
64
|
private
|
|
71
65
|
def create_widgets(widgets)
|
|
72
66
|
Widget.elastic_index.bulk_add(widgets)
|
|
73
|
-
Widget.elastic_index.refresh
|
|
74
67
|
end
|
|
75
68
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'helper'
|
|
2
|
+
|
|
3
|
+
class ElasticRecord::SearchesMany::AutosaveTest < MiniTest::Spec
|
|
4
|
+
def test_save_associations_callback
|
|
5
|
+
warehouse = Warehouse.new
|
|
6
|
+
widget = Widget.new
|
|
7
|
+
warehouse.widgets = [widget]
|
|
8
|
+
assert widget.new_record?
|
|
9
|
+
|
|
10
|
+
warehouse.save
|
|
11
|
+
|
|
12
|
+
assert widget.persisted?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def test_validate_associations_callback
|
|
16
|
+
warehouse = Warehouse.new
|
|
17
|
+
widget = Widget.new color: 123
|
|
18
|
+
warehouse.widgets = [widget]
|
|
19
|
+
|
|
20
|
+
assert warehouse.invalid?
|
|
21
|
+
assert_equal ["is invalid"], warehouse.errors['widgets.color']
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_mark_for_destruction
|
|
25
|
+
widget = Widget.new
|
|
26
|
+
|
|
27
|
+
widget.mark_for_destruction
|
|
28
|
+
|
|
29
|
+
assert widget.marked_for_destruction?
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'helper'
|
|
2
|
+
|
|
3
|
+
class ElasticRecord::SearchesMany::CollectionProxyTest < MiniTest::Spec
|
|
4
|
+
def test_add_to_new_record
|
|
5
|
+
warehouse = Warehouse.new
|
|
6
|
+
widget = Widget.new
|
|
7
|
+
|
|
8
|
+
warehouse.widgets << widget
|
|
9
|
+
|
|
10
|
+
assert widget.new_record?
|
|
11
|
+
assert_equal [widget], warehouse.widgets
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_add_to_persisted_record
|
|
15
|
+
warehouse = Warehouse.create
|
|
16
|
+
widget = Widget.new
|
|
17
|
+
|
|
18
|
+
warehouse.widgets << widget
|
|
19
|
+
|
|
20
|
+
assert !widget.new_record?
|
|
21
|
+
assert_equal [widget], warehouse.widgets
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'helper'
|
|
2
|
+
|
|
3
|
+
class ElasticRecord::SearchesMany::ReflectionTest < MiniTest::Spec
|
|
4
|
+
def test_touch_column
|
|
5
|
+
assert_nil reflection_class.new(Warehouse, :widgets, {}).touch_column
|
|
6
|
+
assert_equal :updated_at, reflection_class.new(Warehouse, :widgets, touch: true).touch_column
|
|
7
|
+
assert_equal :my_column, reflection_class.new(Warehouse, :widgets, touch: :my_column).touch_column
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_counter_cache_column
|
|
11
|
+
assert_nil reflection_class.new(Warehouse, :widgets, {}).counter_cache_column
|
|
12
|
+
assert_equal :widgets_count, reflection_class.new(Warehouse, :widgets, counter_cache: true).counter_cache_column
|
|
13
|
+
assert_equal :my_column, reflection_class.new(Warehouse, :widgets, counter_cache: :my_column).counter_cache_column
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
def reflection_class
|
|
18
|
+
ElasticRecord::SearchesMany::Reflection
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'helper'
|
|
2
|
+
|
|
3
|
+
class ElasticRecord::SearchesManyTest < MiniTest::Spec
|
|
4
|
+
def test_reader
|
|
5
|
+
warehouse = Warehouse.create
|
|
6
|
+
related_widget = Widget.create warehouse: warehouse
|
|
7
|
+
unrelated_widget = Widget.create
|
|
8
|
+
|
|
9
|
+
assert_equal [related_widget], warehouse.widgets
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_write_with_objects
|
|
13
|
+
warehouse = Warehouse.new
|
|
14
|
+
widget = Widget.new
|
|
15
|
+
|
|
16
|
+
warehouse.widgets = [widget]
|
|
17
|
+
|
|
18
|
+
assert widget.new_record?
|
|
19
|
+
assert_equal warehouse.id, widget.warehouse_id
|
|
20
|
+
# assert_equal 1, warehouse.widgets_count
|
|
21
|
+
# assert_in_delta Time.current, warehouse.widgets_updated_at, 5
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_write_with_attributes
|
|
25
|
+
warehouse = Warehouse.new
|
|
26
|
+
|
|
27
|
+
warehouse.widgets = [
|
|
28
|
+
{
|
|
29
|
+
color: 'blue',
|
|
30
|
+
name: 'Toy'
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
widgets = warehouse.widgets
|
|
35
|
+
assert_equal 1, widgets.size
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_write_marks_destroyed
|
|
39
|
+
warehouse = Warehouse.new
|
|
40
|
+
widget = Widget.create warehouse: warehouse
|
|
41
|
+
|
|
42
|
+
warehouse.widgets = []
|
|
43
|
+
|
|
44
|
+
association = warehouse.searches_many_association(:widgets)
|
|
45
|
+
assert_equal 1, association.reader.size
|
|
46
|
+
assert association.reader.first.marked_for_destruction?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_write_existing_record
|
|
50
|
+
widget = Widget.create name: 'Toy', color: 'green'
|
|
51
|
+
warehouse = Warehouse.new widgets: [widget]
|
|
52
|
+
|
|
53
|
+
warehouse.widgets = [
|
|
54
|
+
{
|
|
55
|
+
id: widget.id,
|
|
56
|
+
color: "blue"
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
widgets = warehouse.widgets
|
|
61
|
+
assert_equal 1, widgets.size
|
|
62
|
+
assert_equal "blue", widgets.first.color
|
|
63
|
+
assert_equal "Toy", widgets.first.name
|
|
64
|
+
end
|
|
65
|
+
end
|
data/test/helper.rb
CHANGED
|
@@ -3,8 +3,13 @@ Bundler.require
|
|
|
3
3
|
|
|
4
4
|
require 'minitest/autorun'
|
|
5
5
|
|
|
6
|
-
require 'support/widget'
|
|
7
6
|
require 'support/connect'
|
|
7
|
+
require 'support/models/test_model'
|
|
8
|
+
require 'support/models/warehouse'
|
|
9
|
+
require 'support/models/widget'
|
|
10
|
+
Widget.elastic_index.reset
|
|
11
|
+
|
|
12
|
+
ElasticRecord::Config.model_names = %w(Warehouse Widget)
|
|
8
13
|
|
|
9
14
|
FakeWeb.allow_net_connect = %r[^https?://127.0.0.1]
|
|
10
15
|
|
|
@@ -13,6 +18,16 @@ module MiniTest
|
|
|
13
18
|
def setup
|
|
14
19
|
super
|
|
15
20
|
FakeWeb.clean_registry
|
|
21
|
+
|
|
22
|
+
ElasticRecord::Config.models.each do |model|
|
|
23
|
+
model.elastic_index.enable_deferring!
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def teardown
|
|
28
|
+
ElasticRecord::Config.models.each do |model|
|
|
29
|
+
model.elastic_index.reset_deferring!
|
|
30
|
+
end
|
|
16
31
|
end
|
|
17
32
|
end
|
|
18
33
|
end
|
data/test/support/connect.rb
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
ElasticRecord::Config.servers = '127.0.0.1:9200'
|
|
1
|
+
ElasticRecord::Config.servers = '127.0.0.1:9200'
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
module TestModel
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
extend ActiveModel::Naming
|
|
6
|
+
extend ActiveModel::Callbacks
|
|
7
|
+
define_model_callbacks :save, :destroy
|
|
8
|
+
|
|
9
|
+
include ActiveModel::Dirty
|
|
10
|
+
include ActiveModel::Validations
|
|
11
|
+
|
|
12
|
+
include ElasticRecord::Model
|
|
13
|
+
include ElasticRecord::Callbacks
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module ClassMethods
|
|
17
|
+
def find(ids)
|
|
18
|
+
ids.map { |id| new(id: id, color: 'red') }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def base_class
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def create(attributes = {})
|
|
26
|
+
record = new(attributes)
|
|
27
|
+
record.save
|
|
28
|
+
record
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def define_attributes(attributes)
|
|
32
|
+
define_attribute_methods attributes
|
|
33
|
+
|
|
34
|
+
attributes.each do |attribute|
|
|
35
|
+
define_method attribute do
|
|
36
|
+
instance_variable_get("@#{attribute}")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
define_method "#{attribute}=" do |value|
|
|
40
|
+
send("#{attribute}_will_change!")
|
|
41
|
+
instance_variable_set("@#{attribute}", value)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
define_method 'attributes' do
|
|
46
|
+
Hash[attributes.map { |attr| [attr.to_s, send(attr)] }]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def initialize(attrs = {})
|
|
52
|
+
self.attributes = attrs
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def attributes=(attrs)
|
|
56
|
+
attrs.each do |key, val|
|
|
57
|
+
send("#{key}=", val)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def id=(value)
|
|
62
|
+
@id = value
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def id
|
|
66
|
+
@id ||= rand(10000).to_s
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def save
|
|
70
|
+
@persisted = true
|
|
71
|
+
run_callbacks :save
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def destroy
|
|
75
|
+
@destroyed = true
|
|
76
|
+
run_callbacks :destroy
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def ==(other)
|
|
80
|
+
id == other.id
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def changed?
|
|
84
|
+
true
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def new_record?
|
|
88
|
+
!@persisted
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def persisted?
|
|
92
|
+
@persisted
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def destroyed?
|
|
96
|
+
@destroyed
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
class Widget
|
|
2
|
-
|
|
3
|
-
extend ActiveModel::Callbacks
|
|
4
|
-
define_model_callbacks :save, :destroy
|
|
2
|
+
include TestModel
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
validates :color, format: {with: /[a-z]/}
|
|
5
|
+
|
|
6
|
+
define_attributes [:name, :color, :warehouse_id]
|
|
8
7
|
|
|
9
8
|
self.elastic_index.mapping[:properties].update(
|
|
10
9
|
name: {
|
|
@@ -16,14 +15,13 @@ class Widget
|
|
|
16
15
|
},
|
|
17
16
|
color: {
|
|
18
17
|
type: 'string', index: 'not_analyzed'
|
|
18
|
+
},
|
|
19
|
+
warehouse_id: {
|
|
20
|
+
type: 'string', index: 'not_analyzed'
|
|
19
21
|
}
|
|
20
22
|
)
|
|
21
|
-
|
|
22
|
-
class << self
|
|
23
|
-
def find(ids)
|
|
24
|
-
ids.map { |id| new(id: id, color: 'red') }
|
|
25
|
-
end
|
|
26
23
|
|
|
24
|
+
class << self
|
|
27
25
|
def anon(&block)
|
|
28
26
|
Class.new(self) do
|
|
29
27
|
def self.name
|
|
@@ -33,16 +31,9 @@ class Widget
|
|
|
33
31
|
instance_eval(&block)
|
|
34
32
|
end
|
|
35
33
|
end
|
|
36
|
-
|
|
37
|
-
def base_class
|
|
38
|
-
self
|
|
39
|
-
end
|
|
40
34
|
end
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
attributes.each do |key, val|
|
|
45
|
-
send("#{key}=", val)
|
|
46
|
-
end
|
|
36
|
+
def warehouse=(other)
|
|
37
|
+
self.warehouse_id = other.id
|
|
47
38
|
end
|
|
48
39
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: elastic_record
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-10-
|
|
12
|
+
date: 2012-10-18 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: arelastic
|
|
@@ -62,6 +62,7 @@ files:
|
|
|
62
62
|
- lib/elastic_record/config.rb
|
|
63
63
|
- lib/elastic_record/connection.rb
|
|
64
64
|
- lib/elastic_record/index.rb
|
|
65
|
+
- lib/elastic_record/index/deferred.rb
|
|
65
66
|
- lib/elastic_record/index/documents.rb
|
|
66
67
|
- lib/elastic_record/index/manage.rb
|
|
67
68
|
- lib/elastic_record/index/mapping.rb
|
|
@@ -79,6 +80,12 @@ files:
|
|
|
79
80
|
- lib/elastic_record/relation/none.rb
|
|
80
81
|
- lib/elastic_record/relation/search_methods.rb
|
|
81
82
|
- lib/elastic_record/relation/value_methods.rb
|
|
83
|
+
- lib/elastic_record/searches_many.rb
|
|
84
|
+
- lib/elastic_record/searches_many/association.rb
|
|
85
|
+
- lib/elastic_record/searches_many/autosave.rb
|
|
86
|
+
- lib/elastic_record/searches_many/builder.rb
|
|
87
|
+
- lib/elastic_record/searches_many/collection_proxy.rb
|
|
88
|
+
- lib/elastic_record/searches_many/reflection.rb
|
|
82
89
|
- lib/elastic_record/searching.rb
|
|
83
90
|
- lib/elastic_record/tasks/index.rake
|
|
84
91
|
- test/elastic_record/callbacks_test.rb
|
|
@@ -99,10 +106,16 @@ files:
|
|
|
99
106
|
- test/elastic_record/relation/none_test.rb
|
|
100
107
|
- test/elastic_record/relation/search_methods_test.rb
|
|
101
108
|
- test/elastic_record/relation_test.rb
|
|
109
|
+
- test/elastic_record/searches_many/autosave_test.rb
|
|
110
|
+
- test/elastic_record/searches_many/collection_proxy_test.rb
|
|
111
|
+
- test/elastic_record/searches_many/reflection_test.rb
|
|
112
|
+
- test/elastic_record/searches_many_test.rb
|
|
102
113
|
- test/elastic_record/searching_test.rb
|
|
103
114
|
- test/helper.rb
|
|
104
115
|
- test/support/connect.rb
|
|
105
|
-
- test/support/
|
|
116
|
+
- test/support/models/test_model.rb
|
|
117
|
+
- test/support/models/warehouse.rb
|
|
118
|
+
- test/support/models/widget.rb
|
|
106
119
|
homepage: http://github.com/matthuhiggins/elastic_record
|
|
107
120
|
licenses:
|
|
108
121
|
- MIT
|