diff --git a/mongoengine/document.py b/mongoengine/document.py index 4b5506f4..fdf9d28f 100644 --- a/mongoengine/document.py +++ b/mongoengine/document.py @@ -74,6 +74,11 @@ class Document(BaseDocument): names. Index direction may be specified by prefixing the field names with a **+** or **-** sign. + Index creation can be disabled by specifying :attr:`index_allow_creation` in + the :attr:`meta` dictionary. If this is set to True then indexes will not be + created by MongoEngine. This is useful in production systems where index + creation is performed as part of a deployment system. + By default, _types will be added to the start of every index (that doesn't contain a list) if allow_inheritence is True. This can be disabled by either setting types to False on the specific index or diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index 85a5152a..c9a5fb54 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -481,13 +481,60 @@ class QuerySet(object): """Returns all documents.""" return self.__call__() + def _ensure_indexes_exist(self): + background = self._document._meta.get('index_background', False) + drop_dups = self._document._meta.get('index_drop_dups', False) + index_opts = self._document._meta.get('index_options', {}) + index_types = self._document._meta.get('index_types', True) + + # determine if an index which we are creating includes + # _type as its first field; if so, we can avoid creating + # an extra index on _type, as mongodb will use the existing + # index to service queries against _type + types_indexed = False + def includes_types(fields): + first_field = None + if len(fields): + if isinstance(fields[0], basestring): + first_field = fields[0] + elif isinstance(fields[0], (list, tuple)) and len(fields[0]): + first_field = fields[0][0] + return first_field == '_types' + + # Ensure indexes created by uniqueness constraints + for index in self._document._meta['unique_indexes']: + types_indexed = types_indexed or includes_types(index) + self._collection.ensure_index(index, unique=True, + background=background, drop_dups=drop_dups, **index_opts) + + # Ensure document-defined indexes are created + if self._document._meta['indexes']: + for spec in self._document._meta['indexes']: + types_indexed = types_indexed or includes_types(spec['fields']) + opts = index_opts.copy() + opts['unique'] = spec.get('unique', False) + opts['sparse'] = spec.get('sparse', False) + self._collection.ensure_index(spec['fields'], + background=background, **opts) + + # If _types is being used (for polymorphism), it needs an index, + # only if another index doesn't begin with _types + if index_types and '_types' in self._query and not types_indexed: + self._collection.ensure_index('_types', + background=background, **index_opts) + + # Add geo indicies + for field in self._document._geo_indices(): + index_spec = [(field.db_field, pymongo.GEO2D)] + self._collection.ensure_index(index_spec, + background=background, **index_opts) + @property def _collection(self): """Property that returns the collection object. This allows us to perform operations only if the collection is accessed. """ if self._document not in QuerySet.__already_indexed: - # Ensure collection exists db = self._document._get_db() if self._collection_obj.name not in db.collection_names(): @@ -496,52 +543,8 @@ class QuerySet(object): QuerySet.__already_indexed.add(self._document) - background = self._document._meta.get('index_background', False) - drop_dups = self._document._meta.get('index_drop_dups', False) - index_opts = self._document._meta.get('index_options', {}) - index_types = self._document._meta.get('index_types', True) - - # determine if an index which we are creating includes - # _type as its first field; if so, we can avoid creating - # an extra index on _type, as mongodb will use the existing - # index to service queries against _type - types_indexed = False - def includes_types(fields): - first_field = None - if len(fields): - if isinstance(fields[0], basestring): - first_field = fields[0] - elif isinstance(fields[0], (list, tuple)) and len(fields[0]): - first_field = fields[0][0] - return first_field == '_types' - - # Ensure indexes created by uniqueness constraints - for index in self._document._meta['unique_indexes']: - types_indexed = types_indexed or includes_types(index) - self._collection.ensure_index(index, unique=True, - background=background, drop_dups=drop_dups, **index_opts) - - # Ensure document-defined indexes are created - if self._document._meta['indexes']: - for spec in self._document._meta['indexes']: - types_indexed = types_indexed or includes_types(spec['fields']) - opts = index_opts.copy() - opts['unique'] = spec.get('unique', False) - opts['sparse'] = spec.get('sparse', False) - self._collection.ensure_index(spec['fields'], - background=background, **opts) - - # If _types is being used (for polymorphism), it needs an index, - # only if another index doesn't begin with _types - if index_types and '_types' in self._query and not types_indexed: - self._collection.ensure_index('_types', - background=background, **index_opts) - - # Add geo indicies - for field in self._document._geo_indices(): - index_spec = [(field.db_field, pymongo.GEO2D)] - self._collection.ensure_index(index_spec, - background=background, **index_opts) + if self._document._meta.get('index_allow_creation', True): + self._ensure_indexes_exist() return self._collection_obj diff --git a/tests/document.py b/tests/document.py index 910a78fc..c88164eb 100644 --- a/tests/document.py +++ b/tests/document.py @@ -740,6 +740,28 @@ class DocumentTest(unittest.TestCase): self.assertEqual(info.keys(), ['_types_1_user_guid_1', '_id_', '_types_1_name_1']) Person.drop_collection() + def test_disable_index_creation(self): + """Tests setting allow_index_creation to False on the connection will + disable any index generation. + """ + class User(Document): + meta = { + 'indexes': ['user_guid'], + 'index_allow_creation': False + } + user_guid = StringField(required=True) + + + User.drop_collection() + + u = User(user_guid='123') + u.save() + + self.assertEquals(1, User.objects.count()) + info = User.objects._collection.index_information() + self.assertEqual(info.keys(), ['_id_']) + User.drop_collection() + def test_embedded_document_index(self): """Tests settings an index on an embedded document """