mongoid 3.0.23 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/CHANGELOG.md +253 -9
  2. data/LICENSE +1 -1
  3. data/README.md +4 -1
  4. data/lib/config/locales/en.yml +7 -6
  5. data/lib/mongoid.rb +18 -1
  6. data/lib/mongoid/atomic.rb +22 -20
  7. data/lib/mongoid/atomic/paths/embedded.rb +19 -5
  8. data/lib/mongoid/atomic/paths/root.rb +1 -1
  9. data/lib/mongoid/atomic/positionable.rb +73 -0
  10. data/lib/mongoid/attributes.rb +63 -1
  11. data/lib/mongoid/callbacks.rb +58 -4
  12. data/lib/mongoid/components.rb +8 -3
  13. data/lib/mongoid/config.rb +71 -23
  14. data/lib/mongoid/contextual.rb +2 -1
  15. data/lib/mongoid/contextual/aggregable/mongo.rb +27 -63
  16. data/lib/mongoid/contextual/atomic.rb +4 -3
  17. data/lib/mongoid/contextual/find_and_modify.rb +1 -1
  18. data/lib/mongoid/contextual/geo_near.rb +238 -0
  19. data/lib/mongoid/contextual/map_reduce.rb +12 -1
  20. data/lib/mongoid/contextual/memory.rb +36 -31
  21. data/lib/mongoid/contextual/mongo.rb +147 -91
  22. data/lib/mongoid/contextual/queryable.rb +25 -0
  23. data/lib/mongoid/copyable.rb +4 -1
  24. data/lib/mongoid/criteria.rb +23 -275
  25. data/lib/mongoid/criterion/findable.rb +179 -0
  26. data/lib/mongoid/criterion/modifiable.rb +191 -0
  27. data/lib/mongoid/criterion/scoping.rb +11 -6
  28. data/lib/mongoid/document.rb +7 -56
  29. data/lib/mongoid/equality.rb +66 -0
  30. data/lib/mongoid/errors/mongoid_error.rb +7 -3
  31. data/lib/mongoid/extensions/array.rb +13 -1
  32. data/lib/mongoid/extensions/date.rb +9 -2
  33. data/lib/mongoid/extensions/hash.rb +38 -2
  34. data/lib/mongoid/extensions/nil_class.rb +12 -0
  35. data/lib/mongoid/extensions/object.rb +24 -0
  36. data/lib/mongoid/extensions/string.rb +14 -2
  37. data/lib/mongoid/extensions/time.rb +4 -1
  38. data/lib/mongoid/fields.rb +49 -5
  39. data/lib/mongoid/fields/foreign_key.rb +12 -0
  40. data/lib/mongoid/fields/standard.rb +12 -0
  41. data/lib/mongoid/finders.rb +8 -0
  42. data/lib/mongoid/hierarchy.rb +19 -1
  43. data/lib/mongoid/indexes.rb +30 -4
  44. data/lib/mongoid/indexes/validators/options.rb +12 -2
  45. data/lib/mongoid/inspection.rb +2 -1
  46. data/lib/mongoid/matchers/strategies.rb +5 -5
  47. data/lib/mongoid/observer.rb +27 -36
  48. data/lib/mongoid/persistence.rb +42 -17
  49. data/lib/mongoid/persistence/atomic.rb +10 -5
  50. data/lib/mongoid/persistence/atomic/operation.rb +26 -9
  51. data/lib/mongoid/persistence/atomic/unset.rb +1 -1
  52. data/lib/mongoid/persistence/operations/embedded/insert.rb +5 -2
  53. data/lib/mongoid/persistence/operations/embedded/remove.rb +5 -2
  54. data/lib/mongoid/persistence/operations/update.rb +7 -3
  55. data/lib/mongoid/railties/database.rake +12 -19
  56. data/lib/mongoid/relations.rb +2 -0
  57. data/lib/mongoid/relations/accessors.rb +30 -8
  58. data/lib/mongoid/relations/binding.rb +5 -1
  59. data/lib/mongoid/relations/bindings/referenced/in.rb +1 -1
  60. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +3 -3
  61. data/lib/mongoid/relations/counter_cache.rb +107 -0
  62. data/lib/mongoid/relations/embedded/batchable.rb +13 -4
  63. data/lib/mongoid/relations/embedded/many.rb +30 -1
  64. data/lib/mongoid/relations/macros.rb +2 -0
  65. data/lib/mongoid/relations/marshalable.rb +0 -1
  66. data/lib/mongoid/relations/metadata.rb +63 -11
  67. data/lib/mongoid/relations/options.rb +1 -0
  68. data/lib/mongoid/relations/proxy.rb +45 -2
  69. data/lib/mongoid/relations/referenced/in.rb +11 -2
  70. data/lib/mongoid/relations/referenced/many.rb +31 -3
  71. data/lib/mongoid/relations/referenced/many_to_many.rb +31 -3
  72. data/lib/mongoid/relations/referenced/one.rb +1 -1
  73. data/lib/mongoid/relations/targets/enumerable.rb +5 -1
  74. data/lib/mongoid/relations/touchable.rb +35 -6
  75. data/lib/mongoid/reloading.rb +5 -3
  76. data/lib/mongoid/scoping.rb +2 -2
  77. data/lib/mongoid/sessions.rb +57 -7
  78. data/lib/mongoid/sessions/factory.rb +22 -1
  79. data/lib/mongoid/threaded.rb +4 -30
  80. data/lib/mongoid/threaded/lifecycle.rb +12 -12
  81. data/lib/mongoid/timestamps.rb +1 -0
  82. data/lib/mongoid/timestamps/created.rb +2 -0
  83. data/lib/mongoid/timestamps/created/short.rb +19 -0
  84. data/lib/mongoid/timestamps/short.rb +10 -0
  85. data/lib/mongoid/timestamps/updated.rb +2 -0
  86. data/lib/mongoid/timestamps/updated/short.rb +19 -0
  87. data/lib/mongoid/validations.rb +2 -0
  88. data/lib/mongoid/validations/queryable.rb +2 -2
  89. data/lib/mongoid/validations/uniqueness.rb +1 -18
  90. data/lib/mongoid/version.rb +1 -1
  91. data/lib/rails/generators/mongoid/model/model_generator.rb +1 -0
  92. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +3 -0
  93. data/lib/rails/mongoid.rb +53 -29
  94. data/lib/support/ruby_version.rb +26 -0
  95. metadata +18 -7
