mongoid 3.0.23 → 3.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 (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