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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE +1 -1
  5. data/README.md +4 -4
  6. data/lib/mongoid.rb +3 -2
  7. data/lib/mongoid/association/many.rb +3 -2
  8. data/lib/mongoid/association/proxy.rb +5 -3
  9. data/lib/mongoid/association/referenced/has_many/enumerable.rb +2 -22
  10. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -2
  11. data/lib/mongoid/attributes.rb +28 -20
  12. data/lib/mongoid/config.rb +3 -3
  13. data/lib/mongoid/config/options.rb +5 -2
  14. data/lib/mongoid/contextual.rb +5 -4
  15. data/lib/mongoid/contextual/geo_near.rb +3 -2
  16. data/lib/mongoid/contextual/map_reduce.rb +3 -2
  17. data/lib/mongoid/contextual/mongo.rb +2 -1
  18. data/lib/mongoid/fields/standard.rb +2 -1
  19. data/lib/mongoid/findable.rb +5 -4
  20. data/lib/mongoid/interceptable.rb +5 -1
  21. data/lib/mongoid/railties/database.rake +7 -0
  22. data/lib/mongoid/serializable.rb +3 -1
  23. data/lib/mongoid/shardable.rb +56 -4
  24. data/lib/mongoid/tasks/database.rake +10 -5
  25. data/lib/mongoid/tasks/database.rb +48 -0
  26. data/lib/mongoid/timestamps/timeless.rb +3 -1
  27. data/lib/mongoid/version.rb +1 -1
  28. data/spec/integration/shardable_spec.rb +133 -0
  29. data/spec/lite_spec_helper.rb +3 -0
  30. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +41 -68
  31. data/spec/mongoid/attributes_spec.rb +19 -7
  32. data/spec/mongoid/changeable_spec.rb +23 -0
  33. data/spec/mongoid/document_fields_spec.rb +29 -0
  34. data/spec/mongoid/interceptable_spec.rb +62 -0
  35. data/spec/mongoid/interceptable_spec_models.rb +76 -0
  36. data/spec/mongoid/shardable_models.rb +61 -0
  37. data/spec/mongoid/shardable_spec.rb +69 -16
  38. data/spec/support/lite_constraints.rb +22 -0
  39. metadata +20 -12
  40. 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 the indexes defined on your mongoid models"
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 aren't specified on the models"
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 the indexes defined on your mongoid models without questions!"
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 "Drops the default client database"
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 collections except the system collections"
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
- delegate :[]=, :[], to: :timeless_table
52
+
53
+ def_delegators :timeless_table, :[]=, :[]
52
54
  end
53
55
 
54
56
  private
@@ -2,5 +2,5 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  module Mongoid
5
- VERSION = "7.1.0.rc0"
5
+ VERSION = "7.1.0"
6
6
  end
@@ -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
@@ -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
- shared_examples 'first or one' do
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(subject).to eq(post)
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
- subject
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(subject).to eq(post)
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(subject).to eq(post)
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(subject).to be_nil
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(subject).to eq(post)
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(subject).to eq(post)
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(subject).to be_nil
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.send(subject_method, id_sort: :none)).to eq(first_post)
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(id: 'one', title: "One")
999
+ person.posts.create(title: "One")
988
1000
  end
989
1001
 
990
1002
  let!(:second_post) do
991
- person.posts.create(id: 'atwo', title: "Two")
1003
+ person.posts.create(title: "Two")
992
1004
  end
993
1005
 
994
1006
  it 'uses the sort on id' do
995
- expect(subject).to eq(second_post)
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