@@ -4,6 +4,7 @@ require "mongoid/contextual/aggregable/mongo"
4
4
  require "mongoid/contextual/command"
5
5
  require "mongoid/contextual/eager"
6
6
  require "mongoid/contextual/find_and_modify"
7
+ require "mongoid/contextual/geo_near"
7
8
  require "mongoid/contextual/map_reduce"
8
9
 
9
10
  module Mongoid
@@ -13,25 +14,10 @@ module Mongoid
13
14
  include Aggregable::Mongo
14
15
  include Atomic
15
16
  include Eager
17
+ include Queryable
16
18
 
17
- # @attribute [r] collection The collection to query against.
18
- # @attribute [r] criteria The criteria for the context.
19
- # @attribute [r] klass The klass for the criteria.
20
19
  # @attribute [r] query The Moped query.
21
- attr_reader :collection, :criteria, :klass, :query
22
-
23
- # Is the enumerable of matching documents empty?
24
- #
25
- # @example Is the context empty?
26
- # context.blank?
27
- #
28
- # @return [ true, false ] If the context is empty.
29
- #
30
- # @since 3.0.0
31
- def blank?
32
- !exists?
33
- end
34
- alias :empty? :blank?
20
+ attr_reader :query
35
21
 
36
22
  # Is the context cached?
37
23
  #
@@ -145,12 +131,15 @@ module Mongoid
145
131
  # @example Do any documents exist for the context.
146
132
  # context.exists?
147
133
  #
134
+ # @note We don't use count here since Mongo does not use counted
135
+ # b-tree indexes, unless a count is already cached then that is
136
+ # used to determine the value.
137
+ #
148
138
  # @return [ true, false ] If the count is more than zero.
149
139
  #
150
140
  # @since 3.0.0
151
141
  def exists?
