Skip to content

Commit 55d877b

Browse files
committed
Remove pattern matching support on non-string fields to improve performance of other queries
1 parent 0b46a50 commit 55d877b

File tree

6 files changed

+47
-2
lines changed

6 files changed

+47
-2
lines changed

django_mongodb_backend/features.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ class DatabaseFeatures(GISFeatures, BaseDatabaseFeatures):
6868
"model_fields.test_jsonfield.TestQuerying.test_icontains",
6969
# Unexpected alias_refcount in alias_map.
7070
"queries.tests.Queries1Tests.test_order_by_tables",
71+
# Regex lookup doesn't work on non-string fields.
72+
"lookup.tests.LookupTests.test_regex_non_string",
73+
# Querying ObjectID with string doesn't work.
74+
"lookup.tests.LookupTests.test_lookup_int_as_str",
7175
# The $sum aggregation returns 0 instead of None for null.
7276
"aggregation.test_filter_argument.FilteredAggregateTests.test_plain_annotate",
7377
"aggregation.tests.AggregateTestCase.test_aggregation_default_passed_another_aggregate",

django_mongodb_backend/query_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,4 @@ def process_rhs(node, compiler, connection):
5050
def regex_match(field, regex_vals, insensitive=False):
5151
regex = {"$concat": regex_vals} if isinstance(regex_vals, tuple) else regex_vals
5252
options = "i" if insensitive else ""
53-
return {"$regexMatch": {"input": {"$toString": field}, "regex": regex, "options": options}}
53+
return {"$regexMatch": {"input": field, "regex": regex, "options": options}}

docs/source/releases/5.2.x.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22
Django MongoDB Backend 5.2.x
33
============================
44

5+
5.2.0 beta 3
6+
============
7+
8+
*Unreleased*
9+
10+
Backwards incompatible changes
11+
------------------------------
12+
13+
- Pattern matching lookups (``iexact``, ``startswith``, ``istartswith``,
14+
``endswith``, ``iendswith``, ``contains``, ``icontains``, ``regex``,
15+
and ``iregex``) no longer support non-string fields. These lookups previously
16+
cast their input using ``$toString`` but this caused some queries to perform
17+
poorly because MongoDB couldn't use indexes when running the query.
18+
519
5.2.0 beta 2
620
============
721

docs/source/topics/known-issues.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ Querying
5050
- You can study the skipped tests in ``DatabaseFeatures.django_test_skips``
5151
for more details on known issues.
5252

53+
- Pattern matching lookups (:lookup:`iexact`, :lookup:`startswith`,
54+
:lookup:`istartswith`, :lookup:`endswith`, :lookup:`iendswith`,
55+
:lookup:`contains`, :lookup:`icontains`, :lookup:`regex`,
56+
and :lookup:`iregex`) don't support non-string fields.
57+
5358
Database functions
5459
==================
5560

tests/lookup_/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
from django.db import models
22

33

4+
class Book(models.Model):
5+
title = models.CharField(max_length=10)
6+
7+
def __str__(self):
8+
return self.title
9+
10+
411
class Number(models.Model):
512
num = models.IntegerField(blank=True, null=True)
613

tests/lookup_/tests.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from django.test import TestCase
22

3-
from .models import Number
3+
from .models import Book, Number
44

55

66
class NumericLookupTests(TestCase):
@@ -15,3 +15,18 @@ def test_lt(self):
1515

1616
def test_lte(self):
1717
self.assertQuerySetEqual(Number.objects.filter(num__lte=3), self.objs[:4])
18+
19+
20+
class RegexTests(TestCase):
21+
def test_mql(self):
22+
# $regexMatch must not cast the input to string, otherwise MongoDB
23+
# can't use the field's indexes.
24+
with self.assertNumQueries(1) as ctx:
25+
list(Book.objects.filter(title__regex="Moby Dick"))
26+
query = ctx.captured_queries[0]["sql"]
27+
self.assertEqual(
28+
query,
29+
"db.lookup__book.aggregate(["
30+
"{'$match': {'$expr': {'$regexMatch': {'input': '$title', "
31+
"'regex': 'Moby Dick', 'options': ''}}}}])",
32+
)

0 commit comments

Comments
 (0)