Commits

Waldemar Kornewald  committed b9d8d72

implemented startswith queries using the unicode trick from GAE's documentation. you'll need a composite index because it uses inequality filters, so it might not be practical in all cases

  • Participants
  • Parent commits 1760dcd

Comments (0)

Files changed (4)

 #    def value_to_db_decimal(self, value, max_digits, decimal_places):
 #        return
 
+    def prep_for_like_query(self, value):
+        return value
+
     def check_aggregate_support(self, aggregate):
         # TODO: Only COUNT(*) should be supported. Raise NotImplementedError
         pass

File db/compiler.py

                 else:
                     value = value[0]
 
+            if lookup_type == 'startswith':
+                value = value[:-1]
+
             # Emulated/converted lookups
             if is_primary_key:
                 column = '__key__'
                         "columns (here: %r and %r)" % (self.inequality_field, column))
                 self.inequality_field = column
             elif lookup_type == 'startswith':
-                if not isinstance(value, unicode):
-                    value = value.decode('utf8')
                 op = '>='
                 query["%s %s" % (column, op)] = self.convert_value_for_db(
                     db_type, value)
                 op = '<='
-                value += u'\ufffd'
+                if isinstance(value, str):
+                    value = value.decode('utf8')
+                if isinstance(value, Key):
+                    value = list(value.to_path())
+                    if isinstance(value[-1], str):
+                        value[-1] = value[-1].decode('utf8')
+                    value[-1] += u'\ufffd'
+                    value = Key.from_path(*value)
+                else:
+                    value += u'\ufffd'
                 query["%s %s" % (column, op)] = self.convert_value_for_db(
                     db_type, value)
                 continue

File tests/filter.py

-from .testmodels import FieldsWithOptionsModel, OrderedModel
+from .testmodels import FieldsWithOptionsModel, EmailModel, OrderedModel
 import datetime
 from django.test import TestCase
 from django.db.models import Q
-from google.appengine.api.datastore_errors import BadArgumentError, BadFilterError
+from google.appengine.api.datastore_errors import BadArgumentError, \
+    BadFilterError
 
 class FilterTest(TestCase):
     floats = [5.3, 2.6, 9.1, 1.58]
             self.last_save_time = datetime.datetime.now().time()
             ordered_instance = OrderedModel(priority=index, pk=index + 1)
             ordered_instance.save()
-            model = FieldsWithOptionsModel(floating_point=float,
-                                           integer=int(float), email=email,
-                                           time=self.last_save_time,
-                                           foreign_key=ordered_instance)
-            model.save()
+            FieldsWithOptionsModel(floating_point=float,
+                                   integer=int(float), email=email,
+                                   time=self.last_save_time,
+                                   foreign_key=ordered_instance).save()
+            EmailModel(email=email).save()
+
+    def test_startswith(self):
+        self.assertEquals([entity.email for entity in
+                          FieldsWithOptionsModel.objects.filter(
+                          email__startswith='r').order_by('email')],
+                          ['rasengan@naruto.com', 'rinnengan@sage.de'])
+        self.assertEquals([entity.email for entity in
+                          EmailModel.objects.filter(
+                          email__startswith='r').order_by('email')],
+                          ['rasengan@naruto.com', 'rinnengan@sage.de'])
 
     def test_gt(self):
         # test gt on float