152
- # Don't use count here since Mongo does not use counted b-tree indexes
153
- !query.dup.select(_id: 1).limit(1).entries.first.nil?
142
+ @exists ||= check_existence
154
143
  end
155
144
 
156
145
  # Run an explain on the criteria.
@@ -196,12 +185,63 @@ module Mongoid
196
185
  #
197
186
  # @since 3.0.0
198
187
  def first
199
- apply_sorting
200
- apply_id_sorting
201
- with_eager_loading(query.first)
188
+ if cached? && cache_loaded?
189
+ documents.first
190
+ else
191
+ with_sorting do
192
+ with_eager_loading(query.first)
193
+ end
194
+ end
202
195
  end
203
196
  alias :one :first
204
197
 
198
+ # Execute a $geoNear command against the database.
199
+ #
200
+ # @example Find documents close to 10, 10.
201
+ # context.geo_near([ 10, 10 ])
202
+ #
203
+ # @example Find with spherical distance.
204
+ # context.geo_near([ 10, 10 ]).spherical
205
+ #
206
+ # @example Find with a max distance.
207
+ # context.geo_near([ 10, 10 ]).max_distance(0.5)
208
+ #
209
+ # @example Provide a distance multiplier.
210
+ # context.geo_near([ 10, 10 ]).distance_multiplier(1133)
211
+ #
212
+ # @param [ Array<Float> ] coordinates The coordinates.
213
+ #
214
+ # @return [ GeoNear ] The GeoNear command.
215
+ #
216
+ # @since 3.1.0
217
+ def geo_near(coordinates)
218
+ GeoNear.new(collection, criteria, coordinates)
219
+ end
220
+
221
+ # Invoke the block for each element of Contextual. Create a new array
222
+ # containing the values returned by the block.
223
+ #
224
+ # If the symbol field name is passed instead of the block, additional
225
+ # optimizations would be used.
226
+ #
227
+ # @example Map by some field.
228
+ # context.map(:field1)
229
+ #
230
+ # @exmaple Map with block.
231
+ # context.map(&:field1)
232
+ #
233
+ # @param [ Symbol ] field The field name.
234
+ #
235
+ # @return [ Array ] The result of mapping.
236
+ def map(field = nil, &block)
237
+ if block_given?
238
+ super(&block)
239
+ else
240
+ field = field.to_sym
241
+ criteria.only(field).map(&field.to_proc)
242
+ end
243
+ end
244
+
205
245
  # Create the new Mongo context. This delegates operations to the
206
246
  # underlying driver - in Mongoid's case Moped.
207
247
  #
@@ -230,8 +270,9 @@ module Mongoid
230
270
  #
231
271
  # @since 3.0.0
232
272
  def last
233
- apply_inverse_sorting
234
- with_eager_loading(query.first)
273
+ with_inverse_sorting do
274
+ with_eager_loading(query.first)
275
+ end
235
276
  end
236
277
 
237
278
  # Get's the number of documents matching the query selector.
@@ -276,6 +317,25 @@ module Mongoid
276
317
  MapReduce.new(collection, criteria, map, reduce)
277
318
  end
278
319
 
320
+ # Pluck the single field values from the database. Will return duplicates
321
+ # if they exist and only works for top level fields.
322
+ #
323
+ # @example Pluck a field.
324
+ # context.pluck(:_id)
325
+ #
326
+ # @note This method will return the raw db values - it performs no custom
327
+ # serialization.
328
+ #
329
+ # @param [ String, Symbol ] field The field to pluck.
330
+ #
331
+ # @return [ Array<Object> ] The plucked values.
332
+ #
333
+ # @since 3.1.0
334
+ def pluck(field)
335
+ normalized = field.to_s
336
+ query.select(normalized => 1).map{ |doc| doc[normalized] }.compact
337
+ end
338
+
279
339
  # Skips the provided number of documents.
280
340
  #
281
341
  # @example Skip the documents.
@@ -305,7 +365,10 @@ module Mongoid
305
365
  if block_given?
306
366
  super(&block)
307
367
  else
308
- query.sort(values) and self
368
+ # update the criteria
369
+ @criteria = criteria.order_by(values)
370
+ apply_option(:sort)
371
+ self
309
372
  end
