Indexing on ListFields now works properly

This commit is contained in:
Harry Marr 2010-01-16 15:35:01 +00:00
parent f01add9ef5
commit 3357b55fbf
4 changed files with 44 additions and 12 deletions

View File

@ -11,6 +11,9 @@ class BaseField(object):
"""A base class for fields in a MongoDB document. Instances of this class """A base class for fields in a MongoDB document. Instances of this class
may be added to subclasses of `Document` to define a document's schema. may be added to subclasses of `Document` to define a document's schema.
""" """
# Fields may have _types inserted into indexes by default
_index_with_types = True
def __init__(self, name=None, required=False, default=None, unique=False, def __init__(self, name=None, required=False, default=None, unique=False,
unique_with=None, primary_key=False): unique_with=None, primary_key=False):
@ -172,8 +175,6 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
# Apply document-defined meta options # Apply document-defined meta options
meta.update(attrs.get('meta', {})) meta.update(attrs.get('meta', {}))
meta['indexes'] += base_indexes
# Only simple classes - direct subclasses of Document - may set # Only simple classes - direct subclasses of Document - may set
# allow_inheritance to False # allow_inheritance to False
if not simple_class and not meta['allow_inheritance']: if not simple_class and not meta['allow_inheritance']:
@ -186,6 +187,10 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
new_class = super_new(cls, name, bases, attrs) new_class = super_new(cls, name, bases, attrs)
new_class.objects = QuerySetManager() new_class.objects = QuerySetManager()
user_indexes = [QuerySet._build_index_spec(new_class, spec)
for spec in meta['indexes']] + base_indexes
new_class._meta['indexes'] = user_indexes
unique_indexes = [] unique_indexes = []
for field_name, field in new_class._fields.items(): for field_name, field in new_class._fields.items():
# Generate a list of indexes needed by uniqueness constraints # Generate a list of indexes needed by uniqueness constraints

View File

@ -142,6 +142,9 @@ class ListField(BaseField):
of the field to be used as a list in the database. of the field to be used as a list in the database.
""" """
# ListFields cannot be indexed with _types - MongoDB doesn't support this
_index_with_types = False
def __init__(self, field, **kwargs): def __init__(self, field, **kwargs):
if not isinstance(field, BaseField): if not isinstance(field, BaseField):
raise ValidationError('Argument to ListField constructor must be ' raise ValidationError('Argument to ListField constructor must be '

View File

@ -126,14 +126,19 @@ class QuerySet(object):
construct a multi-field index); keys may be prefixed with a **+** construct a multi-field index); keys may be prefixed with a **+**
or a **-** to determine the index ordering or a **-** to determine the index ordering
""" """
index_list = QuerySet._build_index_spec(self._document, key_or_list)
self._collection.ensure_index(index_list)
return self
@classmethod
def _build_index_spec(cls, doc_cls, key_or_list):
"""Build a PyMongo index spec from a MongoEngine index spec.
"""
if isinstance(key_or_list, basestring): if isinstance(key_or_list, basestring):
key_or_list = [key_or_list] key_or_list = [key_or_list]
index_list = [] index_list = []
# If _types is being used, prepend it to every specified index use_types = doc_cls._meta.get('allow_inheritance', True)
if self._document._meta.get('allow_inheritance'):
index_list.append(('_types', 1))
for key in key_or_list: for key in key_or_list:
# Get direction from + or - # Get direction from + or -
direction = pymongo.ASCENDING direction = pymongo.ASCENDING
@ -141,11 +146,24 @@ class QuerySet(object):
direction = pymongo.DESCENDING direction = pymongo.DESCENDING
if key.startswith(("+", "-")): if key.startswith(("+", "-")):
key = key[1:] key = key[1:]
# Use real field name
key = QuerySet._translate_field_name(self._document, key) # Use real field name, do it manually because we need field
# objects for the next part (list field checking)
parts = key.split('.')
fields = QuerySet._lookup_field(doc_cls, parts)
parts = [field.name for field in fields]
key = '.'.join(parts)
index_list.append((key, direction)) index_list.append((key, direction))
self._collection.ensure_index(index_list)
return self # Check if a list field is being used, don't use _types if it is
if use_types and not all(f._index_with_types for f in fields):
use_types = False
# If _types is being used, prepend it to every specified index
if doc_cls._meta.get('allow_inheritance') and use_types:
index_list.insert(0, ('_types', 1))
return index_list
def __call__(self, *q_objs, **query): def __call__(self, *q_objs, **query):
"""Filter the selected documents by calling the """Filter the selected documents by calling the
@ -177,7 +195,8 @@ class QuerySet(object):
# Ensure document-defined indexes are created # Ensure document-defined indexes are created
if self._document._meta['indexes']: if self._document._meta['indexes']:
for key_or_list in self._document._meta['indexes']: for key_or_list in self._document._meta['indexes']:
self.ensure_index(key_or_list) #self.ensure_index(key_or_list)
self._collection.ensure_index(key_or_list)
# Ensure indexes created by uniqueness constraints # Ensure indexes created by uniqueness constraints
for index in self._document._meta['unique_indexes']: for index in self._document._meta['unique_indexes']:

View File

@ -227,9 +227,11 @@ class DocumentTest(unittest.TestCase):
class BlogPost(Document): class BlogPost(Document):
date = DateTimeField(name='addDate', default=datetime.datetime.now) date = DateTimeField(name='addDate', default=datetime.datetime.now)
category = StringField() category = StringField()
tags = ListField(StringField())
meta = { meta = {
'indexes': [ 'indexes': [
'-date', '-date',
'tags',
('category', '-date') ('category', '-date')
], ],
} }
@ -237,7 +239,8 @@ class DocumentTest(unittest.TestCase):
BlogPost.drop_collection() BlogPost.drop_collection()
info = BlogPost.objects._collection.index_information() info = BlogPost.objects._collection.index_information()
self.assertEqual(len(info), 4) # _id, types, '-date', ('cat', 'date') # _id, types, '-date', 'tags', ('cat', 'date')
self.assertEqual(len(info), 5)
# Indexes are lazy so use list() to perform query # Indexes are lazy so use list() to perform query
list(BlogPost.objects) list(BlogPost.objects)
@ -245,6 +248,8 @@ class DocumentTest(unittest.TestCase):
self.assertTrue([('_types', 1), ('category', 1), ('addDate', -1)] self.assertTrue([('_types', 1), ('category', 1), ('addDate', -1)]
in info.values()) in info.values())
self.assertTrue([('_types', 1), ('addDate', -1)] in info.values()) self.assertTrue([('_types', 1), ('addDate', -1)] in info.values())
# tags is a list field so it shouldn't have _types in the index
self.assertTrue([('tags', 1)] in info.values())
class ExtendedBlogPost(BlogPost): class ExtendedBlogPost(BlogPost):
title = StringField() title = StringField()