mongoid 4.0.0.alpha2 → 4.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +55 -0
- data/README.md +3 -3
- data/lib/config/locales/en.yml +13 -0
- data/lib/mongoid.rb +3 -1
- data/lib/mongoid/atomic.rb +1 -1
- data/lib/mongoid/atomic/paths/embedded/many.rb +1 -1
- data/lib/mongoid/atomic/paths/embedded/one.rb +1 -1
- data/lib/mongoid/attributes.rb +23 -1
- data/lib/mongoid/attributes/processing.rb +1 -1
- data/lib/mongoid/composable.rb +3 -2
- data/lib/mongoid/contextual/command.rb +0 -26
- data/lib/mongoid/contextual/geo_near.rb +1 -1
- data/lib/mongoid/contextual/mongo.rb +6 -29
- data/lib/mongoid/contextual/text_search.rb +3 -5
- data/lib/mongoid/criteria.rb +1 -1
- data/lib/mongoid/criteria/modifiable.rb +27 -7
- data/lib/mongoid/criteria/permission.rb +70 -0
- data/lib/mongoid/document.rb +5 -6
- data/lib/mongoid/errors.rb +2 -0
- data/lib/mongoid/errors/document_not_destroyed.rb +25 -0
- data/lib/mongoid/errors/readonly_document.rb +24 -0
- data/lib/mongoid/extensions/boolean.rb +1 -0
- data/lib/mongoid/extensions/hash.rb +1 -1
- data/lib/mongoid/factory.rb +5 -3
- data/lib/mongoid/fields.rb +32 -0
- data/lib/mongoid/fields/localized.rb +1 -1
- data/lib/mongoid/fields/standard.rb +1 -1
- data/lib/mongoid/findable.rb +1 -0
- data/lib/mongoid/interceptable.rb +11 -6
- data/lib/mongoid/log_subscriber.rb +34 -1
- data/lib/mongoid/persistable/deletable.rb +1 -0
- data/lib/mongoid/persistable/destroyable.rb +7 -2
- data/lib/mongoid/persistable/updatable.rb +27 -26
- data/lib/mongoid/query_cache.rb +246 -0
- data/lib/mongoid/railties/database.rake +4 -26
- data/lib/mongoid/relations.rb +8 -22
- data/lib/mongoid/relations/accessors.rb +0 -3
- data/lib/mongoid/relations/binding.rb +1 -1
- data/lib/mongoid/relations/bindings/embedded/in.rb +1 -1
- data/lib/mongoid/relations/eager.rb +5 -6
- data/lib/mongoid/relations/eager/base.rb +97 -5
- data/lib/mongoid/relations/eager/belongs_to.rb +1 -0
- data/lib/mongoid/relations/eager/has_and_belongs_to_many.rb +16 -9
- data/lib/mongoid/relations/eager/has_many.rb +1 -0
- data/lib/mongoid/relations/eager/has_one.rb +1 -0
- data/lib/mongoid/relations/embedded/batchable.rb +1 -1
- data/lib/mongoid/relations/embedded/in.rb +4 -4
- data/lib/mongoid/relations/embedded/many.rb +7 -5
- data/lib/mongoid/relations/embedded/one.rb +1 -1
- data/lib/mongoid/relations/macros.rb +1 -0
- data/lib/mongoid/relations/marshalable.rb +3 -3
- data/lib/mongoid/relations/proxy.rb +12 -10
- data/lib/mongoid/relations/referenced/in.rb +2 -2
- data/lib/mongoid/relations/referenced/many.rb +9 -9
- data/lib/mongoid/relations/referenced/many_to_many.rb +7 -7
- data/lib/mongoid/relations/referenced/one.rb +4 -4
- data/lib/mongoid/{state.rb → stateful.rb} +13 -1
- data/lib/mongoid/tasks/database.rake +31 -0
- data/lib/mongoid/tasks/database.rb +107 -0
- data/lib/mongoid/threaded.rb +0 -47
- data/lib/mongoid/validatable/uniqueness.rb +4 -16
- data/lib/mongoid/version.rb +1 -1
- data/lib/rails/generators/mongoid/config/templates/mongoid.yml +0 -3
- data/lib/rails/mongoid.rb +0 -124
- data/spec/app/models/edit.rb +5 -0
- data/spec/app/models/even.rb +7 -0
- data/spec/app/models/line_item.rb +1 -1
- data/spec/app/models/note.rb +2 -0
- data/spec/app/models/odd.rb +7 -0
- data/spec/app/models/record.rb +5 -0
- data/spec/app/models/wiki_page.rb +1 -1
- data/spec/mongoid/attributes_spec.rb +76 -1
- data/spec/mongoid/changeable_spec.rb +6 -2
- data/spec/mongoid/contextual/mongo_spec.rb +3 -1
- data/spec/mongoid/contextual/text_search_spec.rb +3 -1
- data/spec/mongoid/criteria/modifiable_spec.rb +192 -0
- data/spec/mongoid/criteria_spec.rb +6 -2
- data/spec/mongoid/errors/document_not_destroyed_spec.rb +33 -0
- data/spec/mongoid/errors/readonly_document_spec.rb +29 -0
- data/spec/mongoid/fields/localized_spec.rb +15 -0
- data/spec/mongoid/fields_spec.rb +88 -2
- data/spec/mongoid/log_subscriber_spec.rb +3 -3
- data/spec/mongoid/persistable/deletable_spec.rb +14 -1
- data/spec/mongoid/persistable/destroyable_spec.rb +45 -1
- data/spec/mongoid/persistable/savable_spec.rb +34 -5
- data/spec/mongoid/query_cache_spec.rb +197 -0
- data/spec/mongoid/relations/bindings/embedded/in_spec.rb +2 -2
- data/spec/mongoid/relations/builders/referenced/many_spec.rb +1 -1
- data/spec/mongoid/relations/eager/has_and_belongs_to_many_spec.rb +11 -37
- data/spec/mongoid/relations/eager/has_one_spec.rb +1 -1
- data/spec/mongoid/relations/embedded/in_spec.rb +1 -1
- data/spec/mongoid/relations/embedded/many_spec.rb +10 -10
- data/spec/mongoid/relations/embedded/one_spec.rb +10 -2
- data/spec/mongoid/relations/referenced/in_spec.rb +1 -1
- data/spec/mongoid/relations/referenced/many_spec.rb +37 -2
- data/spec/mongoid/relations/touchable_spec.rb +20 -0
- data/spec/mongoid/{state_spec.rb → stateful_spec.rb} +26 -1
- data/spec/mongoid/tasks/database_rake_spec.rb +285 -0
- data/spec/mongoid/tasks/database_spec.rb +148 -0
- data/spec/mongoid/validatable/uniqueness_spec.rb +7 -0
- data/spec/rails/mongoid_spec.rb +0 -316
- data/spec/spec_helper.rb +1 -0
- metadata +30 -8
@@ -0,0 +1,246 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid
|
3
|
+
|
4
|
+
# A cache of database queries on a per-request basis.
|
5
|
+
#
|
6
|
+
# @since 4.0.0
|
7
|
+
module QueryCache
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Get the cached queries.
|
11
|
+
#
|
12
|
+
# @example Get the cached queries from the current thread.
|
13
|
+
# QueryCache.cache_table
|
14
|
+
#
|
15
|
+
# @return [ Hash ] The hash of cached queries.
|
16
|
+
#
|
17
|
+
# @since 4.0.0
|
18
|
+
def cache_table
|
19
|
+
Thread.current["[mongoid]:query_cache"] ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Clear the query cache.
|
23
|
+
#
|
24
|
+
# @example Clear the cache.
|
25
|
+
# QueryCache.clear_cache
|
26
|
+
#
|
27
|
+
# @return [ nil ] Always nil.
|
28
|
+
#
|
29
|
+
# @since 4.0.0
|
30
|
+
def clear_cache
|
31
|
+
Thread.current["[mongoid]:query_cache"] = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
# Set whether the cache is enabled.
|
35
|
+
#
|
36
|
+
# @example Set if the cache is enabled.
|
37
|
+
# QueryCache.enabled = true
|
38
|
+
#
|
39
|
+
# @param [ true, false ] value The enabled value.
|
40
|
+
#
|
41
|
+
# @since 4.0.0
|
42
|
+
def enabled=(value)
|
43
|
+
Thread.current["[mongoid]:query_cache:enabled"] = value
|
44
|
+
end
|
45
|
+
|
46
|
+
# Is the query cache enabled on the current thread?
|
47
|
+
#
|
48
|
+
# @example Is the query cache enabled?
|
49
|
+
# QueryCache.enabled?
|
50
|
+
#
|
51
|
+
# @return [ true, false ] If the cache is enabled.
|
52
|
+
#
|
53
|
+
# @since 4.0.0
|
54
|
+
def enabled?
|
55
|
+
!!Thread.current["[mongoid]:query_cache:enabled"]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Execute the block while using the query cache.
|
59
|
+
#
|
60
|
+
# @example Execute with the cache.
|
61
|
+
# QueryCache.cache { collection.find }
|
62
|
+
#
|
63
|
+
# @return [ Object ] The result of the block.
|
64
|
+
#
|
65
|
+
# @since 4.0.0
|
66
|
+
def cache
|
67
|
+
enabled = QueryCache.enabled?
|
68
|
+
QueryCache.enabled = true
|
69
|
+
yield
|
70
|
+
ensure
|
71
|
+
QueryCache.enabled = enabled
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# The middleware to be added to a rack application in order to activate the
|
76
|
+
# query cache.
|
77
|
+
#
|
78
|
+
# @since 4.0.0
|
79
|
+
class Middleware
|
80
|
+
|
81
|
+
# Instantiate the middleware.
|
82
|
+
#
|
83
|
+
# @example Create the new middleware.
|
84
|
+
# Middleware.new(app)
|
85
|
+
#
|
86
|
+
# @param [ Object ] app The rack applciation stack.
|
87
|
+
#
|
88
|
+
# @since 4.0.0
|
89
|
+
def initialize(app)
|
90
|
+
@app = app
|
91
|
+
end
|
92
|
+
|
93
|
+
# Execute the request, wrapping in a query cache.
|
94
|
+
#
|
95
|
+
# @example Execute the request.
|
96
|
+
# middleware.call(env)
|
97
|
+
#
|
98
|
+
# @param [ Object ] env The environment.
|
99
|
+
#
|
100
|
+
# @return [ Object ] The result of the call.
|
101
|
+
#
|
102
|
+
# @since 4.0.0
|
103
|
+
def call(env)
|
104
|
+
QueryCache.cache { @app.call(env) }
|
105
|
+
ensure
|
106
|
+
QueryCache.clear_cache
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
module Base # :nodoc:
|
111
|
+
|
112
|
+
def alias_query_cache_clear(*method_names)
|
113
|
+
method_names.each do |method_name|
|
114
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
115
|
+
def #{method_name}_with_clear_cache(*args)
|
116
|
+
QueryCache.clear_cache
|
117
|
+
#{method_name}_without_clear_cache(*args)
|
118
|
+
end
|
119
|
+
CODE
|
120
|
+
|
121
|
+
alias_method_chain method_name, :clear_cache
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Module to include in objects which need to wrap caching behaviour around
|
127
|
+
# them.
|
128
|
+
#
|
129
|
+
# @since 4.0.0
|
130
|
+
module Cacheable
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def with_cache
|
135
|
+
return yield unless QueryCache.enabled?
|
136
|
+
return yield if system_collection?
|
137
|
+
key = cache_key
|
138
|
+
if QueryCache.cache_table.has_key?(key)
|
139
|
+
instrument(key) { QueryCache.cache_table[key] }
|
140
|
+
else
|
141
|
+
QueryCache.cache_table[key] = yield
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def instrument(key, &block)
|
146
|
+
ActiveSupport::Notifications.instrument("query_cache.mongoid", key: key, &block)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Adds behaviour around caching to a Moped Query object.
|
151
|
+
#
|
152
|
+
# @since 4.0.0
|
153
|
+
module Query
|
154
|
+
extend ActiveSupport::Concern
|
155
|
+
include Cacheable
|
156
|
+
|
157
|
+
included do
|
158
|
+
extend QueryCache::Base
|
159
|
+
alias_method_chain :cursor, :cache
|
160
|
+
alias_method_chain :first, :cache
|
161
|
+
alias_query_cache_clear :remove, :remove_all, :update, :update_all, :upsert
|
162
|
+
end
|
163
|
+
|
164
|
+
# Provide a wrapped query cache cursor.
|
165
|
+
#
|
166
|
+
# @example Get the wrapped caching cursor.
|
167
|
+
# query.cursor_with_cache
|
168
|
+
#
|
169
|
+
# @return [ CachedCursor ] The cached cursor.
|
170
|
+
#
|
171
|
+
# @since 4.0.0
|
172
|
+
def cursor_with_cache
|
173
|
+
CachedCursor.new(session, operation)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Override first with caching.
|
177
|
+
#
|
178
|
+
# @example Get the first with a cache.
|
179
|
+
# query.first_with_cache
|
180
|
+
#
|
181
|
+
# @return [ Hash ] The first document.
|
182
|
+
#
|
183
|
+
# @since 4.0.0
|
184
|
+
def first_with_cache
|
185
|
+
with_cache do
|
186
|
+
first_without_cache
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def cache_key
|
193
|
+
[ operation.database, operation.collection, operation.selector ]
|
194
|
+
end
|
195
|
+
|
196
|
+
def system_collection?
|
197
|
+
operation.collection =~ /^system./
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Adds behaviour to the query cache for collections.
|
202
|
+
#
|
203
|
+
# @since 4.0.0
|
204
|
+
module Collection
|
205
|
+
extend ActiveSupport::Concern
|
206
|
+
|
207
|
+
included do
|
208
|
+
extend QueryCache::Base
|
209
|
+
alias_query_cache_clear :insert
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# A Cursor that attempts to load documents from memory first before hitting
|
214
|
+
# the database if the same query has already been executed.
|
215
|
+
#
|
216
|
+
# @since 4.0.0
|
217
|
+
class CachedCursor < Moped::Cursor
|
218
|
+
include Cacheable
|
219
|
+
|
220
|
+
# Override the loading of docs to attempt to fetch from the cache.
|
221
|
+
#
|
222
|
+
# @example Load the documents.
|
223
|
+
# cursor.load_docs
|
224
|
+
#
|
225
|
+
# @return [ Array<Hash> ] The documents.
|
226
|
+
#
|
227
|
+
# @since 4.0.0
|
228
|
+
def load_docs
|
229
|
+
with_cache { super }
|
230
|
+
end
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
def cache_key
|
235
|
+
[ @database, @collection, @selector ]
|
236
|
+
end
|
237
|
+
|
238
|
+
def system_collection?
|
239
|
+
@collection =~ /^system./
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
Moped::Query.__send__(:include, Mongoid::QueryCache::Query)
|
246
|
+
Moped::Collection.__send__(:include, Mongoid::QueryCache::Collection)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
load 'mongoid/tasks/database.rake'
|
2
|
+
|
1
3
|
namespace :db do
|
2
4
|
|
3
5
|
unless Rake::Task.task_defined?("db:drop")
|
@@ -21,7 +23,7 @@ namespace :db do
|
|
21
23
|
|
22
24
|
unless Rake::Task.task_defined?("db:setup")
|
23
25
|
desc "Create the database, and initialize with the seed data"
|
24
|
-
task :setup => [ "db:create", "
|
26
|
+
task :setup => [ "db:create", "mongoid:create_indexes", "db:seed" ]
|
25
27
|
end
|
26
28
|
|
27
29
|
unless Rake::Task.task_defined?("db:reset")
|
@@ -64,32 +66,8 @@ namespace :db do
|
|
64
66
|
end
|
65
67
|
|
66
68
|
namespace :mongoid do
|
67
|
-
|
68
|
-
task :create_indexes => :environment do
|
69
|
-
::Rails.application.eager_load!
|
70
|
-
::Rails::Mongoid.create_indexes
|
71
|
-
end
|
72
|
-
|
73
|
-
desc "Remove indexes that exist in the database but aren't specified on the models"
|
74
|
-
task :remove_undefined_indexes => :environment do
|
75
|
-
::Rails.application.eager_load!
|
76
|
-
::Rails::Mongoid.remove_undefined_indexes
|
77
|
-
end
|
78
|
-
|
79
|
-
desc "Remove the indexes defined on your mongoid models without questions!"
|
80
|
-
task :remove_indexes => :environment do
|
69
|
+
task :load_models do
|
81
70
|
::Rails.application.eager_load!
|
82
|
-
::Rails::Mongoid.remove_indexes
|
83
|
-
end
|
84
|
-
|
85
|
-
desc "Drops the database for the current Rails.env"
|
86
|
-
task :drop => :environment do
|
87
|
-
::Mongoid::Sessions.default.drop
|
88
|
-
end
|
89
|
-
|
90
|
-
desc "Drop all collections except the system collections"
|
91
|
-
task :purge => :environment do
|
92
|
-
::Mongoid.purge!
|
93
71
|
end
|
94
72
|
end
|
95
73
|
end
|
data/lib/mongoid/relations.rb
CHANGED
@@ -46,7 +46,8 @@ module Mongoid
|
|
46
46
|
include Touchable
|
47
47
|
include CounterCache
|
48
48
|
|
49
|
-
attr_accessor :
|
49
|
+
attr_accessor :__metadata
|
50
|
+
alias :relation_metadata :__metadata
|
50
51
|
|
51
52
|
# Determine if the document itself is embedded in another document via the
|
52
53
|
# proper channels. (If it has a parent document.)
|
@@ -70,7 +71,7 @@ module Mongoid
|
|
70
71
|
#
|
71
72
|
# @since 2.0.0.rc.1
|
72
73
|
def embedded_many?
|
73
|
-
|
74
|
+
__metadata && __metadata.macro == :embeds_many
|
74
75
|
end
|
75
76
|
|
76
77
|
# Determine if the document is part of an embeds_one relation.
|
@@ -82,7 +83,7 @@ module Mongoid
|
|
82
83
|
#
|
83
84
|
# @since 2.0.0.rc.1
|
84
85
|
def embedded_one?
|
85
|
-
|
86
|
+
__metadata && __metadata.macro == :embeds_one
|
86
87
|
end
|
87
88
|
|
88
89
|
# Get the metadata name for this document. If no metadata was defined
|
@@ -97,8 +98,8 @@ module Mongoid
|
|
97
98
|
#
|
98
99
|
# @since 3.0.0
|
99
100
|
def metadata_name
|
100
|
-
raise Errors::NoMetadata.new(self.class.name) unless
|
101
|
-
|
101
|
+
raise Errors::NoMetadata.new(self.class.name) unless __metadata
|
102
|
+
__metadata.name
|
102
103
|
end
|
103
104
|
|
104
105
|
# Determine if the document is part of an references_many relation.
|
@@ -110,7 +111,7 @@ module Mongoid
|
|
110
111
|
#
|
111
112
|
# @since 2.0.0.rc.1
|
112
113
|
def referenced_many?
|
113
|
-
|
114
|
+
__metadata && __metadata.macro == :has_many
|
114
115
|
end
|
115
116
|
|
116
117
|
# Determine if the document is part of an references_one relation.
|
@@ -122,7 +123,7 @@ module Mongoid
|
|
122
123
|
#
|
123
124
|
# @since 2.0.0.rc.1
|
124
125
|
def referenced_one?
|
125
|
-
|
126
|
+
__metadata && __metadata.macro == :has_one
|
126
127
|
end
|
127
128
|
|
128
129
|
# Convenience method for iterating through the loaded relations and
|
@@ -143,20 +144,5 @@ module Mongoid
|
|
143
144
|
end
|
144
145
|
end
|
145
146
|
end
|
146
|
-
|
147
|
-
module ClassMethods
|
148
|
-
|
149
|
-
# This is convenience for librarys still on the old API.
|
150
|
-
#
|
151
|
-
# @example Get the associations.
|
152
|
-
# Person.associations
|
153
|
-
#
|
154
|
-
# @return [ Hash ] The relations.
|
155
|
-
#
|
156
|
-
# @since 2.3.1
|
157
|
-
def associations
|
158
|
-
self.relations
|
159
|
-
end
|
160
|
-
end
|
161
147
|
end
|
162
148
|
end
|
@@ -155,7 +155,6 @@ module Mongoid
|
|
155
155
|
# person.game?
|
156
156
|
#
|
157
157
|
# @param [ String, Symbol ] name The name of the relation.
|
158
|
-
# @param [ Metadata ] The metadata.
|
159
158
|
#
|
160
159
|
# @return [ Class ] The model being set up.
|
161
160
|
#
|
@@ -201,7 +200,6 @@ module Mongoid
|
|
201
200
|
# Person.ids_getter("addresses", metadata)
|
202
201
|
#
|
203
202
|
# @param [ String, Symbol ] name The name of the relation.
|
204
|
-
# @param [ Metadata] metadata The metadata for the relation.
|
205
203
|
#
|
206
204
|
# @return [ Class ] The class being set up.
|
207
205
|
def ids_getter(name, metadata)
|
@@ -212,7 +210,6 @@ module Mongoid
|
|
212
210
|
self
|
213
211
|
end
|
214
212
|
|
215
|
-
|
216
213
|
# Defines the setter for the relation. This does a few things based on
|
217
214
|
# some conditions. If there is an existing association, a target
|
218
215
|
# substitution will take place, otherwise a new relation will be
|
@@ -193,7 +193,7 @@ module Mongoid
|
|
193
193
|
def set_base_metadata
|
194
194
|
inverse_metadata = metadata.inverse_metadata(target)
|
195
195
|
if inverse_metadata != metadata && !inverse_metadata.nil?
|
196
|
-
base.
|
196
|
+
base.__metadata = inverse_metadata
|
197
197
|
end
|
198
198
|
end
|
199
199
|
|
@@ -24,7 +24,7 @@ module Mongoid
|
|
24
24
|
#
|
25
25
|
# @since 2.0.0.rc.1
|
26
26
|
def bind_one
|
27
|
-
base.
|
27
|
+
base.__metadata = metadata.inverse_metadata(target) unless base.__metadata
|
28
28
|
base.parentize(target)
|
29
29
|
binding do
|
30
30
|
if base.embedded_many?
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require "mongoid/relations/eager/base"
|
2
3
|
require "mongoid/relations/eager/belongs_to"
|
3
4
|
require "mongoid/relations/eager/has_one"
|
@@ -11,12 +12,10 @@ module Mongoid
|
|
11
12
|
attr_accessor :eager_loaded
|
12
13
|
|
13
14
|
def with_eager_loading(document)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
doc
|
19
|
-
end
|
15
|
+
return nil unless document
|
16
|
+
doc = Factory.from_db(klass, document, criteria.options[:fields])
|
17
|
+
eager_load_one(doc)
|
18
|
+
doc
|
20
19
|
end
|
21
20
|
|
22
21
|
def eager_load_one(doc)
|
@@ -1,56 +1,148 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
module Mongoid
|
2
3
|
module Relations
|
3
4
|
module Eager
|
5
|
+
# Base class for eager load preload functions.
|
6
|
+
#
|
7
|
+
# @since 4.0.0
|
4
8
|
class Base
|
9
|
+
|
10
|
+
# Instantiate the eager load class.
|
11
|
+
#
|
12
|
+
# @example Create the new belongs to eager load preloader.
|
13
|
+
# BelongsTo.new(relations_metadata, parent_docs)
|
14
|
+
#
|
15
|
+
# @param [ Array<Metadata> ] Relations to eager load
|
16
|
+
# @param [ Array<Document> ] Documents to preload the relations
|
17
|
+
#
|
18
|
+
# @return [ Base ] The eager load preloader
|
19
|
+
#
|
20
|
+
# @since 4.0.0
|
5
21
|
def initialize(associations, docs)
|
6
22
|
@associations = associations
|
7
23
|
@docs = docs
|
8
24
|
@grouped_docs = {}
|
9
25
|
end
|
10
26
|
|
27
|
+
# Shift the current relation metadata
|
28
|
+
#
|
29
|
+
# @example Shift the current metadata.
|
30
|
+
# loader.shift_metadata
|
31
|
+
#
|
32
|
+
# @return [ Object ] The relation metadata object.
|
33
|
+
#
|
34
|
+
# @since 4.0.0
|
11
35
|
def shift_metadata
|
12
36
|
@metadata = @associations.shift
|
13
37
|
end
|
14
38
|
|
39
|
+
# Run the preloader.
|
40
|
+
#
|
41
|
+
# @example Preload the relations into the documents.
|
42
|
+
# loader.run
|
43
|
+
#
|
44
|
+
# @return [ Array ] The list of documents given.
|
45
|
+
#
|
46
|
+
# @since 4.0.0
|
15
47
|
def run
|
16
48
|
while shift_metadata
|
17
49
|
preload
|
18
50
|
end
|
51
|
+
@docs
|
19
52
|
end
|
20
53
|
|
54
|
+
# Preload the current relation.
|
55
|
+
#
|
56
|
+
# This method should be implemented in the subclass
|
57
|
+
#
|
58
|
+
# @example Preload the current relation into the documents.
|
59
|
+
# loader.preload
|
60
|
+
#
|
61
|
+
# @since 4.0.0
|
21
62
|
def preload
|
22
63
|
raise NotImplementedError
|
23
64
|
end
|
24
65
|
|
66
|
+
# Run the preloader.
|
67
|
+
#
|
68
|
+
# @example Iterate over the documents loadded for the current relation
|
69
|
+
# loader.each_loaded_document { |doc| }
|
70
|
+
#
|
71
|
+
# @since 4.0.0
|
25
72
|
def each_loaded_document
|
26
73
|
@metadata.klass.any_in(key => keys_from_docs).each do |doc|
|
27
74
|
yield doc
|
28
75
|
end
|
29
76
|
end
|
30
77
|
|
78
|
+
# Set the pre-loaded document into its parent.
|
79
|
+
#
|
80
|
+
# @example Set docs into parent with pk = "foo"
|
81
|
+
# loader.set_on_parent("foo", docs)
|
82
|
+
#
|
83
|
+
# @param [ ObjectId ] parent`s id
|
84
|
+
# @param [ Document, Array ] element to push into the parent
|
85
|
+
#
|
86
|
+
# @since 4.0.0
|
31
87
|
def set_on_parent(id, element)
|
32
88
|
grouped_docs[id].each do |d|
|
33
89
|
set_relation(d, element)
|
34
90
|
end
|
35
91
|
end
|
36
92
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
93
|
+
# Return a hash with the current documents grouped by key.
|
94
|
+
#
|
95
|
+
# @example Return a hash with the current documents grouped by key.
|
96
|
+
# loader.grouped_docs
|
97
|
+
#
|
98
|
+
# @return [ Hash ] hash with groupd documents.
|
99
|
+
#
|
100
|
+
# @since 4.0.0
|
41
101
|
def grouped_docs
|
42
102
|
@grouped_docs[@metadata.name] ||= @docs.group_by do |doc|
|
43
103
|
doc.send(group_by_key)
|
44
104
|
end
|
45
105
|
end
|
46
106
|
|
107
|
+
# Group the documents and return the keys
|
108
|
+
#
|
109
|
+
# @example
|
110
|
+
# loader.keys_from_docs
|
111
|
+
#
|
112
|
+
# @return [ Array ] keys, ids
|
113
|
+
#
|
114
|
+
# @since 4.0.0
|
47
115
|
def keys_from_docs
|
48
116
|
grouped_docs.keys
|
49
117
|
end
|
50
118
|
|
51
|
-
|
119
|
+
# Return the key to group the current documents.
|
120
|
+
#
|
121
|
+
# This method should be implemented in the subclass
|
122
|
+
#
|
123
|
+
# @example Return the key for group
|
124
|
+
# loader.group_by_key
|
125
|
+
#
|
126
|
+
# @return [ Symbol ] Key to group by the current documents.
|
127
|
+
#
|
128
|
+
# @since 4.0.0
|
129
|
+
def group_by_key
|
52
130
|
raise NotImplementedError
|
53
131
|
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
# Set the pre-loaded document into its parent.
|
135
|
+
#
|
136
|
+
# @example Set docs into parent using the current relation name.
|
137
|
+
# loader.set_relation(doc, docs)
|
138
|
+
#
|
139
|
+
# @param [ Document ] document
|
140
|
+
# @param [ Document, Array ] element to set into the parent
|
141
|
+
#
|
142
|
+
# @since 4.0.0
|
143
|
+
def set_relation(doc, element)
|
144
|
+
doc.set_relation(@metadata.name, element)
|
145
|
+
end
|
54
146
|
end
|
55
147
|
end
|
56
148
|
end
|