310
373
  end
311
374
 
@@ -314,7 +377,7 @@ module Mongoid
314
377
  # @example Update the first matching document.
315
378
  # context.update({ "$set" => { name: "Smiths" }})
316
379
  #
317
- # @param [ Hash ] attributes The new attributes for each document.
380
+ # @param [ Hash ] attributes The new attributes for the document.
318
381
  #
319
382
  # @return [ nil, false ] False if no attributes were provided.
320
383
  #
@@ -339,6 +402,24 @@ module Mongoid
339
402
 
340
403
  private
341
404
 
405
+ # Checks if any documents exist in the database.
406
+ #
407
+ # @api private
408
+ #
409
+ # @example Check for document existsence.
410
+ # context.check_existence
411
+ #
412
+ # @return [ true, false ] If documents exist.
413
+ #
414
+ # @since 3.1.0
415
+ def check_existence
416
+ if cached? && cache_loaded?
417
+ !documents.empty?
418
+ else
419
+ @count ? @count > 0 : !query.dup.select(_id: 1).limit(1).entries.first.nil?
420
+ end
421
+ end
422
+
342
423
  # Update the documents for the provided method.
343
424
  #
344
425
  # @api private
@@ -355,7 +436,7 @@ module Mongoid
355
436
  def update_documents(attributes, method = :update)
356
437
  return false unless attributes
357
438
  attributes = Hash[attributes.map { |k, v| [klass.database_field_name(k.to_s), v] }]
358
- query.send(method, attributes.__consolidate__)
439
+ query.send(method, attributes.__consolidate__(klass))
359
440
  end
360
441
 
361
442
  # Apply the field limitations.
@@ -372,55 +453,32 @@ module Mongoid
372
453
  end
373
454
  end
374
455
 
375
- # Apply the skip option.
456
+ # Apply the options.
376
457
  #
377
458
  # @api private
378
459
  #
379
- # @example Apply the skip option.
380
- # context.apply_skip
460
+ # @example Apply all options.
461
+ # context.apply_options
381
462
  #
382
- # @since 3.0.0
383
- def apply_skip
384
- if spec = criteria.options[:skip]
385
- query.skip(spec)
463
+ # @since 3.1.0
464
+ def apply_options
465
+ apply_fields
466
+ [ :hint, :limit, :skip, :sort, :batch_size, :max_scan ].each do |name|
467
+ apply_option(name)
386
468
  end
387
469
  end
388
470
 
389
- # Apply the limit option.
471
+ # Apply an option.
390
472
  #
391
473
  # @api private
392
474
  #
393
- # @example Apply the limit option.
394
- # context.apply_limit
395
- #
396
- # @since 3.0.0
397
- def apply_limit
398
- if spec = criteria.options[:limit]
399
- query.limit(spec)
400
- end
401
- end
402
-
403
- # Map the sort symbols to the correct MongoDB values.
404
- #
405
- # @example Apply the sorting params.
406
- # context.apply_sorting
407
- #
408
- # @since 3.0.0
409
- def apply_sorting
410
- if spec = criteria.options[:sort]
411
- query.sort(spec)
412
- end
413
- end
414
-
415
- # Apply the hint option
416
- #
417
- # @example Apply the hint params.
418
- # context.apply_hint
475
+ # @example Apply the skip option.
476
+ # context.apply_option(:skip)
419
477
  #
420
- # @since 3.0.0
421
- def apply_hint
422
- if spec = criteria.options[:hint]
423
- query.hint(spec)
478
+ # @since 3.1.0
479
+ def apply_option(name)
480
+ if spec = criteria.options[name]
481
+ query.send(name, spec)
424
482
  end
425
483
  end
426
484
 
@@ -429,27 +487,39 @@ module Mongoid
429
487
  #
430
488
  # @api private
431
489
  #
432
- # @example Apply the id sorting params.
433
- # context.apply_dd_sorting
490
+ # @example Apply the id sorting params to the given block
491
+ # context.with_sorting
434
492
  #
435
493
  # @since 3.0.0
