mongoid 7.5.2 → 7.5.4
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/Rakefile +25 -0
- data/lib/mongoid/association/embedded/batchable.rb +20 -3
- data/lib/mongoid/atomic/paths/embedded/many.rb +19 -0
- data/lib/mongoid/contextual/mongo.rb +24 -7
- data/lib/mongoid/criteria/queryable/selector.rb +1 -1
- data/lib/mongoid/criteria/queryable/storable.rb +1 -1
- data/lib/mongoid/shardable.rb +35 -11
- data/lib/mongoid/version.rb +1 -1
- data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +21 -0
- data/spec/mongoid/association/embedded/embeds_many_models.rb +121 -0
- data/spec/mongoid/contextual/mongo_spec.rb +23 -3
- data/spec/mongoid/criteria/queryable/selector_spec.rb +75 -2
- data/spec/mongoid/criteria/queryable/storable_spec.rb +72 -0
- data/spec/mongoid/shardable_models.rb +14 -0
- data/spec/mongoid/shardable_spec.rb +157 -51
- data/spec/mongoid_spec.rb +7 -1
- data/spec/shared/lib/mrss/docker_runner.rb +0 -4
- data/spec/shared/lib/mrss/lite_constraints.rb +8 -0
- data/spec/shared/shlib/server.sh +5 -5
- data.tar.gz.sig +0 -0
- metadata +584 -580
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce7f228fdc033dc1142f46f4771395d36acafc97e0c144599cd063af537c7ae3
|
4
|
+
data.tar.gz: 15f8f0efbb076eb03f25f23898cbe27d044e7441e4a7ae4bc8d22f036838bb98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91f40b7136ae729e6831c5033c60c0a1e2207cd98d80bf5caaef50844230d6830efe555748571d9bdef62a101f75ffd21d2a7942278f84b68e2a51fc6a8f20df
|
7
|
+
data.tar.gz: 3555b90da32865e1faadb9efccc6db0262adea620b4dd473d5dc1ee533b00451039dfaacd982ef5c8f6be4009ccff589119b2f512971a1b5ac509bb7ae4aa96e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/Rakefile
CHANGED
@@ -11,6 +11,15 @@ $: << File.join(ROOT, 'spec/shared/lib')
|
|
11
11
|
require "rake"
|
12
12
|
require "rspec/core/rake_task"
|
13
13
|
require 'mrss/spec_organizer'
|
14
|
+
require 'rubygems/package'
|
15
|
+
require 'rubygems/security/policies'
|
16
|
+
|
17
|
+
def signed_gem?(path_to_gem)
|
18
|
+
Gem::Package.new(path_to_gem, Gem::Security::HighSecurity).verify
|
19
|
+
true
|
20
|
+
rescue Gem::Security::Exception => e
|
21
|
+
false
|
22
|
+
end
|
14
23
|
|
15
24
|
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
16
25
|
require "mongoid/version"
|
@@ -103,3 +112,19 @@ namespace :release do
|
|
103
112
|
end
|
104
113
|
end
|
105
114
|
end
|
115
|
+
|
116
|
+
desc 'Verifies that all built gems in pkg/ are valid'
|
117
|
+
task :verify do
|
118
|
+
gems = Dir['pkg/*.gem']
|
119
|
+
if gems.empty?
|
120
|
+
puts 'There are no gems in pkg/ to verify'
|
121
|
+
else
|
122
|
+
gems.each do |gem|
|
123
|
+
if signed_gem?(gem)
|
124
|
+
puts "#{gem} is signed"
|
125
|
+
else
|
126
|
+
abort "#{gem} is not signed"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -80,7 +80,8 @@ module Mongoid
|
|
80
80
|
def batch_replace(docs)
|
81
81
|
if docs.blank?
|
82
82
|
if _assigning? && !empty?
|
83
|
-
_base.delayed_atomic_sets.
|
83
|
+
_base.delayed_atomic_sets.delete(path)
|
84
|
+
clear_atomic_path_cache
|
84
85
|
_base.add_atomic_unset(first)
|
85
86
|
target_duplicate = _target.dup
|
86
87
|
pre_process_batch_remove(target_duplicate, :delete)
|
@@ -92,7 +93,8 @@ module Mongoid
|
|
92
93
|
_base.delayed_atomic_sets.clear unless _assigning?
|
93
94
|
docs = normalize_docs(docs).compact
|
94
95
|
_target.clear and _unscoped.clear
|
95
|
-
_base.delayed_atomic_unsets.
|
96
|
+
_base.delayed_atomic_unsets.delete(path)
|
97
|
+
clear_atomic_path_cache
|
96
98
|
inserts = execute_batch_set(docs)
|
97
99
|
add_atomic_sets(inserts)
|
98
100
|
end
|
@@ -234,7 +236,22 @@ module Mongoid
|
|
234
236
|
#
|
235
237
|
# @return [ String ] The atomic path.
|
236
238
|
def path
|
237
|
-
@path ||= _unscoped.
|
239
|
+
@path ||= if _unscoped.empty?
|
240
|
+
Mongoid::Atomic::Paths::Embedded::Many.position_without_document(_base, _association)
|
241
|
+
else
|
242
|
+
_unscoped.first.atomic_path
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Clear the cache for path and atomic_paths. This method is used when
|
247
|
+
# the path method is used, and the association has not been set on the
|
248
|
+
# document yet, which can cause path and atomic_paths to be calculated
|
249
|
+
# incorrectly later.
|
250
|
+
#
|
251
|
+
# @api private
|
252
|
+
def clear_atomic_path_cache
|
253
|
+
self.path = nil
|
254
|
+
_base.instance_variable_set("@atomic_paths", nil)
|
238
255
|
end
|
239
256
|
|
240
257
|
# Set the atomic path.
|
@@ -34,6 +34,25 @@ module Mongoid
|
|
34
34
|
locator = document.new_record? ? "" : ".#{document._index}"
|
35
35
|
"#{pos}#{"." unless pos.blank?}#{document._association.store_as}#{locator}"
|
36
36
|
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
|
40
|
+
# Get the position of where the document would go for the given
|
41
|
+
# association. The use case for this function is when trying to
|
42
|
+
# persist an empty list for an embedded association. All of the
|
43
|
+
# existing functions for getting the position to store a document
|
44
|
+
# require passing in a document to store, which we don't have when
|
45
|
+
# trying to store the empty list.
|
46
|
+
#
|
47
|
+
# @param [ Document ] parent The parent document to store in.
|
48
|
+
# @param [ Association ] association The association.
|
49
|
+
#
|
50
|
+
# @return [ String ] The position string.
|
51
|
+
def position_without_document(parent, association)
|
52
|
+
pos = parent.atomic_position
|
53
|
+
"#{pos}#{"." unless pos.blank?}#{association.store_as}"
|
54
|
+
end
|
55
|
+
end
|
37
56
|
end
|
38
57
|
end
|
39
58
|
end
|
@@ -260,16 +260,16 @@ module Mongoid
|
|
260
260
|
#
|
261
261
|
# @return [ Document ] The first document.
|
262
262
|
def first(limit_or_opts = nil)
|
263
|
-
limit = limit_or_opts
|
263
|
+
limit, opts = extract_limit_and_opts(limit_or_opts)
|
264
264
|
if cached? && cache_loaded?
|
265
265
|
return limit ? documents.first(limit) : documents.first
|
266
266
|
end
|
267
267
|
try_numbered_cache(:first, limit) do
|
268
|
-
if
|
268
|
+
if opts.key?(:id_sort)
|
269
269
|
Mongoid::Warnings.warn_id_sort_deprecated
|
270
270
|
end
|
271
271
|
sorted_view = view
|
272
|
-
if sort = view.sort || ({ _id: 1 } unless
|
272
|
+
if sort = view.sort || ({ _id: 1 } unless opts[:id_sort] == :none)
|
273
273
|
sorted_view = view.sort(sort)
|
274
274
|
end
|
275
275
|
if raw_docs = sorted_view.limit(limit || 1).to_a
|
@@ -376,12 +376,12 @@ module Mongoid
|
|
376
376
|
#
|
377
377
|
# @return [ Document ] The last document.
|
378
378
|
def last(limit_or_opts = nil)
|
379
|
-
limit = limit_or_opts
|
379
|
+
limit, opts = extract_limit_and_opts(limit_or_opts)
|
380
380
|
if cached? && cache_loaded?
|
381
381
|
return limit ? documents.last(limit) : documents.last
|
382
382
|
end
|
383
383
|
res = try_numbered_cache(:last, limit) do
|
384
|
-
with_inverse_sorting(
|
384
|
+
with_inverse_sorting(opts) do
|
385
385
|
if raw_docs = view.limit(limit || 1).to_a
|
386
386
|
process_raw_docs(raw_docs, limit)
|
387
387
|
end
|
@@ -612,6 +612,23 @@ module Mongoid
|
|
612
612
|
end
|
613
613
|
end
|
614
614
|
|
615
|
+
# Extract the limit and opts from the given argument, so that code
|
616
|
+
# can operate without having to worry about the current type and
|
617
|
+
# state of the argument.
|
618
|
+
#
|
619
|
+
# @param [ nil | Integer | Hash ] limit_or_opts The value to pull the
|
620
|
+
# limit and option hash from.
|
621
|
+
#
|
622
|
+
# @return [ Array<nil | Integer, Hash> ] A 2-array of the limit and the
|
623
|
+
# option hash.
|
624
|
+
def extract_limit_and_opts(limit_or_opts)
|
625
|
+
case limit_or_opts
|
626
|
+
when nil, Integer then [ limit_or_opts, {} ]
|
627
|
+
when Hash then [ nil, limit_or_opts ]
|
628
|
+
else raise ArgumentError, "expected nil, Integer, or Hash"
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
615
632
|
# Update the documents for the provided method.
|
616
633
|
#
|
617
634
|
# @api private
|
@@ -676,10 +693,10 @@ module Mongoid
|
|
676
693
|
# @example Apply the inverse sorting params to the given block
|
677
694
|
# context.with_inverse_sorting
|
678
695
|
def with_inverse_sorting(opts = {})
|
679
|
-
Mongoid::Warnings.warn_id_sort_deprecated if opts.
|
696
|
+
Mongoid::Warnings.warn_id_sort_deprecated if opts.key?(:id_sort)
|
680
697
|
|
681
698
|
begin
|
682
|
-
if sort = criteria.options[:sort] || ( { _id: 1 } unless opts
|
699
|
+
if sort = criteria.options[:sort] || ( { _id: 1 } unless opts[:id_sort] == :none )
|
683
700
|
@view = view.sort(Hash[sort.map{|k, v| [k, -1*v]}])
|
684
701
|
end
|
685
702
|
yield
|
@@ -47,7 +47,7 @@ module Mongoid
|
|
47
47
|
if value.is_a?(Hash) && selector[field].is_a?(Hash) &&
|
48
48
|
value.keys.all? { |key|
|
49
49
|
key_s = key.to_s
|
50
|
-
key_s.start_with?('$') && !selector[field].
|
50
|
+
key_s.start_with?('$') && !selector[field].keys.map(&:to_s).include?(key_s)
|
51
51
|
}
|
52
52
|
then
|
53
53
|
# Multiple operators can be combined on the same field by
|
data/lib/mongoid/shardable.rb
CHANGED
@@ -47,18 +47,22 @@ module Mongoid
|
|
47
47
|
self.class.shard_key_fields
|
48
48
|
end
|
49
49
|
|
50
|
-
# Returns the selector that would match the
|
51
|
-
#
|
50
|
+
# Returns the selector that would match the defined shard keys. If
|
51
|
+
# `prefer_persisted` is false (the default), it uses the current values
|
52
|
+
# of the specified shard keys, otherwise, it will try to use whatever value
|
53
|
+
# was most recently persisted.
|
54
|
+
#
|
55
|
+
# @param [ true | false ] prefer_persisted Whether to use the current
|
56
|
+
# value of the shard key fields, or to use their most recently persisted
|
57
|
+
# values.
|
52
58
|
#
|
53
59
|
# @return [ Hash ] The shard key selector.
|
54
60
|
#
|
55
61
|
# @api private
|
56
|
-
def shard_key_selector
|
57
|
-
|
58
|
-
|
59
|
-
selector[field.to_s] = send(field)
|
62
|
+
def shard_key_selector(prefer_persisted: false)
|
63
|
+
shard_key_fields.each_with_object({}) do |field, selector|
|
64
|
+
selector[field.to_s] = shard_key_field_value(field.to_s, prefer_persisted: prefer_persisted)
|
60
65
|
end
|
61
|
-
selector
|
62
66
|
end
|
63
67
|
|
64
68
|
# Returns the selector that would match the existing version of this
|
@@ -72,11 +76,31 @@ module Mongoid
|
|
72
76
|
#
|
73
77
|
# @api private
|
74
78
|
def shard_key_selector_in_db
|
75
|
-
|
76
|
-
|
77
|
-
|
79
|
+
shard_key_selector(prefer_persisted: true)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns the value for the named shard key. If the field identifies
|
83
|
+
# an embedded document, the key will be parsed and recursively evaluated.
|
84
|
+
# If `prefer_persisted` is true, the value last persisted to the database
|
85
|
+
# will be returned, regardless of what the current value of the attribute
|
86
|
+
# may be.
|
87
|
+
#
|
88
|
+
# @param [String] field The name of the field to evaluate
|
89
|
+
# @param [ true|false ] prefer_persisted Whether or not to prefer the
|
90
|
+
# persisted value over the current value.
|
91
|
+
#
|
92
|
+
# @return [ Object ] The value of the named field.
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
def shard_key_field_value(field, prefer_persisted:)
|
96
|
+
if field.include?(".")
|
97
|
+
relation, remaining = field.split(".", 2)
|
98
|
+
send(relation)&.shard_key_field_value(remaining, prefer_persisted: prefer_persisted)
|
99
|
+
elsif prefer_persisted && !new_record?
|
100
|
+
attribute_was(field)
|
101
|
+
else
|
102
|
+
send(field)
|
78
103
|
end
|
79
|
-
selector
|
80
104
|
end
|
81
105
|
|
82
106
|
module ClassMethods
|
data/lib/mongoid/version.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
|
+
require_relative '../embeds_many_models.rb'
|
4
5
|
|
5
6
|
describe Mongoid::Association::Embedded::EmbedsMany::Proxy do
|
6
7
|
|
@@ -4649,4 +4650,24 @@ describe Mongoid::Association::Embedded::EmbedsMany::Proxy do
|
|
4649
4650
|
end
|
4650
4651
|
end
|
4651
4652
|
end
|
4653
|
+
|
4654
|
+
context "when using assign_attributes with an already populated array" do
|
4655
|
+
let(:post) { EmmPost.create! }
|
4656
|
+
|
4657
|
+
before do
|
4658
|
+
post.assign_attributes(company_tags: [{id: BSON::ObjectId.new, title: 'a'}],
|
4659
|
+
user_tags: [{id: BSON::ObjectId.new, title: 'b'}])
|
4660
|
+
post.save!
|
4661
|
+
post.reload
|
4662
|
+
post.assign_attributes(company_tags: [{id: BSON::ObjectId.new, title: 'c'}],
|
4663
|
+
user_tags: [])
|
4664
|
+
post.save!
|
4665
|
+
post.reload
|
4666
|
+
end
|
4667
|
+
|
4668
|
+
it "has the correct embedded documents" do
|
4669
|
+
expect(post.company_tags.length).to eq(1)
|
4670
|
+
expect(post.company_tags.first.title).to eq("c")
|
4671
|
+
end
|
4672
|
+
end
|
4652
4673
|
end
|
@@ -67,3 +67,124 @@ class EmmOuter
|
|
67
67
|
|
68
68
|
field :level, :type => Integer
|
69
69
|
end
|
70
|
+
|
71
|
+
class EmmCustomerAddress
|
72
|
+
include Mongoid::Document
|
73
|
+
|
74
|
+
embedded_in :addressable, polymorphic: true, inverse_of: :work_address
|
75
|
+
end
|
76
|
+
|
77
|
+
class EmmFriend
|
78
|
+
include Mongoid::Document
|
79
|
+
|
80
|
+
embedded_in :befriendable, polymorphic: true
|
81
|
+
end
|
82
|
+
|
83
|
+
class EmmCustomer
|
84
|
+
include Mongoid::Document
|
85
|
+
|
86
|
+
embeds_one :home_address, class_name: 'EmmCustomerAddress', as: :addressable
|
87
|
+
embeds_one :work_address, class_name: 'EmmCustomerAddress', as: :addressable
|
88
|
+
|
89
|
+
embeds_many :close_friends, class_name: 'EmmFriend', as: :befriendable
|
90
|
+
embeds_many :acquaintances, class_name: 'EmmFriend', as: :befriendable
|
91
|
+
end
|
92
|
+
|
93
|
+
class EmmUser
|
94
|
+
include Mongoid::Document
|
95
|
+
include Mongoid::Timestamps
|
96
|
+
|
97
|
+
embeds_many :orders, class_name: 'EmmOrder'
|
98
|
+
end
|
99
|
+
|
100
|
+
class EmmOrder
|
101
|
+
include Mongoid::Document
|
102
|
+
|
103
|
+
field :amount, type: Integer
|
104
|
+
|
105
|
+
embedded_in :user, class_name: 'EmmUser'
|
106
|
+
end
|
107
|
+
|
108
|
+
module EmmSpec
|
109
|
+
# There is also a top-level Car class defined.
|
110
|
+
class Car
|
111
|
+
include Mongoid::Document
|
112
|
+
|
113
|
+
embeds_many :doors
|
114
|
+
end
|
115
|
+
|
116
|
+
class Door
|
117
|
+
include Mongoid::Document
|
118
|
+
|
119
|
+
embedded_in :car
|
120
|
+
end
|
121
|
+
|
122
|
+
class Tank
|
123
|
+
include Mongoid::Document
|
124
|
+
|
125
|
+
embeds_many :guns
|
126
|
+
embeds_many :emm_turrets
|
127
|
+
# This association references a model that is not in our module,
|
128
|
+
# and it does not define class_name hence Mongoid will not be able to
|
129
|
+
# figure out the inverse for this association.
|
130
|
+
embeds_many :emm_hatches
|
131
|
+
|
132
|
+
# class_name is intentionally unqualified, references a class in the
|
133
|
+
# same module. Rails permits class_name to be unqualified like this.
|
134
|
+
embeds_many :launchers, class_name: 'Launcher'
|
135
|
+
end
|
136
|
+
|
137
|
+
class Gun
|
138
|
+
include Mongoid::Document
|
139
|
+
|
140
|
+
embedded_in :tank
|
141
|
+
end
|
142
|
+
|
143
|
+
class Launcher
|
144
|
+
include Mongoid::Document
|
145
|
+
|
146
|
+
# class_name is intentionally unqualified.
|
147
|
+
embedded_in :tank, class_name: 'Tank'
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# This is intentionally on top level.
|
152
|
+
class EmmTurret
|
153
|
+
include Mongoid::Document
|
154
|
+
|
155
|
+
embedded_in :tank, class_name: 'EmmSpec::Tank'
|
156
|
+
end
|
157
|
+
|
158
|
+
# This is intentionally on top level.
|
159
|
+
class EmmHatch
|
160
|
+
include Mongoid::Document
|
161
|
+
|
162
|
+
# No :class_name option on this association intentionally.
|
163
|
+
embedded_in :tank
|
164
|
+
end
|
165
|
+
|
166
|
+
class EmmPost
|
167
|
+
include Mongoid::Document
|
168
|
+
|
169
|
+
embeds_many :company_tags, class_name: "EmmCompanyTag"
|
170
|
+
embeds_many :user_tags, class_name: "EmmUserTag"
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
class EmmCompanyTag
|
175
|
+
include Mongoid::Document
|
176
|
+
|
177
|
+
field :title, type: String
|
178
|
+
|
179
|
+
embedded_in :post, class_name: "EmmPost"
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
class EmmUserTag
|
184
|
+
include Mongoid::Document
|
185
|
+
|
186
|
+
field :title, type: String
|
187
|
+
|
188
|
+
embedded_in :post, class_name: "EmmPost"
|
189
|
+
end
|
190
|
+
|
@@ -1554,9 +1554,9 @@ describe Mongoid::Contextual::Mongo do
|
|
1554
1554
|
expect(context.send(method)).to eq(depeche_mode)
|
1555
1555
|
end
|
1556
1556
|
|
1557
|
-
context
|
1557
|
+
context "when calling ##{method}" do
|
1558
1558
|
|
1559
|
-
it 'returns the
|
1559
|
+
it 'returns the requested document, sorted by _id' do
|
1560
1560
|
expect(context.send(method)).to eq(depeche_mode)
|
1561
1561
|
expect(context.last).to eq(rolling_stones)
|
1562
1562
|
end
|
@@ -1571,7 +1571,7 @@ describe Mongoid::Contextual::Mongo do
|
|
1571
1571
|
expect(context.send(method, opts)).to eq(depeche_mode)
|
1572
1572
|
end
|
1573
1573
|
|
1574
|
-
context
|
1574
|
+
context "when calling ##{method}" do
|
1575
1575
|
|
1576
1576
|
it 'doesn\'t apply a sort on _id' do
|
1577
1577
|
expect(context.send(method, opts)).to eq(depeche_mode)
|
@@ -1883,6 +1883,16 @@ describe Mongoid::Contextual::Mongo do
|
|
1883
1883
|
end
|
1884
1884
|
end
|
1885
1885
|
|
1886
|
+
context "when given an empty hash" do
|
1887
|
+
let(:context) { described_class.new(criteria) }
|
1888
|
+
let(:criteria) { Band.criteria }
|
1889
|
+
let(:docs) { context.send(method, {}) }
|
1890
|
+
|
1891
|
+
it "behaves as if limit is nil" do
|
1892
|
+
expect(docs).to eq(depeche_mode)
|
1893
|
+
end
|
1894
|
+
end
|
1895
|
+
|
1886
1896
|
context "when calling #first then #last" do
|
1887
1897
|
|
1888
1898
|
let(:context) do
|
@@ -2358,6 +2368,16 @@ describe Mongoid::Contextual::Mongo do
|
|
2358
2368
|
end
|
2359
2369
|
end
|
2360
2370
|
end
|
2371
|
+
|
2372
|
+
context "when given an empty hash" do
|
2373
|
+
let(:context) { described_class.new(criteria) }
|
2374
|
+
let(:criteria) { Band.criteria }
|
2375
|
+
let(:docs) { context.last({}) }
|
2376
|
+
|
2377
|
+
it "behaves as if limit is nil" do
|
2378
|
+
expect(docs).to eq(rolling_stones)
|
2379
|
+
end
|
2380
|
+
end
|
2361
2381
|
end
|
2362
2382
|
|
2363
2383
|
describe "#initialize" do
|
@@ -44,7 +44,7 @@ describe Mongoid::Criteria::Queryable::Selector do
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
context "when selector contains a $nin" do
|
47
|
+
context "when selector contains a $nin string" do
|
48
48
|
|
49
49
|
let(:initial) do
|
50
50
|
{ "$nin" => ["foo"] }
|
@@ -72,7 +72,35 @@ describe Mongoid::Criteria::Queryable::Selector do
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
context "when selector contains a $
|
75
|
+
context "when selector contains a $nin symbol" do
|
76
|
+
|
77
|
+
let(:initial) do
|
78
|
+
{ :$nin => ["foo"] }
|
79
|
+
end
|
80
|
+
|
81
|
+
before do
|
82
|
+
selector["field"] = initial
|
83
|
+
end
|
84
|
+
|
85
|
+
context "when merging in a new $nin" do
|
86
|
+
|
87
|
+
let(:other) do
|
88
|
+
{ "field" => { :$nin => ["bar"] } }
|
89
|
+
end
|
90
|
+
|
91
|
+
before do
|
92
|
+
selector.merge!(other)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "combines the two $nin queries into one" do
|
96
|
+
expect(selector).to eq({
|
97
|
+
"field" => { :$nin => ["foo", "bar"] }
|
98
|
+
})
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "when selector contains a $in string" do
|
76
104
|
|
77
105
|
let(:initial) do
|
78
106
|
{ "$in" => [1, 2] }
|
@@ -117,6 +145,51 @@ describe Mongoid::Criteria::Queryable::Selector do
|
|
117
145
|
end
|
118
146
|
end
|
119
147
|
|
148
|
+
context "when selector contains a $in symbol" do
|
149
|
+
|
150
|
+
let(:initial) do
|
151
|
+
{ :$in => [1, 2] }
|
152
|
+
end
|
153
|
+
|
154
|
+
before do
|
155
|
+
selector["field"] = initial
|
156
|
+
end
|
157
|
+
|
158
|
+
context "when merging in a new $in with an intersecting value" do
|
159
|
+
|
160
|
+
let(:other) do
|
161
|
+
{ "field" => { :$in => [1] } }
|
162
|
+
end
|
163
|
+
|
164
|
+
before do
|
165
|
+
selector.merge!(other)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "intersects the $in values" do
|
169
|
+
expect(selector).to eq({
|
170
|
+
"field" => { :$in => [1] }
|
171
|
+
})
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "when merging in a new $in with no intersecting values" do
|
176
|
+
|
177
|
+
let(:other) do
|
178
|
+
{ "field" => { :$in => [3] } }
|
179
|
+
end
|
180
|
+
|
181
|
+
before do
|
182
|
+
selector.merge!(other)
|
183
|
+
end
|
184
|
+
|
185
|
+
it "intersects the $in values" do
|
186
|
+
expect(selector).to eq({
|
187
|
+
"field" => { :$in => [] }
|
188
|
+
})
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
120
193
|
context "when selector is not nested" do
|
121
194
|
|
122
195
|
before do
|
@@ -210,7 +210,79 @@ describe Mongoid::Criteria::Queryable::Storable do
|
|
210
210
|
}
|
211
211
|
end
|
212
212
|
end
|
213
|
+
|
214
|
+
context 'when value is a hash combine values with different operator keys' do
|
215
|
+
let(:base) do
|
216
|
+
query.add_field_expression('foo', {'$in' => ['bar']})
|
217
|
+
end
|
218
|
+
|
219
|
+
let(:modified) do
|
220
|
+
base.add_field_expression('foo', {'$nin' => ['zoom']})
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'combines the conditions using $and' do
|
224
|
+
modified.selector.should == {
|
225
|
+
'foo' => {
|
226
|
+
'$in' => ['bar'],
|
227
|
+
'$nin' => ['zoom']
|
228
|
+
}
|
229
|
+
}
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context 'when value is a hash with symbol operator key combine values with different operator keys' do
|
234
|
+
let(:base) do
|
235
|
+
query.add_field_expression('foo', {:$in => ['bar']})
|
236
|
+
end
|
237
|
+
|
238
|
+
let(:modified) do
|
239
|
+
base.add_field_expression('foo', {:$nin => ['zoom']})
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'combines the conditions using $and' do
|
243
|
+
modified.selector.should == {
|
244
|
+
'foo' => {
|
245
|
+
:$in => ['bar'],
|
246
|
+
:$nin => ['zoom']
|
247
|
+
}
|
248
|
+
}
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
context 'when value is a hash add values with same operator keys using $and' do
|
253
|
+
let(:base) do
|
254
|
+
query.add_field_expression('foo', {'$in' => ['bar']})
|
255
|
+
end
|
256
|
+
|
257
|
+
let(:modified) do
|
258
|
+
base.add_field_expression('foo', {'$in' => ['zoom']})
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'adds the new condition using $and' do
|
262
|
+
modified.selector.should == {
|
263
|
+
'foo' => {'$in' => ['bar']},
|
264
|
+
'$and' => ['foo' => {'$in' => ['zoom']}]
|
265
|
+
}
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
context 'when value is a hash with symbol operator key add values with same operator keys using $and' do
|
270
|
+
let(:base) do
|
271
|
+
query.add_field_expression('foo', {:$in => ['bar']})
|
272
|
+
end
|
273
|
+
|
274
|
+
let(:modified) do
|
275
|
+
base.add_field_expression('foo', {:$in => ['zoom']})
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'adds the new condition using $and' do
|
279
|
+
modified.selector.should == {
|
280
|
+
'foo' => {:$in => ['bar']},
|
281
|
+
'$and' => ['foo' => {:$in => ['zoom']}]
|
282
|
+
}
|
283
|
+
end
|
213
284
|
end
|
285
|
+
end
|
214
286
|
|
215
287
|
describe '#add_operator_expression' do
|
216
288
|
let(:query_method) { :add_operator_expression }
|