Module: Mongoid::Tasks::Database

Extended by:
Database
Included in:
Database
Defined in:
lib/mongoid/tasks/database.rb

Overview

Utility module to manage database collections, indexes, sharding, etc. Invoked from Rake tasks.

Instance Method Summary collapse

Instance Method Details

#create_collections(models = ::Mongoid.models, force: false) ⇒ Object

Create collections for each model given the provided globs and the class is not embedded.

Parameters:

  • models. (Array<Mongoid::Document>)

    Array of document classes for which collections should be created. Defaulted to all document classes in the application.

  • force (true | false) (defaults to: false)

    If true, the method will drop existing collections before creating new ones. If false, the method will create only new collection (that do not exist in the database).



19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/mongoid/tasks/database.rb', line 19

def create_collections(models = ::Mongoid.models, force: false)
  models.each do |model|
    if !model.embedded? || model.cyclic?
      model.create_collection(force: force)
      logger.info("MONGOID: Created collection for #{model}:")
    else
      logger.info("MONGOID: collection options ignored on: #{model}, please define in the root model.")
    end
  rescue Exception
    logger.error "error while creating collection for #{model}"
    raise
  end
end

#create_indexes(models = ::Mongoid.models) ⇒ Array<Class>

Create indexes for each model given the provided globs and the class is not embedded.

Examples:

Create all the indexes.

Mongoid::Tasks::Database.create_indexes

Returns:

  • (Array<Class>)

    The indexed models.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/mongoid/tasks/database.rb', line 40

def create_indexes(models = ::Mongoid.models)
  models.each do |model|
    next if model.index_specifications.empty?

    if !model.embedded? || model.cyclic?
      model.create_indexes
      logger.info("MONGOID: Created indexes on #{model}:")
      model.index_specifications.each do |spec|
        logger.info("MONGOID: Index: #{spec.key}, Options: #{spec.options}")
      end
      model
    else
      logger.info("MONGOID: Index ignored on: #{model}, please define in the root model.")
      nil
    end
  end.compact
end

#create_search_indexes(models = ::Mongoid.models, wait: true) ⇒ Object

Submit requests for the search indexes to be created. This will happen asynchronously. If "wait" is true, the method will block while it waits for the indexes to be created.

Parameters:

  • models (Array<Mongoid::Document>) (defaults to: ::Mongoid.models)

    the models to build search indexes for.

  • wait (true | false) (defaults to: true)

    whether to wait for the indexes to be built.



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/mongoid/tasks/database.rb', line 66

def create_search_indexes(models = ::Mongoid.models, wait: true)
  searchable = models.select { |m| m.search_index_specs.any? }

  # queue up the search index creation requests
  index_names_by_model = searchable.each_with_object({}) do |model, obj|
    logger.info("MONGOID: Creating search indexes on #{model}...")
    obj[model] = model.create_search_indexes
  end

  wait_for_search_indexes(index_names_by_model) if wait
end

#remove_indexes(models = ::Mongoid.models) ⇒ Array<Class>

Remove indexes for each model given the provided globs and the class is not embedded.

Examples:

Remove all the indexes.

Mongoid::Tasks::Database.remove_indexes

Returns:

  • (Array<Class>)

    The un-indexed models.



147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/mongoid/tasks/database.rb', line 147

def remove_indexes(models = ::Mongoid.models)
  models.each do |model|
    next if model.embedded?

    begin
      model.remove_indexes
    rescue Mongo::Error::OperationFailure
      next
    end
    model
  end.compact
end

#remove_search_indexes(models = ::Mongoid.models) ⇒ Object

Remove all search indexes from the given models.



164
165
166
167
168
169
170
# File 'lib/mongoid/tasks/database.rb', line 164

def remove_search_indexes(models = ::Mongoid.models)
  models.each do |model|
    next if model.embedded?

    model.remove_search_indexes
  end
end

#remove_undefined_indexes(models = ::Mongoid.models) ⇒ Hash{Class => Array(Hash)}

Remove indexes that exist in the database but aren't specified on the models.

Examples:

Remove undefined indexes.

Mongoid::Tasks::Database.remove_undefined_indexes

Returns:

  • (Hash{Class => Array(Hash)})

    The list of indexes that were removed by model.



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/mongoid/tasks/database.rb', line 124