436
- def apply_id_sorting
437
- unless criteria.options.has_key?(:sort)
438
- query.sort(_id: 1)
494
+ def with_sorting
495
+ begin
496
+ unless criteria.options.has_key?(:sort)
497
+ query.sort(_id: 1)
498
+ end
499
+ yield
500
+ ensure
501
+ apply_option(:sort)
439
502
  end
440
503
  end
441
504
 
442
505
  # Map the inverse sort symbols to the correct MongoDB values.
443
506
  #
444
- # @example Apply the inverse sorting params.
445
- # context.apply_inverse_sorting
507
+ # @api private
508
+ #
509
+ # @example Apply the inverse sorting params to the given block
510
+ # context.with_inverse_sorting
446
511
  #
447
512
  # @since 3.0.0
448
- def apply_inverse_sorting
449
- if spec = criteria.options[:sort]
450
- query.sort(Hash[spec.map{|k, v| [k, -1*v]}])
451
- else
452
- query.sort(_id: -1)
513
+ def with_inverse_sorting
514
+ begin
515
+ if spec = criteria.options[:sort]
516
+ query.sort(Hash[spec.map{|k, v| [k, -1*v]}])
517
+ else
518
+ query.sort(_id: -1)
519
+ end
520
+ yield
521
+ ensure
522
+ apply_option(:sort)
453
523
  end
454
524
  end
455
525
 
@@ -524,20 +594,6 @@ module Mongoid
524
594
  end
525
595
  end
526
596
 
527
- # Apply all the optional criterion.
528
- #
529
- # @example Apply the options.
530
- # context.apply_options
531
- #
532
- # @since 3.0.0
533
- def apply_options
534
- apply_fields
535
- apply_limit
536
- apply_skip
537
- apply_sorting
538
- apply_hint
539
- end
540
-
541
597
  # If we are limiting results, we need to set the field limitations on a
542
598
  # thread local to avoid overriding the default values.
543
599
  #
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Contextual
4
+ module Queryable
5
+
6
+ # @attribute [r] collection The collection to query against.
7
+ # @attribute [r] criteria The criteria for the context.
8
+ # @attribute [r] klass The klass for the criteria.
9
+ attr_reader :collection, :criteria, :klass
10
+
11
+ # Is the enumerable of matching documents empty?
12
+ #
13
+ # @example Is the context empty?
14
+ # context.blank?
15
+ #
16
+ # @return [ true, false ] If the context is empty.
17
+ #
18
+ # @since 3.0.0
19
+ def blank?
20
+ !exists?
21
+ end
22
+ alias :empty? :blank?
23
+ end
24
+ end
25
+ end
@@ -18,7 +18,10 @@ module Mongoid
18
18
  #
19
19
  # @return [ Document ] The new document.
20
20
  def clone
21
- attrs = clone_document.except("_id")
21
+ # @note This next line is here to address #2704, even though having an
22
+ # _id and id field in the document would cause problems with Mongoid
23
+ # elsewhere.
24
+ attrs = clone_document.except("_id", "id")
22
25
  self.class.new(attrs, without_protection: true)
23
26
  end
24
27
  alias :dup :clone
@@ -1,6 +1,8 @@
1
1
  # encoding: utf-8
2
2
  require "mongoid/criterion/inspection"
3
+ require "mongoid/criterion/findable"
3
4
  require "mongoid/criterion/marshalable"
5
+ require "mongoid/criterion/modifiable"
4
6
  require "mongoid/criterion/scoping"
5
7
 
6
8
  module Mongoid
@@ -16,7 +18,9 @@ module Mongoid
16
18
  include Contextual
17
19
  include Origin::Queryable
18
20
  include Criterion::Inspection
21
+ include Criterion::Findable
19
22
  include Criterion::Marshalable
23
+ include Criterion::Modifiable
20
24
  include Criterion::Scoping
21
25
 
22
26
  attr_accessor :embedded, :klass
@@ -48,23 +52,6 @@ module Mongoid
48
52
  entries.as_json(options)
49
53
  end
50
54
 
