2
2
from django .core import exceptions
3
3
from django .db .models import JSONField
4
4
from django .db .models .fields import AutoField , Field
5
- from django .db .models .fields .json import DataContains , JSONExact , KeyTransform
5
+ from django .db .models .fields .json import (
6
+ ContainedBy ,
7
+ DataContains ,
8
+ HasAnyKeys ,
9
+ HasKey ,
10
+ HasKeyLookup ,
11
+ HasKeys ,
12
+ JSONExact ,
13
+ KeyTransform ,
14
+ )
6
15
from django .utils .translation import gettext_lazy as _
7
16
8
17
from .base import DatabaseWrapper
9
- from .query_utils import process_lhs , process_rhs
18
+ from .query_utils import process_lhs
10
19
11
20
12
21
class MongoAutoField (AutoField ):
@@ -50,19 +59,35 @@ def from_db_value(self, value, expression, connection):
50
59
)
51
60
52
61
62
+ def process_rhs (node , compiler , connection ):
63
+ _ , value = node .process_rhs (compiler , connection )
64
+ lookup_name = node .lookup_name
65
+ if lookup_name not in ("in" , "range" ):
66
+ value = value [0 ] if len (value ) > 0 else []
67
+
68
+ return value
69
+
70
+
53
71
def key_transform (self , compiler , connection ):
54
- _ , _ , key_transforms = self .preprocess_lhs (compiler , connection )
55
- lhs_mql = process_lhs (self , compiler , connection )
72
+ key_transforms = [self .key_name ]
73
+ previous = self .lhs
74
+ while isinstance (previous , KeyTransform ):
75
+ key_transforms .insert (0 , previous .key_name )
76
+ previous = previous .lhs
77
+ lhs_mql = previous .as_mql (compiler , connection )
56
78
return "." .join ([lhs_mql , * key_transforms ])
57
79
58
80
59
81
def data_contains (self , compiler , connection ):
60
82
lhs_mql = process_lhs (self , compiler , connection )
61
83
value = process_rhs (self , compiler , connection )
62
- # rhs_mql = connection.operators[self.lookup_name](value)
63
84
if not value :
64
- return {lhs_mql : {"$ne" : None }}
65
- return {lhs_mql : {"$all" : value }}
85
+ return {lhs_mql : {"$type" : "array" }}
86
+ if isinstance (value , list ):
87
+ return {"$and" : [{lhs_mql : v } for v in value ]}
88
+ if isinstance (value , dict ):
89
+ return {"$and" : [{f"{ lhs_mql } .{ k } " : v } for k , v in value .items ()]}
90
+ return {lhs_mql : value }
66
91
67
92
68
93
def json_exact (self , compiler , connection ):
@@ -71,8 +96,58 @@ def json_exact(self, compiler, connection):
71
96
return {lhs_mql : {"$eq" : rhs_mql }}
72
97
73
98
99
+ def contained_by (self , compiler , connection ):
100
+ lhs_mql = process_lhs (self , compiler , connection )
101
+ value = process_rhs (self , compiler , connection )
102
+ if isinstance (value , list ):
103
+ if not value :
104
+ return {lhs_mql : []}
105
+ return {"$and" : [{"$or" : [{lhs_mql : v }, {lhs_mql : {"$exists" : False }}]} for v in value ]}
106
+ if isinstance (value , dict ):
107
+ if not value :
108
+ return {lhs_mql : {}}
109
+ return {
110
+ "$and" : [
111
+ {"$or" : [{f"{ lhs_mql } .{ k } " : v }, {f"{ lhs_mql } .{ k } " : {"$exists" : False }}]}
112
+ for k , v in value .items ()
113
+ ]
114
+ }
115
+ return {lhs_mql : value }
116
+
117
+
118
+ def has_key_lookup (self , compiler , connection ):
119
+ lhs = process_lhs (self , compiler , connection )
120
+ rhs = self .rhs
121
+ if not isinstance (rhs , (list | tuple )):
122
+ rhs = [rhs ]
123
+ paths = []
124
+ for key in rhs :
125
+ if isinstance (key , KeyTransform ):
126
+ * _ , rhs_key_transforms = key .preprocess_lhs (compiler , connection )
127
+ rhs_key_transforms = "." .join (
128
+ rhs_key_transforms
129
+ ) # process_lhs(key, compiler, connection)
130
+ else :
131
+ rhs_key_transforms = str (key )
132
+ rhs_json_path = f"{ lhs } .{ rhs_key_transforms } "
133
+ paths .append (rhs_json_path )
134
+
135
+ keys = []
136
+ for path in paths :
137
+ keys .append ({path : {"$exists" : True }})
138
+ if self .mongo_operator is None :
139
+ assert len (keys ) == 1
140
+ return keys [0 ]
141
+ return {self .mongo_operator : keys }
142
+
143
+
74
144
def load_fields ():
75
145
JSONField .from_db_value = from_db_value
76
146
DataContains .as_mql = data_contains
77
147
KeyTransform .as_mql = key_transform
78
148
JSONExact .as_mql = json_exact
149
+ ContainedBy .as_mql = contained_by
150
+ HasKeyLookup .as_mql = has_key_lookup
151
+ HasAnyKeys .mongo_operator = "$or"
152
+ HasKey .mongo_operator = None
153
+ HasKeys .mongo_operator = "$and"
0 commit comments