def remove_undefined_indexes(models = ::Mongoid.models)
  Threaded.with_collection_management do
    undefined_indexes(models).each do |model, indexes|
      indexes.each do |index|
        key = index['key'].symbolize_keys
        collection = model.collection
        collection.indexes(session: model.send(:_session)).drop_one(key)
        logger.info(
          "MONGOID: Removed index '#{index['name']}' on collection " +
          "'#{collection.name}' in database '#{collection.database.name}'."
        )
      end
    end
  end
end

#shard_collections(models = ::Mongoid.models) ⇒ Array<Class>

Shard collections for models that declare shard keys.

Returns the model classes that have had their collections sharded, including model classes whose collections had already been sharded prior to the invocation of this method.

Examples:

Shard all collections

Mongoid::Tasks::Database.shard_collections

Returns:

  • (Array<Class>)

    The sharded models



182
183
184
185
186
# File 'lib/mongoid/tasks/database.rb', line 182

def shard_collections(models = ::Mongoid.models)
  Threaded.with_collection_management do
    shard_collections_for(models)
  end
end

#shard_collections_for(models) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/mongoid/tasks/database.rb', line 188

def shard_collections_for(models)
  models.map do |model|
    next if model.shard_config.nil?

    if model.embedded? && !model.cyclic?
      logger.warn("MONGOID: #{model} has shard config but is embedded")
      next
    end

    unless model.collection.cluster.sharded? || model.collection.cluster.load_balanced?
      logger.warn("MONGOID: #{model} has shard config but is not persisted in a sharded cluster: #{model.collection.cluster.summary}")
      next
    end

    # Database of the collection must exist in order to run collStats.
    # Depending on server version, the collection itself must also
    # exist.
    # MongoDB does not have a command to create the database; the best
    # approximation of it is to create the collection we want.
    # On older servers, creating a collection that already exists is
    # an error.
    # Additionally, 3.6 and potentially older servers do not provide
    # the error code when they are asked to collStats a non-existent
    # collection (https://jira.mongodb.org/browse/SERVER-50070).
    begin
      model.collection.database.command(collStats: model.collection.name).first
    rescue Mongo::Error::OperationFailure => e
      # Code 26 is database does not exist.
      # Code 8 is collection does not exist, as of 4.0.
      # On 3.6 and earlier match the text of exception message.
      if e.code == 26 || e.code == 8 ||
         (e.code.nil? && e.message =~ /not found/)
        model.collection.create

        model.collection.database.command(collStats: model.collection.name).first
      else
        raise
      end
    end

    stats = model.collection.database.command(collStats: model.collection.name).first
    if stats[:sharded]
      logger.info("MONGOID: #{model.collection.namespace} is already sharded for #{model}")
      next model
    end

    admin_db = model.collection.client.use(:admin).database

    begin
      admin_db.command(enableSharding: model.collection.database.name)
    rescue Mongo::Error::OperationFailure => e
      # Server 2.6 fails if sharding is already enabled
      raise unless e.code == 23 || (e.code.nil? && e.message =~ /already enabled/)
      # Nothing
    end

    begin
      admin_db.command(shardCollection: model.collection.namespace, **model.shard_config)
    rescue Mongo::Error::OperationFailure => e
      logger.error("MONGOID: Failed to shard collection #{model.collection.namespace} for #{model}: #{e.class}: #{e}")
      next
    end

    logger.info("MONGOID: Sharded collection #{model.collection.namespace} for #{model}")

    model
  end.compact
end

#undefined_indexes(models = ::Mongoid.models) ⇒ Array<Hash>

Return the list of indexes by model that exist in the database but aren't specified on the models.

Examples:

Return the list of unused indexes.

Mongoid::Tasks::Database.undefined_indexes

Returns:

  • (Array<Hash>)

    The list of undefined indexes by model.



85
86
87
88
89
# File 'lib/mongoid/tasks/database.rb', line 85

def undefined_indexes(models = ::Mongoid.models)
  Threaded.with_collection_management do
    undefined_indexes_for(models)
  end
end

#undefined_indexes_for(models) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/mongoid/tasks/database.rb', line 91

def undefined_indexes_for(models)
  undefined_by_model = {}

  models.each do |model|
    next if model.embedded?

    begin
      model.collection.indexes(session: model.send(:_session)).each do |index|
        # ignore default index
        next if index['name'] == '_id_'

        key = index['key'].symbolize_keys
        spec = model.index_specification(key, index['name'])
        next if spec

        # index not specified
        undefined_by_model[model] ||= []
        undefined_by_model[model] << index
      end
    rescue Mongo::Error::OperationFailure
    end
  end

  undefined_by_model
end