51
- # Build a document given the selector and return it.
52
- # Complex criteria, such as $in and $or operations will get ignored.
53
- #
54
- # @example build the document.
55
- # Person.where(:title => "Sir").build
56
- #
57
- # @example Build with selectors getting ignored.
58
- # Person.where(:age.gt => 5).build
59
- #
60
- # @return [ Document ] A non-persisted document.
61
- #
62
- # @since 2.0.0
63
- def build(attrs = {}, &block)
64
- create_document(:new, attrs, &block)
65
- end
66
- alias :new :build
67
-
68
55
  # Tells the criteria that the cursor that gets returned needs to be
69
56
  # cached. This is so multiple iterations don't hit the database multiple
70
57
  # times, however this is not advisable when working with large data sets
@@ -90,41 +77,6 @@ module Mongoid
90
77
  options[:cache] == true
91
78
  end
92
79
 
93
- # Create a document in the database given the selector and return it.
94
- # Complex criteria, such as $in and $or operations will get ignored.
95
- #
96
- # @example Create the document.
97
- # Person.where(:title => "Sir").create
98
- #
99
- # @example Create with selectors getting ignored.
100
- # Person.where(:age.gt => 5).create
101
- #
102
- # @return [ Document ] A newly created document.
103
- #
104
- # @since 2.0.0.rc.1
105
- def create(attrs = {}, &block)
106
- create_document(:create, attrs, &block)
107
- end
108
-
109
- # Create a document in the database given the selector and return it.
110
- # Complex criteria, such as $in and $or operations will get ignored.
111
- # If validation fails, an error will be raised.
112
- #
113
- # @example Create the document.
114
- # Person.where(:title => "Sir").create
115
- #
116
- # @example Create with selectors getting ignored.
117
- # Person.where(:age.gt => 5).create
118
- #
119
- # @raise [ Errors::Validations ] on a validation error.
120
- #
121
- # @return [ Document ] A newly created document.
122
- #
123
- # @since 3.0.0
124
- def create!(attrs = {}, &block)
125
- create_document(:create!, attrs, &block)
126
- end
127
-
128
80
  # Get the documents from the embedded criteria.
129
81
  #
130
82
  # @example Get the documents.
@@ -162,24 +114,6 @@ module Mongoid
162
114
  !!@embedded
163
115
  end
164
116
 
165
- # Execute the criteria or raise an error if no documents found.
166
- #
167
- # @example Execute or raise
168
- # criteria.execute_or_raise(id)
169
- #
170
- # @param [ Object ] args The arguments passed.
171
- #
172
- # @raise [ Errors::DocumentNotFound ] If nothing returned.
173
- #
174
- # @return [ Document, Array<Document> ] The document(s).
175
- #
176
- # @since 2.0.0
177
- def execute_or_raise(ids, multi)
178
- result = multiple_from_map_or_db(ids)
179
- check_for_missing_documents!(result, ids)
180
- multi ? result : result.first
181
- end
182
-
183
117
  # Extract a single id from the provided criteria. Could be in an $and
184
118
  # query or a straight _id query.
185
119
  #
@@ -226,72 +160,6 @@ module Mongoid
226
160
  end
227
161
  end
228
162
 
