|
1 | 1 | from bson import SON, ObjectId |
2 | 2 | from django.db import models |
| 3 | +from django.db.models import Case, F, IntegerField, Value, When |
3 | 4 | from django.test import TestCase |
4 | 5 |
|
5 | 6 | from django_mongodb_backend.test import MongoTestCaseMixin |
@@ -1198,3 +1199,166 @@ def test_or_mixed_local_remote_pushdown(self): |
1198 | 1199 | {"$match": {"$or": [{"title": "B1"}, {"queries__author.name": "Bob"}]}}, |
1199 | 1200 | ], |
1200 | 1201 | ) |
| 1202 | + |
| 1203 | + def test_conditional_expression_not_pushed(self): |
| 1204 | + a1 = Author.objects.create(name="Vicente") |
| 1205 | + a2 = Author.objects.create(name="Carlos") |
| 1206 | + a3 = Author.objects.create(name="Maria") |
| 1207 | + |
| 1208 | + Book.objects.create(title="B1", author=a1, isbn="111") |
| 1209 | + b2 = Book.objects.create(title="B2", author=a2, isbn="222") |
| 1210 | + Book.objects.create(title="B3", author=a3, isbn="333") |
| 1211 | + |
| 1212 | + annotated = Book.objects.annotate( |
| 1213 | + score=Case( |
| 1214 | + When(author__name="Vicente", then=Value(2)), |
| 1215 | + When(author__name="Carlos", then=Value(4)), |
| 1216 | + default=Value(1), |
| 1217 | + output_field=IntegerField(), |
| 1218 | + ) |
| 1219 | + + Value(1) |
| 1220 | + ).filter(score__gt=3) |
| 1221 | + |
| 1222 | + with self.assertNumQueries(1) as ctx: |
| 1223 | + self.assertSequenceEqual(annotated, [b2]) |
| 1224 | + self.assertAggregateQuery( |
| 1225 | + ctx.captured_queries[0]["sql"], |
| 1226 | + "queries__book", |
| 1227 | + [ |
| 1228 | + { |
| 1229 | + "$lookup": { |
| 1230 | + "from": "queries__author", |
| 1231 | + "let": {"parent__field__0": "$author_id"}, |
| 1232 | + "pipeline": [ |
| 1233 | + { |
| 1234 | + "$match": { |
| 1235 | + "$expr": {"$and": [{"$eq": ["$$parent__field__0", "$_id"]}]} |
| 1236 | + } |
| 1237 | + } |
| 1238 | + ], |
| 1239 | + "as": "queries__author", |
| 1240 | + } |
| 1241 | + }, |
| 1242 | + {"$unwind": "$queries__author"}, |
| 1243 | + { |
| 1244 | + "$match": { |
| 1245 | + "$expr": { |
| 1246 | + "$gt": [ |
| 1247 | + { |
| 1248 | + "$add": [ |
| 1249 | + { |
| 1250 | + "$switch": { |
| 1251 | + "branches": [ |
| 1252 | + { |
| 1253 | + "case": { |
| 1254 | + "$eq": [ |
| 1255 | + "$queries__author.name", |
| 1256 | + "Vicente", |
| 1257 | + ] |
| 1258 | + }, |
| 1259 | + "then": {"$literal": 2}, |
| 1260 | + }, |
| 1261 | + { |
| 1262 | + "case": { |
| 1263 | + "$eq": [ |
| 1264 | + "$queries__author.name", |
| 1265 | + "Carlos", |
| 1266 | + ] |
| 1267 | + }, |
| 1268 | + "then": {"$literal": 4}, |
| 1269 | + }, |
| 1270 | + ], |
| 1271 | + "default": {"$literal": 1}, |
| 1272 | + } |
| 1273 | + }, |
| 1274 | + {"$literal": 1}, |
| 1275 | + ] |
| 1276 | + }, |
| 1277 | + 3, |
| 1278 | + ] |
| 1279 | + } |
| 1280 | + } |
| 1281 | + }, |
| 1282 | + { |
| 1283 | + "$project": { |
| 1284 | + "score": { |
| 1285 | + "$add": [ |
| 1286 | + { |
| 1287 | + "$switch": { |
| 1288 | + "branches": [ |
| 1289 | + { |
| 1290 | + "case": { |
| 1291 | + "$eq": ["$queries__author.name", "Vicente"] |
| 1292 | + }, |
| 1293 | + "then": {"$literal": 2}, |
| 1294 | + }, |
| 1295 | + { |
| 1296 | + "case": { |
| 1297 | + "$eq": ["$queries__author.name", "Carlos"] |
| 1298 | + }, |
| 1299 | + "then": {"$literal": 4}, |
| 1300 | + }, |
| 1301 | + ], |
| 1302 | + "default": {"$literal": 1}, |
| 1303 | + } |
| 1304 | + }, |
| 1305 | + {"$literal": 1}, |
| 1306 | + ] |
| 1307 | + }, |
| 1308 | + "_id": 1, |
| 1309 | + "title": 1, |
| 1310 | + "author_id": 1, |
| 1311 | + "isbn": 1, |
| 1312 | + } |
| 1313 | + }, |
| 1314 | + ], |
| 1315 | + ) |
| 1316 | + |
| 1317 | + def test_simple_annotation_pushdown(self): |
| 1318 | + a1 = Author.objects.create(name="Alice") |
| 1319 | + a2 = Author.objects.create(name="Bob") |
| 1320 | + b1 = Book.objects.create(title="B1", author=a1, isbn="111") |
| 1321 | + Book.objects.create(title="B2", author=a2, isbn="222") |
| 1322 | + b3 = Book.objects.create(title="B3", author=a1, isbn="333") |
| 1323 | + qs = Book.objects.annotate(name_length=F("author__name")).filter(name_length="Alice") |
| 1324 | + expected = [b1, b3] |
| 1325 | + with self.assertNumQueries(1) as ctx: |
| 1326 | + self.assertSequenceEqual(qs, expected) |
| 1327 | + self.assertAggregateQuery( |
| 1328 | + ctx.captured_queries[0]["sql"], |
| 1329 | + "queries__book", |
| 1330 | + [ |
| 1331 | + { |
| 1332 | + "$lookup": { |
| 1333 | + "from": "queries__author", |
| 1334 | + "let": {"parent__field__0": "$author_id"}, |
| 1335 | + "pipeline": [ |
| 1336 | + { |
| 1337 | + "$match": { |
| 1338 | + "$and": [ |
| 1339 | + { |
| 1340 | + "$expr": { |
| 1341 | + "$and": [{"$eq": ["$$parent__field__0", "$_id"]}] |
| 1342 | + } |
| 1343 | + }, |
| 1344 | + {"name": "Alice"}, |
| 1345 | + ] |
| 1346 | + } |
| 1347 | + } |
| 1348 | + ], |
| 1349 | + "as": "queries__author", |
| 1350 | + } |
| 1351 | + }, |
| 1352 | + {"$unwind": "$queries__author"}, |
| 1353 | + {"$match": {"queries__author.name": "Alice"}}, |
| 1354 | + { |
| 1355 | + "$project": { |
| 1356 | + "queries__author": {"name_length": "$queries__author.name"}, |
| 1357 | + "_id": 1, |
| 1358 | + "title": 1, |
| 1359 | + "author_id": 1, |
| 1360 | + "isbn": 1, |
| 1361 | + } |
| 1362 | + }, |
| 1363 | + ], |
| 1364 | + ) |
0 commit comments