@@ -897,3 +897,122 @@ def test_self_join_tag_three_levels_pushable(self):
897897 {"$match" : {"$and" : [{"name" : "T1" }, {"T2.name" : "T2" }, {"T3.name" : "T3" }]}},
898898 ],
899899 )
900+
901+ def test_partial_and_pushdown (self ):
902+ a1 = Author .objects .create (name = "Alice" )
903+ a2 = Author .objects .create (name = "Bob" )
904+ b1 = Book .objects .create (title = "B1" , author = a1 , isbn = "111" )
905+ Book .objects .create (title = "B2" , author = a2 , isbn = "222" )
906+ cond = models .Q (author__name = "Alice" ) & models .Q (title__contains = "B" )
907+ expected = [b1 ]
908+ with self .assertNumQueries (1 ) as ctx :
909+ self .assertSequenceEqual (Book .objects .filter (cond ), expected )
910+ self .assertAggregateQuery (
911+ ctx .captured_queries [0 ]["sql" ],
912+ "queries__book" ,
913+ [
914+ {
915+ "$lookup" : {
916+ "from" : "queries__author" ,
917+ "let" : {"parent__field__0" : "$author_id" },
918+ "pipeline" : [
919+ {
920+ "$match" : {
921+ "$and" : [
922+ {
923+ "$expr" : {
924+ "$and" : [{"$eq" : ["$$parent__field__0" , "$_id" ]}]
925+ }
926+ },
927+ {"name" : "Alice" },
928+ ]
929+ }
930+ }
931+ ],
932+ "as" : "queries__author" ,
933+ }
934+ },
935+ {"$unwind" : "$queries__author" },
936+ {
937+ "$match" : {
938+ "$and" : [
939+ {"queries__author.name" : "Alice" },
940+ {"title" : {"$regex" : "B" , "$options" : "" }},
941+ ]
942+ }
943+ },
944+ ],
945+ )
946+
947+ def test_not_or_demorgan_pushdown (self ):
948+ a1 = Author .objects .create (name = "Alice" )
949+ a2 = Author .objects .create (name = "Bob" )
950+ b1 = Book .objects .create (title = "B1" , author = a1 , isbn = "111" )
951+ Book .objects .create (title = "B2" , author = a2 , isbn = "222" )
952+ expected = [b1 ]
953+ with self .assertNumQueries (1 ) as ctx :
954+ self .assertSequenceEqual (
955+ Book .objects .filter (~ (models .Q (author__name = "Bob" ) | models .Q (isbn = "222" ))),
956+ expected ,
957+ )
958+ self .assertAggregateQuery (
959+ ctx .captured_queries [0 ]["sql" ],
960+ "queries__book" ,
961+ [
962+ {
963+ "$lookup" : {
964+ "from" : "queries__author" ,
965+ "let" : {"parent__field__0" : "$author_id" },
966+ "pipeline" : [
967+ {
968+ "$match" : {
969+ "$and" : [
970+ {
971+ "$expr" : {
972+ "$and" : [{"$eq" : ["$$parent__field__0" , "$_id" ]}]
973+ }
974+ },
975+ {"$nor" : [{"name" : "Bob" }]},
976+ ]
977+ }
978+ }
979+ ],
980+ "as" : "queries__author" ,
981+ }
982+ },
983+ {"$unwind" : "$queries__author" },
984+ {"$match" : {"$nor" : [{"$or" : [{"queries__author.name" : "Bob" }, {"isbn" : "222" }]}]}},
985+ ],
986+ )
987+
988+ def test_or_mixed_local_remote_pushdown (self ):
989+ a1 = Author .objects .create (name = "Alice" )
990+ a2 = Author .objects .create (name = "Bob" )
991+ b1 = Book .objects .create (title = "B1" , author = a1 , isbn = "111" )
992+ b2 = Book .objects .create (title = "B2" , author = a2 , isbn = "222" )
993+ cond = models .Q (title = "B1" ) | models .Q (author__name = "Bob" )
994+ expected = [b1 , b2 ]
995+ with self .assertNumQueries (1 ) as ctx :
996+ self .assertSequenceEqual (Book .objects .filter (cond ), expected )
997+ self .assertAggregateQuery (
998+ ctx .captured_queries [0 ]["sql" ],
999+ "queries__book" ,
1000+ [
1001+ {
1002+ "$lookup" : {
1003+ "from" : "queries__author" ,
1004+ "let" : {"parent__field__0" : "$author_id" },
1005+ "pipeline" : [
1006+ {
1007+ "$match" : {
1008+ "$expr" : {"$and" : [{"$eq" : ["$$parent__field__0" , "$_id" ]}]}
1009+ }
1010+ }
1011+ ],
1012+ "as" : "queries__author" ,
1013+ }
1014+ },
1015+ {"$unwind" : "$queries__author" },
1016+ {"$match" : {"$or" : [{"title" : "B1" }, {"queries__author.name" : "Bob" }]}},
1017+ ],
1018+ )
0 commit comments