229
- # Find the matchind document(s) in the criteria for the provided ids.
230
- #
231
- # @example Find by an id.
232
- # criteria.find(Moped::BSON::ObjectId.new)
233
- #
234
- # @example Find by multiple ids.
235
- # criteria.find([ Moped::BSON::ObjectId.new, Moped::BSON::ObjectId.new ])
236
- #
237
- # @param [ Array<Moped::BSON::ObjectId> ] args The ids to search for.
238
- #
239
- # @return [ Array<Document>, Document ] The matching document(s).
240
- #
241
- # @since 1.0.0
242
- def find(*args)
243
- ids = args.__find_args__
244
- raise_invalid if ids.any?(&:nil?)
245
- for_ids(ids).execute_or_raise(ids, args.multi_arged?)
246
- end
247
-
248
- # Find the first +Document+ given the conditions, or creates a new document
249
- # with the conditions that were supplied.
250
- #
251
- # @example Find or create the document.
252
- # Person.find_or_create_by(:attribute => "value")
253
- #
254
- # @param [ Hash ] attrs The attributes to check.
255
- #
256
- # @return [ Document ] A matching or newly created document.
257
- def find_or_create_by(attrs = {}, &block)
258
- find_or(:create, attrs, &block)
259
- end
260
-
261
- # Find the first +Document+ given the conditions, or initializes a new document
262
- # with the conditions that were supplied.
263
- #
264
- # @example Find or initialize the document.
265
- # Person.find_or_initialize_by(:attribute => "value")
266
- #
267
- # @param [ Hash ] attrs The attributes to check.
268
- #
269
- # @return [ Document ] A matching or newly initialized document.
270
- def find_or_initialize_by(attrs = {}, &block)
271
- find_or(:new, attrs, &block)
272
- end
273
-
274
- # Adds a criterion to the +Criteria+ that specifies an id that must be matched.
275
- #
276
- # @example Add a single id criteria.
277
- # criteria.for_ids([ 1 ])
278
- #
279
- # @example Add multiple id criteria.
280
- # criteria.for_ids([ 1, 2 ])
281
- #
282
- # @param [ Array ] ids The array of ids.
283
- #
284
- # @return [ Criteria ] The cloned criteria.
285
- def for_ids(ids)
286
- ids = mongoize_ids(ids)
287
- method = extract_id ? :all_of : :where
288
- if ids.size > 1
289
- send(method, { _id: { "$in" => ids }})
290
- else
291
- send(method, { _id: ids.first })
292
- end
293
- end
294
-
295
163
  # When freezing a criteria we need to initialize the context first
296
164
  # otherwise the setting of the context on attempted iteration will raise a
297
165
  # runtime error.
@@ -306,39 +174,6 @@ module Mongoid
306
174
  context and inclusions and super
307
175
  end
308
176
 
309
- # Get the document from the identity map, and if not found hit the
310
- # database.
311
- #
312
- # @example Get the document from the map or criteria.
313
- # criteria.from_map_or_db
314
- #
315
- # @return [ Document ] The found document.
316
- #
317
- # @since 2.2.1
318
- def from_map_or_db
319
- id = extract_id
320
- id = klass.fields["_id"].mongoize(id) if id
321
- doc = IdentityMap.get(klass, id || selector.except("_type"))
322
- return nil if doc == {}
323
- doc && doc.matches?(selector) ? doc : first
324
- end
325
-
326
- # Get the documents from the identity map, and if not found hit the
327
- # database.
328
- #
329
- # @example Get the documents from the map or criteria.
330
- # criteria.multiple_from_map_or_db(ids)
331
- #
332
- # @param [ ids ] The searched ids.
333
- #
334
- # @return [ Array<Document> ] The found documents.
335
- def multiple_from_map_or_db(ids)
336
- return entries if embedded?
337
- ids = mongoize_ids(ids)
338
- result = from_identity_map(ids)
339
- ids.empty? ? result : result + from_database(ids)
340
- end
341
-
342
177
  # Initialize the new criteria.
343
178
  #
344
179
  # @example Init the new criteria.
@@ -468,7 +303,7 @@ module Mongoid
468
303
  #
469
304
  # @since 1.0.0
470
305
  def only(*args)
471
- return clone if args.empty?
306
+ return clone if args.flatten.empty?
472
307
  args = args.flatten
473
308
  if klass.hereditary?
474
309
  super(*args.push(:_type))
@@ -589,6 +424,24 @@ module Mongoid
589
424
  crit
590
425
  end
591
426
 
427
+ # Find documents by the provided javascript and scope. Uses a $where but is
428
+ # different from +Criteria#where+ in that it will pass a code object to the
429
+ # query instead of a pure string. Safe against Javascript injection
430
+ # attacks.
431
+ #
432
+ # @example Find by javascript.
433
+ # Band.for_js("this.name = param", param: "Tool")
434
+ #
435
+ # @param [ String ] javascript The javascript to execute in the $where.
436
+ # @param [ Hash ] scope The scope for the code.
437
+ #
438
+ # @return [ Criteria ] The criteria.
439
+ #
440
+ # @since 3.1.0
441
+ def for_js(javascript, scope = {})
442
+ js_query(Moped::BSON::Code.new(javascript, scope))
443
+ end
444
+
592
445
  private
593
446
 
