@@ -1079,3 +1079,122 @@ def test_self_join_tag_three_levels_pushable(self):
10791079 {"$match" : {"$and" : [{"name" : "T1" }, {"T2.name" : "T2" }, {"T3.name" : "T3" }]}},
10801080 ],
10811081 )
1082+
1083+ def test_partial_and_pushdown (self ):
1084+ a1 = Author .objects .create (name = "Alice" )
1085+ a2 = Author .objects .create (name = "Bob" )
1086+ b1 = Book .objects .create (title = "B1" , author = a1 , isbn = "111" )
1087+ Book .objects .create (title = "B2" , author = a2 , isbn = "222" )
1088+ cond = models .Q (author__name = "Alice" ) & models .Q (title__contains = "B" )
1089+ expected = [b1 ]
1090+ with self .assertNumQueries (1 ) as ctx :
1091+ self .assertSequenceEqual (Book .objects .filter (cond ), expected )
1092+ self .assertAggregateQuery (
1093+ ctx .captured_queries [0 ]["sql" ],
1094+ "queries__book" ,
1095+ [
1096+ {
1097+ "$lookup" : {
1098+ "from" : "queries__author" ,
1099+ "let" : {"parent__field__0" : "$author_id" },
1100+ "pipeline" : [
1101+ {
1102+ "$match" : {
1103+ "$and" : [
1104+ {
1105+ "$expr" : {
1106+ "$and" : [{"$eq" : ["$$parent__field__0" , "$_id" ]}]
1107+ }
1108+ },
1109+ {"name" : "Alice" },
1110+ ]
1111+ }
1112+ }
1113+ ],
1114+ "as" : "queries__author" ,
1115+ }
1116+ },
1117+ {"$unwind" : "$queries__author" },
1118+ {
1119+ "$match" : {
1120+ "$and" : [
1121+ {"queries__author.name" : "Alice" },
1122+ {"title" : {"$regex" : "B" , "$options" : "" }},
1123+ ]
1124+ }
1125+ },
1126+ ],
1127+ )
1128+
1129+ def test_not_or_demorgan_pushdown (self ):
1130+ a1 = Author .objects .create (name = "Alice" )
1131+ a2 = Author .objects .create (name = "Bob" )
1132+ b1 = Book .objects .create (title = "B1" , author = a1 , isbn = "111" )
1133+ Book .objects .create (title = "B2" , author = a2 , isbn = "222" )
1134+ expected = [b1 ]
1135+ with self .assertNumQueries (1 ) as ctx :
1136+ self .assertSequenceEqual (
1137+ Book .objects .filter (~ (models .Q (author__name = "Bob" ) | models .Q (isbn = "222" ))),
1138+ expected ,
1139+ )
1140+ self .assertAggregateQuery (
1141+ ctx .captured_queries [0 ]["sql" ],
1142+ "queries__book" ,
1143+ [
1144+ {
1145+ "$lookup" : {
1146+ "from" : "queries__author" ,
1147+ "let" : {"parent__field__0" : "$author_id" },
1148+ "pipeline" : [
1149+ {
1150+ "$match" : {
1151+ "$and" : [
1152+ {
1153+ "$expr" : {
1154+ "$and" : [{"$eq" : ["$$parent__field__0" , "$_id" ]}]
1155+ }
1156+ },
1157+ {"$nor" : [{"name" : "Bob" }]},
1158+ ]
1159+ }
1160+ }
1161+ ],
1162+ "as" : "queries__author" ,
1163+ }
1164+ },
1165+ {"$unwind" : "$queries__author" },
1166+ {"$match" : {"$nor" : [{"$or" : [{"queries__author.name" : "Bob" }, {"isbn" : "222" }]}]}},
1167+ ],
1168+ )
1169+
1170+ def test_or_mixed_local_remote_pushdown (self ):
1171+ a1 = Author .objects .create (name = "Alice" )
1172+ a2 = Author .objects .create (name = "Bob" )
1173+ b1 = Book .objects .create (title = "B1" , author = a1 , isbn = "111" )
1174+ b2 = Book .objects .create (title = "B2" , author = a2 , isbn = "222" )
1175+ cond = models .Q (title = "B1" ) | models .Q (author__name = "Bob" )
1176+ expected = [b1 , b2 ]
1177+ with self .assertNumQueries (1 ) as ctx :
1178+ self .assertSequenceEqual (Book .objects .filter (cond ), expected )
1179+ self .assertAggregateQuery (
1180+ ctx .captured_queries [0 ]["sql" ],
1181+ "queries__book" ,
1182+ [
1183+ {
1184+ "$lookup" : {
1185+ "from" : "queries__author" ,
1186+ "let" : {"parent__field__0" : "$author_id" },
1187+ "pipeline" : [
1188+ {
1189+ "$match" : {
1190+ "$expr" : {"$and" : [{"$eq" : ["$$parent__field__0" , "$_id" ]}]}
1191+ }
1192+ }
1193+ ],
1194+ "as" : "queries__author" ,
1195+ }
1196+ },
1197+ {"$unwind" : "$queries__author" },
1198+ {"$match" : {"$or" : [{"title" : "B1" }, {"queries__author.name" : "Bob" }]}},
1199+ ],
1200+ )
0 commit comments