umutbozkurt/django-rest-framework-mongoengine

UniqueValidator is resulting in regex query rather than an equality check

bazinga012 opened this issue · 2 comments

UniqueValidator is resulting in regex queries which is impacting performace.

Consider the following situation:

from mongoengine import Document
class MyModel(Document):
	my_field = StringField(required=True, unique=True)

from rest_framework_mongoengine import serializers
class MyModelSerializer(serializers.DocumentSerializer):
	class Meta:
    	        model = MyModel
    	        fields = '__all__'

MyModelSerializer(data=payload).is_valid()

This call to is_valid() will result in query for checking uniqueness of my_field by the UniqueValidator with lookup value being "exact".

class UniqueValidator(object):
    """
    Validator that corresponds to `unique=True` on a model field.

    Should be applied to an individual field on the serializer.
    """
    message = _('This field must be unique.')

    def __init__(self, queryset, message=None, lookup='exact'):
        self.queryset = queryset
        self.serializer_field = None
        self.message = message or self.message
        self.lookup = lookup

for "exact" lookup mongoengine generates a regex query(as shown below)

mongoengine/fields.py

def prepare_query_value(self, op, value):
    if not isinstance(op, six.string_types):
        return value

    if op.lstrip('i') in ('startswith', 'endswith', 'contains'):
        flags = 0
        if op.startswith('i'):
            flags = re.IGNORECASE
            op = op.lstrip('i')

        regex = r'%s'
        if op == 'startswith':
            regex = r'^%s'
        elif op == 'endswith':
            regex = r'%s$'
        elif op == 'exact':
            regex = r'^%s$'  

so query will be of the form: find({"field_name": /^<value_from_payload>$/})
This regex is resulting in performance hit.

If I override the constructor in rest_framework_mongoengine

class UniqueValidator(MongoValidatorMixin, validators.UniqueValidator):    
    def __init__(self, queryset, message=None, lookup=''):
        return super(UniqueValidator, self).__init__(queryset, message, lookup) 

This results in find query with equality check(rather than regex).

Can we have this change or is there any other way to achieve the equality query rather than a regex query?

@bazinga012

Hi! I'm sorry, I don't have time even for updating for breaking changes, not speaking about performance.

If you decide to modify this on your own, pull requests are welcome.