594
447
  # Are documents in the query missing, and are we configured to raise an
@@ -612,45 +465,6 @@ module Mongoid
612
465
  end
613
466
  end
614
467
 
615
- # Create a document given the provided method and attributes from the
616
- # existing selector.
617
- #
618
- # @api private
619
- #
620
- # @example Create a new document.
621
- # criteria.create_document(:new, {})
622
- #
623
- # @param [ Symbol ] method Either :new or :create.
624
- # @param [ Hash ] attrs Additional attributes to use.
625
- #
626
- # @return [ Document ] The new or saved document.
627
- #
628
- # @since 3.0.0
629
- def create_document(method, attrs = {}, &block)
630
- klass.__send__(method,
631
- selector.reduce(attrs) do |hash, (key, value)|
632
- unless key.to_s =~ /\$/ || value.is_a?(Hash)
633
- hash[key] = value
634
- end
635
- hash
636
- end, &block)
637
- end
638
-
639
- # Find the first object or create/initialize it.
640
- #
641
- # @api private
642
- #
643
- # @example Find or perform an action.
644
- # Person.find_or(:create, :name => "Dev")
645
- #
646
- # @param [ Symbol ] method The method to invoke.
647
- # @param [ Hash ] attrs The attributes to query or set.
648
- #
649
- # @return [ Document ] The first or new document.
650
- def find_or(method, attrs = {}, &block)
651
- where(attrs).first || send(method, attrs, &block)
652
- end
653
-
654
468
  # Clone or dup the current +Criteria+. This will return a new criteria with
655
469
  # the selector, options, klass, embedded options, etc intact.
656
470
  #
@@ -675,44 +489,6 @@ module Mongoid
675
489
  super
676
490
  end
677
491
 
678
- # Get documents from the database only.
679
- #
680
- # @api private
681
- #
682
- # @example Get documents from the database.
683
- # criteria.from_database(ids)
684
- #
685
- # @param [ Array<Object> ] ids The ids to fetch with.
686
- #
687
- # @return [ Array<Document> ] The matching documents.
688
- #
689
- # @since 3.0.0
690
- def from_database(ids)
691
- (ids.size > 1 ? any_in(id: ids) : where(id: ids.first)).entries
692
- end
693
-
694
- # Get documents from the identity map only.
695
- #
696
- # @api private
697
- #
698
- # @example Get documents from the identity map.
699
- # criteria.from_identity_map(ids)
700
- #
701
- # @param [ Array<Object> ] ids The ids to fetch with.
702
- #
703
- # @return [ Array<Document> ] The matching documents.
704
- #
705
- # @since 3.0.0
706
- def from_identity_map(ids)
707
- result = []
708
- selection = selector_with_type_selection
709
- ids.reject! do |id|
710
- doc = IdentityMap.get(klass, id)
711
- doc && doc.matches?(selection) ? result.push(doc) : false
712
- end
713
- result
714
- end
715
-
716
492
  # Used for chaining +Criteria+ scopes together in the for of class methods
717
493
  # on the +Document+ the criteria is for.
718
494
  #
@@ -748,34 +524,6 @@ module Mongoid
748
524
  selector.merge!(type_selection) if type_selectable?
749
525
  end
750
526
 
751
- # Convert all the ids to their proper types.
752
- #
753
- # @api private
754
- #
755
- # @example Convert the ids.
756
- # criteria.mongoize_ids(ids)
757
- #
758
- # @param [ Array<Object> ] ids The ids to convert.
759
- #
760
- # @return [ Array<Object> ] The converted ids.
761
- #
762
- # @since 3.0.0
763
- def mongoize_ids(ids)
764
- ids.map{ |id| klass.fields["_id"].mongoize(id) }
765
- end
766
-
767
- # Convenience method of raising an invalid options error.
768
- #
769
- # @example Raise the error.
770
- # criteria.raise_invalid
771
- #
772
- # @raise [ Errors::InvalidOptions ] The error.
773
- #
774
- # @since 2.0.0
775
- def raise_invalid
776
- raise Errors::InvalidFind.new
777
- end
778
-
779
527
  # Is the criteria type selectable?
780
528
  #
781
529
  # @api private