-        self.assertEquals([entity.floating_point for entity in \
+        self.assertEquals([entity.floating_point for entity in
                           FieldsWithOptionsModel.objects.filter(
                           floating_point__gt=3.1).order_by('floating_point')],
                           [5.3, 9.1])
 
         # test gt on integer
-        self.assertEquals([entity.integer for entity in \
+        self.assertEquals([entity.integer for entity in
                           FieldsWithOptionsModel.objects.filter(
                           integer__gt=3).order_by('integer')],
                           [5, 9])
 
         # test filter on primary_key field
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                           FieldsWithOptionsModel.objects.filter(email__gt='as').
                           order_by('email')], ['rasengan@naruto.com',
                           'rinnengan@sage.de', 'sharingan@uchias.com', ])
 
         # test ForeignKeys with id
-        self.assertEquals(sorted([entity.email for entity in \
+        self.assertEquals(sorted([entity.email for entity in
                             FieldsWithOptionsModel.objects.filter(
                             foreign_key__gt=2)]),
                             ['rasengan@naruto.com', 'rinnengan@sage.de', ])
 
         # and with instance
         ordered_instance = OrderedModel.objects.get(priority=1)
-        self.assertEquals(sorted([entity.email for entity in \
+        self.assertEquals(sorted([entity.email for entity in
                             FieldsWithOptionsModel.objects.filter(
                             foreign_key__gt=ordered_instance)]),
                             ['rasengan@naruto.com', 'rinnengan@sage.de', ])
 
     def test_lt(self):
         # test lt on float
-        self.assertEquals([entity.floating_point for entity in \
+        self.assertEquals([entity.floating_point for entity in
                           FieldsWithOptionsModel.objects.filter(
                           floating_point__lt=3.1).order_by('floating_point')],
                           [1.58, 2.6])
 
         # test lt on integer
-        self.assertEquals([entity.integer for entity in \
+        self.assertEquals([entity.integer for entity in
                           FieldsWithOptionsModel.objects.filter(
                           integer__lt=3).order_by('integer')],
                           [1, 2])
 
         # test filter on primary_key field
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                           FieldsWithOptionsModel.objects.filter(email__lt='as').
                           order_by('email')], ['app-engine@scholardocs.com', ])
 
          # filter on datetime
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                             FieldsWithOptionsModel.objects.filter(
                             time__lt=self.last_save_time).order_by('time')],
                             ['app-engine@scholardocs.com', 'sharingan@uchias.com',
                             'rinnengan@sage.de',])
 
         # test ForeignKeys with id
-        self.assertEquals(sorted([entity.email for entity in \
+        self.assertEquals(sorted([entity.email for entity in
                             FieldsWithOptionsModel.objects.filter(
                             foreign_key__lt=3)]),
                             ['app-engine@scholardocs.com', 'sharingan@uchias.com'])
 
         # and with instance
         ordered_instance = OrderedModel.objects.get(priority=2)
-        self.assertEquals(sorted([entity.email for entity in \
+        self.assertEquals(sorted([entity.email for entity in
                             FieldsWithOptionsModel.objects.filter(
                             foreign_key__lt=ordered_instance)]),
                             ['app-engine@scholardocs.com', 'sharingan@uchias.com'])
 
     def test_gte(self):
         # test gte on float
-        self.assertEquals([entity.floating_point for entity in \
+        self.assertEquals([entity.floating_point for entity in
                           FieldsWithOptionsModel.objects.filter(
                           floating_point__gte=2.6).order_by('floating_point')],
                           [2.6, 5.3, 9.1])
 
         # test gte on integer
-        self.assertEquals([entity.integer for entity in \
+        self.assertEquals([entity.integer for entity in
                           FieldsWithOptionsModel.objects.filter(
                           integer__gte=2).order_by('integer')],
                           [2, 5, 9])
 
         # test filter on primary_key field
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                           FieldsWithOptionsModel.objects.filter(
                           email__gte='rinnengan@sage.de').order_by('email')],
                           ['rinnengan@sage.de', 'sharingan@uchias.com', ])
 
     def test_lte(self):
         # test lte on float
-        self.assertEquals([entity.floating_point for entity in \
+        self.assertEquals([entity.floating_point for entity in
                           FieldsWithOptionsModel.objects.filter(
                           floating_point__lte=5.3).order_by('floating_point')],
                           [1.58, 2.6, 5.3])
 
         # test lte on integer
-        self.assertEquals([entity.integer for entity in \
+        self.assertEquals([entity.integer for entity in
                           FieldsWithOptionsModel.objects.filter(
                           integer__lte=5).order_by('integer')],
                           [1, 2, 5])
 
         # test filter on primary_key field
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                           FieldsWithOptionsModel.objects.filter(
                           email__lte='rinnengan@sage.de').order_by('email')],
                           ['app-engine@scholardocs.com', 'rasengan@naruto.com',
 
     def test_equals(self):
         # test equality filter on primary_key field
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                           FieldsWithOptionsModel.objects.filter(
                           email='rinnengan@sage.de').order_by('email')],
                           ['rinnengan@sage.de'])
 
 
     def test_exclude(self):
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                             FieldsWithOptionsModel.objects.all().exclude(
                             floating_point__lt=9.1).order_by('floating_point')],
                             ['rinnengan@sage.de', ])
 
         # test exclude with foreignKey
         ordered_instance = OrderedModel.objects.get(priority=1)
-        self.assertEquals(sorted([entity.email for entity in \
+        self.assertEquals(sorted([entity.email for entity in
                             FieldsWithOptionsModel.objects.all().exclude(
                             foreign_key__gt=ordered_instance)]),
                             ['app-engine@scholardocs.com', 'sharingan@uchias.com',])
 
         # test across multiple columns. On app engine only one filter is allowed
         # to be an inequality filter
-        self.assertEquals([(entity.floating_point, entity.integer) for entity in \
+        self.assertEquals([(entity.floating_point, entity.integer) for entity in
                           FieldsWithOptionsModel.objects.filter(
                           floating_point__lte=5.3).filter(integer=2).order_by(
                           'floating_point')], [(2.6, 2), ])
 
         # test multiple filters including the primary_key field
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                           FieldsWithOptionsModel.objects.filter(
                           email__gte='rinnengan@sage.de').filter(integer=2).order_by(
                           'email')], ['sharingan@uchias.com', ])
 
         # test in filter on primary key with another arbitrary filter
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                           FieldsWithOptionsModel.objects.filter(
                           email__in=['rinnengan@sage.de',
                           'sharingan@uchias.com']).filter(integer__gt=2).order_by(
 
     def test_slicing(self):
         # test slicing on filter with primary_key
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                           FieldsWithOptionsModel.objects.filter(
                           email__lte='rinnengan@sage.de').order_by('email')[:2]],
                           ['app-engine@scholardocs.com', 'rasengan@naruto.com', ])
 
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                           FieldsWithOptionsModel.objects.filter(
                           email__lte='rinnengan@sage.de').order_by('email')[1:2]],
                           ['rasengan@naruto.com', ])
 
         # test on non pk field
-        self.assertEquals([entity.integer for entity in \
+        self.assertEquals([entity.integer for entity in
                           FieldsWithOptionsModel.objects.all().order_by(
                           'integer')[:2]], [1, 2, ])
 
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                           FieldsWithOptionsModel.objects.all().order_by(
                             'email')[::2]],
                           ['app-engine@scholardocs.com', 'rinnengan@sage.de',])
 
     def test_Q_objects(self):
-        self.assertEquals([entity.email for entity in \
+        self.assertEquals([entity.email for entity in
                           FieldsWithOptionsModel.objects.filter(
                             Q(email__lte='rinnengan@sage.de')).order_by('email')][:2],
                           ['app-engine@scholardocs.com', 'rasengan@naruto.com', ])
 
-        self.assertEquals([entity.integer for entity in \
+        self.assertEquals([entity.integer for entity in
                           FieldsWithOptionsModel.objects.exclude(Q(integer__lt=5) |
                             Q(integer__gte=9)).order_by('integer')],
                             [5, ])

File tests/testmodels.py

 from django.db import models
 
+class EmailModel(models.Model):
+    email = models.EmailField()
+
 class FieldsWithoutOptionsModel(models.Model):
     datetime = models.DateTimeField()
     date = models.DateField()