mongoid 7.1.0.rc0 → 7.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/LICENSE +1 -1
- data/README.md +4 -4
- data/lib/mongoid.rb +3 -2
- data/lib/mongoid/association/many.rb +3 -2
- data/lib/mongoid/association/proxy.rb +5 -3
- data/lib/mongoid/association/referenced/has_many/enumerable.rb +2 -22
- data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -2
- data/lib/mongoid/attributes.rb +28 -20
- data/lib/mongoid/config.rb +3 -3
- data/lib/mongoid/config/options.rb +5 -2
- data/lib/mongoid/contextual.rb +5 -4
- data/lib/mongoid/contextual/geo_near.rb +3 -2
- data/lib/mongoid/contextual/map_reduce.rb +3 -2
- data/lib/mongoid/contextual/mongo.rb +2 -1
- data/lib/mongoid/fields/standard.rb +2 -1
- data/lib/mongoid/findable.rb +5 -4
- data/lib/mongoid/interceptable.rb +5 -1
- data/lib/mongoid/railties/database.rake +7 -0
- data/lib/mongoid/serializable.rb +3 -1
- data/lib/mongoid/shardable.rb +56 -4
- data/lib/mongoid/tasks/database.rake +10 -5
- data/lib/mongoid/tasks/database.rb +48 -0
- data/lib/mongoid/timestamps/timeless.rb +3 -1
- data/lib/mongoid/version.rb +1 -1
- data/spec/integration/shardable_spec.rb +133 -0
- data/spec/lite_spec_helper.rb +3 -0
- data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +41 -68
- data/spec/mongoid/attributes_spec.rb +19 -7
- data/spec/mongoid/changeable_spec.rb +23 -0
- data/spec/mongoid/document_fields_spec.rb +29 -0
- data/spec/mongoid/interceptable_spec.rb +62 -0
- data/spec/mongoid/interceptable_spec_models.rb +76 -0
- data/spec/mongoid/shardable_models.rb +61 -0
- data/spec/mongoid/shardable_spec.rb +69 -16
- data/spec/support/lite_constraints.rb +22 -0
- metadata +20 -12
- metadata.gz.sig +0 -0
@@ -5,27 +5,32 @@ namespace :db do
|
|
5
5
|
task :load_models do
|
6
6
|
end
|
7
7
|
|
8
|
-
desc "Create
|
8
|
+
desc "Create indexes specified in Mongoid models"
|
9
9
|
task :create_indexes => [:environment, :load_models] do
|
10
10
|
::Mongoid::Tasks::Database.create_indexes
|
11
11
|
end
|
12
12
|
|
13
|
-
desc "Remove indexes that exist in the database but
|
13
|
+
desc "Remove indexes that exist in the database but are not specified in Mongoid models"
|
14
14
|
task :remove_undefined_indexes => [:environment, :load_models] do
|
15
15
|
::Mongoid::Tasks::Database.remove_undefined_indexes
|
16
16
|
end
|
17
17
|
|
18
|
-
desc "Remove
|
18
|
+
desc "Remove indexes specified in Mongoid models"
|
19
19
|
task :remove_indexes => [:environment, :load_models] do
|
20
20
|
::Mongoid::Tasks::Database.remove_indexes
|
21
21
|
end
|
22
22
|
|
23
|
-
desc "
|
23
|
+
desc "Shard collections with shard keys specified in Mongoid models"
|
24
|
+
task :shard_collections => [:environment, :load_models] do
|
25
|
+
::Mongoid::Tasks::Database.shard_collections
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Drop the database of the default Mongoid client"
|
24
29
|
task :drop => :environment do
|
25
30
|
::Mongoid::Clients.default.database.drop
|
26
31
|
end
|
27
32
|
|
28
|
-
desc "Drop all
|
33
|
+
desc "Drop all non-system collections"
|
29
34
|
task :purge => :environment do
|
30
35
|
::Mongoid.purge!
|
31
36
|
end
|
@@ -108,6 +108,54 @@ module Mongoid
|
|
108
108
|
end.compact
|
109
109
|
end
|
110
110
|
|
111
|
+
# Shard collections for models that declare shard keys.
|
112
|
+
#
|
113
|
+
# Returns the model classes that have had their collections sharded,
|
114
|
+
# including model classes whose collections had already been sharded
|
115
|
+
# prior to the invocation of this method.
|
116
|
+
#
|
117
|
+
# @example Shard all collections
|
118
|
+
# Mongoid::Tasks::Database.shard_collections
|
119
|
+
#
|
120
|
+
# @return [ Array<Class> ] The sharded models
|
121
|
+
def shard_collections(models = ::Mongoid.models)
|
122
|
+
models.map do |model|
|
123
|
+
next if model.shard_config.nil?
|
124
|
+
|
125
|
+
if model.embedded? && !model.cyclic?
|
126
|
+
logger.warn("MONGOID: #{model} has shard config but is emdedded")
|
127
|
+
next
|
128
|
+
end
|
129
|
+
|
130
|
+
unless model.collection.cluster.sharded?
|
131
|
+
logger.warn("MONGOID: #{model} has shard config but is not persisted in a sharded cluster: #{model.collection.cluster.summary}")
|
132
|
+
next
|
133
|
+
end
|
134
|
+
|
135
|
+
# Database must exist in order to run collStats
|
136
|
+
model.collection.create
|
137
|
+
|
138
|
+
stats = model.collection.database.command(collStats: model.collection.name).first
|
139
|
+
if stats[:sharded]
|
140
|
+
logger.info("MONGOID: #{model.collection.namespace} is already sharded for #{model}")
|
141
|
+
next model
|
142
|
+
end
|
143
|
+
|
144
|
+
admin_db = model.collection.client.use(:admin).database
|
145
|
+
admin_db.command(enableSharding: model.collection.database.name)
|
146
|
+
begin
|
147
|
+
admin_db.command(shardCollection: model.collection.namespace, **model.shard_config)
|
148
|
+
rescue Mongo::Error::OperationFailure => e
|
149
|
+
logger.error("MONGOID: Failed to shard collection #{model.collection.namespace} for #{model}: #{e.class}: #{e}")
|
150
|
+
next
|
151
|
+
end
|
152
|
+
|
153
|
+
logger.info("MONGOID: Sharded collection #{model.collection.namespace} for #{model}")
|
154
|
+
|
155
|
+
model
|
156
|
+
end.compact
|
157
|
+
end
|
158
|
+
|
111
159
|
private
|
112
160
|
|
113
161
|
def logger
|
@@ -44,11 +44,13 @@ module Mongoid
|
|
44
44
|
end
|
45
45
|
|
46
46
|
class << self
|
47
|
+
extend Forwardable
|
47
48
|
|
48
49
|
def timeless_table
|
49
50
|
Thread.current['[mongoid]:timeless'] ||= Hash.new
|
50
51
|
end
|
51
|
-
|
52
|
+
|
53
|
+
def_delegators :timeless_table, :[]=, :[]
|
52
54
|
end
|
53
55
|
|
54
56
|
private
|
data/lib/mongoid/version.rb
CHANGED
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
require_relative '../mongoid/shardable_models'
|
6
|
+
|
7
|
+
describe 'Sharding helpers' do
|
8
|
+
require_topology :sharded
|
9
|
+
|
10
|
+
describe 'shard_collection rake task' do
|
11
|
+
let(:shard_collections) do
|
12
|
+
Mongoid::Tasks::Database.shard_collections([model_cls])
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:create_indexes) do
|
16
|
+
Mongoid::Tasks::Database.create_indexes([model_cls])
|
17
|
+
end
|
18
|
+
|
19
|
+
shared_examples_for 'shards collection' do
|
20
|
+
it 'returns the model class' do
|
21
|
+
shard_collections.should == [model_cls]
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'shards collection' do
|
25
|
+
shard_collections
|
26
|
+
|
27
|
+
stats = model_cls.collection.database.command(collStats: model_cls.collection.name).first
|
28
|
+
stats[:sharded].should be true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'simple model' do
|
33
|
+
let(:model_cls) { SmMovie }
|
34
|
+
|
35
|
+
before do
|
36
|
+
SmMovie.collection.drop
|
37
|
+
end
|
38
|
+
|
39
|
+
it_behaves_like 'shards collection'
|
40
|
+
|
41
|
+
it 'makes inserts work' do
|
42
|
+
shard_collections
|
43
|
+
create_indexes
|
44
|
+
|
45
|
+
SmMovie.create!(year: 2020)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when database does not exist' do
|
50
|
+
let(:model_cls) { SmMovie }
|
51
|
+
|
52
|
+
before do
|
53
|
+
SmMovie.collection.database.drop
|
54
|
+
end
|
55
|
+
|
56
|
+
it_behaves_like 'shards collection'
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when database exists but collection does not exist' do
|
60
|
+
let(:model_cls) { SmMovie }
|
61
|
+
|
62
|
+
before do
|
63
|
+
SmMovie.collection.database.drop
|
64
|
+
SmMovie.collection.create
|
65
|
+
SmMovie.collection.drop
|
66
|
+
end
|
67
|
+
|
68
|
+
it_behaves_like 'shards collection'
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'when collection is already sharded' do
|
72
|
+
let(:model_cls) { SmMovie }
|
73
|
+
|
74
|
+
before do
|
75
|
+
Mongoid::Tasks::Database.shard_collections([model_cls])
|
76
|
+
end
|
77
|
+
|
78
|
+
it_behaves_like 'shards collection'
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'when hashed shard key is given' do
|
82
|
+
let(:model_cls) { SmAssistant }
|
83
|
+
|
84
|
+
before do
|
85
|
+
SmAssistant.collection.drop
|
86
|
+
end
|
87
|
+
|
88
|
+
it_behaves_like 'shards collection'
|
89
|
+
|
90
|
+
it 'makes inserts work' do
|
91
|
+
shard_collections
|
92
|
+
create_indexes
|
93
|
+
|
94
|
+
SmAssistant.create!(gender: 'm')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'when shard configuration is invalid' do
|
99
|
+
let(:model_cls) { SmActor }
|
100
|
+
|
101
|
+
it 'returns empty array' do
|
102
|
+
shard_collections.should == []
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'does not shards collection' do
|
106
|
+
shard_collections
|
107
|
+
|
108
|
+
stats = model_cls.collection.database.command(collStats: model_cls.collection.name).first
|
109
|
+
stats[:sharded].should be false
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
context 'when models have no sharded configuration' do
|
115
|
+
let(:model_cls) { SmNotSharded }
|
116
|
+
|
117
|
+
before do
|
118
|
+
model_cls.shard_config.should be nil
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'returns empty array' do
|
122
|
+
shard_collections.should == []
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'does not shards collection' do
|
126
|
+
shard_collections
|
127
|
+
|
128
|
+
stats = model_cls.collection.database.command(collStats: model_cls.collection.name).first
|
129
|
+
stats[:sharded].should be false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/spec/lite_spec_helper.rb
CHANGED
@@ -15,6 +15,7 @@ require "mongo"
|
|
15
15
|
require 'pp'
|
16
16
|
|
17
17
|
require 'support/spec_config'
|
18
|
+
require 'support/lite_constraints'
|
18
19
|
|
19
20
|
unless SpecConfig.instance.ci?
|
20
21
|
begin
|
@@ -58,6 +59,8 @@ RSpec.configure do |config|
|
|
58
59
|
end
|
59
60
|
end
|
60
61
|
end
|
62
|
+
|
63
|
+
config.extend(LiteConstraints)
|
61
64
|
end
|
62
65
|
|
63
66
|
# require all shared examples
|
@@ -782,11 +782,7 @@ describe Mongoid::Association::Referenced::HasMany::Targets::Enumerable do
|
|
782
782
|
end
|
783
783
|
end
|
784
784
|
|
785
|
-
|
786
|
-
|
787
|
-
subject do
|
788
|
-
enumerable.public_send(subject_method)
|
789
|
-
end
|
785
|
+
describe "#first" do
|
790
786
|
|
791
787
|
let(:person) do
|
792
788
|
Person.create
|
@@ -810,8 +806,12 @@ describe Mongoid::Association::Referenced::HasMany::Targets::Enumerable do
|
|
810
806
|
Post.create(person_id: person.id)
|
811
807
|
end
|
812
808
|
|
809
|
+
let(:first) do
|
810
|
+
enumerable.first
|
811
|
+
end
|
812
|
+
|
813
813
|
it "returns the first unloaded doc" do
|
814
|
-
expect(
|
814
|
+
expect(first).to eq(post)
|
815
815
|
end
|
816
816
|
|
817
817
|
it "does not load the enumerable" do
|
@@ -820,7 +820,7 @@ describe Mongoid::Association::Referenced::HasMany::Targets::Enumerable do
|
|
820
820
|
|
821
821
|
it "receives query only once" do
|
822
822
|
expect(criteria).to receive(:first).once
|
823
|
-
|
823
|
+
first
|
824
824
|
end
|
825
825
|
end
|
826
826
|
|
@@ -838,10 +838,14 @@ describe Mongoid::Association::Referenced::HasMany::Targets::Enumerable do
|
|
838
838
|
enumerable << post_two
|
839
839
|
end
|
840
840
|
|
841
|
+
let(:first) do
|
842
|
+
enumerable.first
|
843
|
+
end
|
844
|
+
|
841
845
|
context "when a perviously persisted unloaded doc exists" do
|
842
846
|
|
843
847
|
it "returns the first added doc" do
|
844
|
-
expect(
|
848
|
+
expect(first).to eq(post)
|
845
849
|
end
|
846
850
|
|
847
851
|
it "does not load the enumerable" do
|
@@ -861,8 +865,12 @@ describe Mongoid::Association::Referenced::HasMany::Targets::Enumerable do
|
|
861
865
|
enumerable << post
|
862
866
|
end
|
863
867
|
|
868
|
+
let(:first) do
|
869
|
+
enumerable.first
|
870
|
+
end
|
871
|
+
|
864
872
|
it "returns the first loaded doc" do
|
865
|
-
expect(
|
873
|
+
expect(first).to eq(post)
|
866
874
|
end
|
867
875
|
|
868
876
|
it "does not load the enumerable" do
|
@@ -872,8 +880,12 @@ describe Mongoid::Association::Referenced::HasMany::Targets::Enumerable do
|
|
872
880
|
|
873
881
|
context "when unloaded and added are empty" do
|
874
882
|
|
883
|
+
let(:first) do
|
884
|
+
enumerable.first
|
885
|
+
end
|
886
|
+
|
875
887
|
it "returns nil" do
|
876
|
-
expect(
|
888
|
+
expect(first).to be_nil
|
877
889
|
end
|
878
890
|
|
879
891
|
it "does not load the enumerable" do
|
@@ -894,8 +906,12 @@ describe Mongoid::Association::Referenced::HasMany::Targets::Enumerable do
|
|
894
906
|
described_class.new([ post ])
|
895
907
|
end
|
896
908
|
|
909
|
+
let(:first) do
|
910
|
+
enumerable.first
|
911
|
+
end
|
912
|
+
|
897
913
|
it "returns the first loaded doc" do
|
898
|
-
expect(
|
914
|
+
expect(first).to eq(post)
|
899
915
|
end
|
900
916
|
end
|
901
917
|
|
@@ -913,8 +929,12 @@ describe Mongoid::Association::Referenced::HasMany::Targets::Enumerable do
|
|
913
929
|
enumerable << post
|
914
930
|
end
|
915
931
|
|
932
|
+
let(:first) do
|
933
|
+
enumerable.first
|
934
|
+
end
|
935
|
+
|
916
936
|
it "returns the first added doc" do
|
917
|
-
expect(
|
937
|
+
expect(first).to eq(post)
|
918
938
|
end
|
919
939
|
end
|
920
940
|
|
@@ -924,8 +944,12 @@ describe Mongoid::Association::Referenced::HasMany::Targets::Enumerable do
|
|
924
944
|
described_class.new([])
|
925
945
|
end
|
926
946
|
|
947
|
+
let(:first) do
|
948
|
+
enumerable.first
|
949
|
+
end
|
950
|
+
|
927
951
|
it "returns nil" do
|
928
|
-
expect(
|
952
|
+
expect(first).to be_nil
|
929
953
|
end
|
930
954
|
end
|
931
955
|
end
|
@@ -953,21 +977,9 @@ describe Mongoid::Association::Referenced::HasMany::Targets::Enumerable do
|
|
953
977
|
end
|
954
978
|
|
955
979
|
it 'does not use the sort on id' do
|
956
|
-
expect(enumerable.
|
980
|
+
expect(enumerable.first(id_sort: :none)).to eq(first_post)
|
957
981
|
end
|
958
982
|
end
|
959
|
-
end
|
960
|
-
|
961
|
-
describe "#first" do
|
962
|
-
let(:subject_method) do
|
963
|
-
:first
|
964
|
-
end
|
965
|
-
|
966
|
-
it_behaves_like 'first or one'
|
967
|
-
|
968
|
-
subject do
|
969
|
-
enumerable.public_send(subject_method)
|
970
|
-
end
|
971
983
|
|
972
984
|
context 'when the id_sort option is not provided' do
|
973
985
|
|
@@ -984,54 +996,15 @@ describe Mongoid::Association::Referenced::HasMany::Targets::Enumerable do
|
|
984
996
|
end
|
985
997
|
|
986
998
|
let!(:first_post) do
|
987
|
-
person.posts.create(
|
999
|
+
person.posts.create(title: "One")
|
988
1000
|
end
|
989
1001
|
|
990
1002
|
let!(:second_post) do
|
991
|
-
person.posts.create(
|
1003
|
+
person.posts.create(title: "Two")
|
992
1004
|
end
|
993
1005
|
|
994
1006
|
it 'uses the sort on id' do
|
995
|
-
expect(
|
996
|
-
end
|
997
|
-
end
|
998
|
-
end
|
999
|
-
|
1000
|
-
describe "#one" do
|
1001
|
-
let(:subject_method) do
|
1002
|
-
:one
|
1003
|
-
end
|
1004
|
-
|
1005
|
-
it_behaves_like 'first or one'
|
1006
|
-
|
1007
|
-
subject do
|
1008
|
-
enumerable.public_send(subject_method)
|
1009
|
-
end
|
1010
|
-
|
1011
|
-
context 'when the id_sort option is not provided' do
|
1012
|
-
|
1013
|
-
let(:person) do
|
1014
|
-
Person.create
|
1015
|
-
end
|
1016
|
-
|
1017
|
-
let(:criteria) do
|
1018
|
-
Post.where(person_id: person.id)
|
1019
|
-
end
|
1020
|
-
|
1021
|
-
let(:enumerable) do
|
1022
|
-
described_class.new(criteria)
|
1023
|
-
end
|
1024
|
-
|
1025
|
-
let!(:first_post) do
|
1026
|
-
person.posts.create(id: 'one', title: "One")
|
1027
|
-
end
|
1028
|
-
|
1029
|
-
let!(:second_post) do
|
1030
|
-
person.posts.create(id: 'atwo', title: "Two")
|
1031
|
-
end
|
1032
|
-
|
1033
|
-
it 'does not use the sort on id' do
|
1034
|
-
expect(subject).to eq(first_post)
|
1007
|
+
expect(enumerable.first).to eq(first_post)
|
1035
1008
|
end
|
1036
1009
|
end
|
1037
1010
|
end
|